Les enseignants avaient besoin de consignes plus claires pour les élèves : le champ description en texte brut ne permettait ni mise en forme ni partage de documents. Cette limitation obligeait à décrire verbalement les ressources au lieu de les joindre directement. L'éditeur WYSIWYG (TipTap) remplace le textarea avec gras, italique, listes et liens. Le contenu HTML est sanitisé côté backend via symfony/html-sanitizer pour prévenir les injections XSS. Les pièces jointes (PDF, JPEG, PNG, max 10 Mo) sont uploadées via une API dédiée avec validation MIME côté domaine et protection path-traversal sur le téléchargement. Les descriptions en texte brut existantes restent lisibles sans migration de données.
122 lines
3.9 KiB
PHP
122 lines
3.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Scolarite\Infrastructure\Persistence\Doctrine;
|
|
|
|
use App\Scolarite\Domain\Model\Homework\HomeworkAttachment;
|
|
use App\Scolarite\Domain\Model\Homework\HomeworkAttachmentId;
|
|
use App\Scolarite\Domain\Model\Homework\HomeworkId;
|
|
use App\Scolarite\Domain\Repository\HomeworkAttachmentRepository;
|
|
|
|
use function array_fill_keys;
|
|
use function array_map;
|
|
|
|
use DateTimeImmutable;
|
|
use Doctrine\DBAL\ArrayParameterType;
|
|
use Doctrine\DBAL\Connection;
|
|
use Override;
|
|
|
|
final readonly class DoctrineHomeworkAttachmentRepository implements HomeworkAttachmentRepository
|
|
{
|
|
public function __construct(
|
|
private Connection $connection,
|
|
) {
|
|
}
|
|
|
|
#[Override]
|
|
public function findByHomeworkId(HomeworkId $homeworkId): array
|
|
{
|
|
$rows = $this->connection->fetchAllAssociative(
|
|
'SELECT * FROM homework_attachments WHERE homework_id = :homework_id',
|
|
['homework_id' => (string) $homeworkId],
|
|
);
|
|
|
|
return array_map($this->hydrate(...), $rows);
|
|
}
|
|
|
|
#[Override]
|
|
public function hasAttachments(HomeworkId ...$homeworkIds): array
|
|
{
|
|
if ($homeworkIds === []) {
|
|
return [];
|
|
}
|
|
|
|
$ids = array_map(static fn (HomeworkId $id): string => (string) $id, $homeworkIds);
|
|
|
|
/** @var array<array{homework_id: string}> $rows */
|
|
$rows = $this->connection->fetchAllAssociative(
|
|
'SELECT DISTINCT homework_id FROM homework_attachments WHERE homework_id IN (:ids)',
|
|
['ids' => $ids],
|
|
['ids' => ArrayParameterType::STRING],
|
|
);
|
|
|
|
$result = array_fill_keys($ids, false);
|
|
|
|
foreach ($rows as $row) {
|
|
/** @var string $hwId */
|
|
$hwId = $row['homework_id'];
|
|
$result[$hwId] = true;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
#[Override]
|
|
public function save(HomeworkId $homeworkId, HomeworkAttachment $attachment): void
|
|
{
|
|
$this->connection->executeStatement(
|
|
'INSERT INTO homework_attachments (id, homework_id, filename, file_path, file_size, mime_type, uploaded_at)
|
|
VALUES (:id, :homework_id, :filename, :file_path, :file_size, :mime_type, :uploaded_at)',
|
|
[
|
|
'id' => (string) $attachment->id,
|
|
'homework_id' => (string) $homeworkId,
|
|
'filename' => $attachment->filename,
|
|
'file_path' => $attachment->filePath,
|
|
'file_size' => $attachment->fileSize,
|
|
'mime_type' => $attachment->mimeType,
|
|
'uploaded_at' => $attachment->uploadedAt->format(DateTimeImmutable::ATOM),
|
|
],
|
|
);
|
|
}
|
|
|
|
#[Override]
|
|
public function delete(HomeworkId $homeworkId, HomeworkAttachment $attachment): void
|
|
{
|
|
$this->connection->executeStatement(
|
|
'DELETE FROM homework_attachments WHERE id = :id AND homework_id = :homework_id',
|
|
[
|
|
'id' => (string) $attachment->id,
|
|
'homework_id' => (string) $homeworkId,
|
|
],
|
|
);
|
|
}
|
|
|
|
/** @param array<string, mixed> $row */
|
|
private function hydrate(array $row): HomeworkAttachment
|
|
{
|
|
/** @var string $id */
|
|
$id = $row['id'];
|
|
/** @var string $filename */
|
|
$filename = $row['filename'];
|
|
/** @var string $filePath */
|
|
$filePath = $row['file_path'];
|
|
/** @var string|int $rawFileSize */
|
|
$rawFileSize = $row['file_size'];
|
|
$fileSize = (int) $rawFileSize;
|
|
/** @var string $mimeType */
|
|
$mimeType = $row['mime_type'];
|
|
/** @var string $uploadedAt */
|
|
$uploadedAt = $row['uploaded_at'];
|
|
|
|
return new HomeworkAttachment(
|
|
id: HomeworkAttachmentId::fromString($id),
|
|
filename: $filename,
|
|
filePath: $filePath,
|
|
fileSize: $fileSize,
|
|
mimeType: $mimeType,
|
|
uploadedAt: new DateTimeImmutable($uploadedAt),
|
|
);
|
|
}
|
|
}
|