feat(repo): implémentation MySQL de CardRepository
This commit is contained in:
1 parent
2721326a07
commit
ab24d329b9
1 file changed
+123
@@ -0,0 +1,123 @@
|
|||||||
|
import { createPool, type Pool, type ResultSetHeader, type RowDataPacket } from 'mysql2/promise'
|
||||||
|
import type { Card, CardInput } from '../domain/card'
|
||||||
|
import type { CardRepository } from '../domain/card-repository'
|
||||||
|
|
||||||
|
interface CardRow extends RowDataPacket {
|
||||||
|
id: string
|
||||||
|
question: string
|
||||||
|
response: string
|
||||||
|
theme: string
|
||||||
|
tags: string | null
|
||||||
|
createdAt: Date
|
||||||
|
updatedAt: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
const FIELDS = ['id', 'question', 'response', 'theme', 'tags', 'createdAt', 'updatedAt'] as const
|
||||||
|
|
||||||
|
export class MysqlCardRepository implements CardRepository {
|
||||||
|
private readonly pool: Pool
|
||||||
|
|
||||||
|
constructor(pool?: Pool) {
|
||||||
|
this.pool =
|
||||||
|
pool ??
|
||||||
|
createPool({
|
||||||
|
host: process.env.MYSQL_HOST ?? 'localhost',
|
||||||
|
port: Number(process.env.MYSQL_PORT ?? 13306),
|
||||||
|
user: process.env.MYSQL_USER ?? 'flashcards',
|
||||||
|
password: process.env.MYSQL_PASSWORD ?? 'flashcards',
|
||||||
|
database: process.env.MYSQL_DATABASE ?? 'flashcards',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapRow(row: CardRow): Card {
|
||||||
|
return {
|
||||||
|
id: row.id,
|
||||||
|
question: row.question,
|
||||||
|
response: row.response,
|
||||||
|
theme: row.theme,
|
||||||
|
tags: row.tags ? JSON.parse(row.tags) : [],
|
||||||
|
createdAt: new Date(row.createdAt),
|
||||||
|
updatedAt: new Date(row.updatedAt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAll(): Promise<Card[]> {
|
||||||
|
const [rows] = await this.pool.query<CardRow[]>(`SELECT ${FIELDS.join(', ')} FROM cards`)
|
||||||
|
return rows.map((row) => this.mapRow(row))
|
||||||
|
}
|
||||||
|
|
||||||
|
async findById(id: string): Promise<Card | null> {
|
||||||
|
const [rows] = await this.pool.query<CardRow[]>(`SELECT ${FIELDS.join(', ')} FROM cards WHERE id = ?`, [id])
|
||||||
|
return rows[0] ? this.mapRow(rows[0]) : null
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(input: CardInput): Promise<Card> {
|
||||||
|
const id = crypto.randomUUID()
|
||||||
|
const now = new Date()
|
||||||
|
await this.pool.query(
|
||||||
|
`INSERT INTO cards (${FIELDS.join(', ')}) VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
[id, input.question, input.response, input.theme, JSON.stringify(input.tags ?? []), now, now],
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
question: input.question,
|
||||||
|
response: input.response,
|
||||||
|
theme: input.theme,
|
||||||
|
tags: input.tags ?? [],
|
||||||
|
createdAt: now,
|
||||||
|
updatedAt: now,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id: string, input: Partial<CardInput>): Promise<Card | null> {
|
||||||
|
const sets: string[] = []
|
||||||
|
const values: unknown[] = []
|
||||||
|
if (input.question !== undefined) {
|
||||||
|
sets.push('question = ?')
|
||||||
|
values.push(input.question)
|
||||||
|
}
|
||||||
|
if (input.response !== undefined) {
|
||||||
|
sets.push('response = ?')
|
||||||
|
values.push(input.response)
|
||||||
|
}
|
||||||
|
if (input.theme !== undefined) {
|
||||||
|
sets.push('theme = ?')
|
||||||
|
values.push(input.theme)
|
||||||
|
}
|
||||||
|
if (input.tags !== undefined) {
|
||||||
|
sets.push('tags = ?')
|
||||||
|
values.push(JSON.stringify(input.tags))
|
||||||
|
}
|
||||||
|
if (sets.length === 0) {
|
||||||
|
return this.findById(id)
|
||||||
|
}
|
||||||
|
sets.push('updatedAt = ?')
|
||||||
|
values.push(new Date())
|
||||||
|
values.push(id)
|
||||||
|
await this.pool.query(`UPDATE cards SET ${sets.join(', ')} WHERE id = ?`, values)
|
||||||
|
return this.findById(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(id: string): Promise<boolean> {
|
||||||
|
const [result] = await this.pool.query<ResultSetHeader>('DELETE FROM cards WHERE id = ?', [id])
|
||||||
|
return result.affectedRows > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
async findRandom(): Promise<Card | null> {
|
||||||
|
const [rows] = await this.pool.query<CardRow[]>(`SELECT ${FIELDS.join(', ')} FROM cards ORDER BY RAND() LIMIT 1`)
|
||||||
|
return rows[0] ? this.mapRow(rows[0]) : null
|
||||||
|
}
|
||||||
|
|
||||||
|
async findRandomByTheme(theme: string): Promise<Card | null> {
|
||||||
|
const [rows] = await this.pool.query<CardRow[]>(
|
||||||
|
`SELECT ${FIELDS.join(', ')} FROM cards WHERE theme = ? ORDER BY RAND() LIMIT 1`,
|
||||||
|
[theme],
|
||||||
|
)
|
||||||
|
return rows[0] ? this.mapRow(rows[0]) : null
|
||||||
|
}
|
||||||
|
|
||||||
|
async findThemes(): Promise<string[]> {
|
||||||
|
const [rows] = await this.pool.query<RowDataPacket[]>('SELECT DISTINCT theme FROM cards ORDER BY theme')
|
||||||
|
return rows.map((row) => row.theme as string)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user