feat: Permettre aux enseignants de dupliquer un devoir vers plusieurs classes
Un enseignant qui donne le même travail à plusieurs classes devait jusqu'ici recréer manuellement chaque devoir. La duplication permet de sélectionner les classes cibles, d'ajuster les dates d'échéance par classe, et de créer tous les devoirs en une seule opération atomique (transaction). La validation s'effectue par classe (affectation enseignant, date d'échéance) avec un rapport d'erreurs détaillé. L'infrastructure de warnings est prête pour les règles de timing de la Story 5.3. Le filtrage par classe dans la liste des devoirs passe côté serveur pour rester compatible avec la pagination.
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Application\Command\DuplicateHomework;
|
||||
|
||||
final readonly class DuplicateHomeworkCommand
|
||||
{
|
||||
/**
|
||||
* @param array<string> $targetClassIds
|
||||
* @param array<string, string> $dueDates Clé = classId, valeur = date (Y-m-d)
|
||||
*/
|
||||
public function __construct(
|
||||
public string $tenantId,
|
||||
public string $homeworkId,
|
||||
public string $teacherId,
|
||||
public array $targetClassIds,
|
||||
public array $dueDates = [],
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Application\Command\DuplicateHomework;
|
||||
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassId;
|
||||
use App\Administration\Domain\Model\User\UserId;
|
||||
use App\Scolarite\Application\Port\CurrentCalendarProvider;
|
||||
use App\Scolarite\Application\Port\EnseignantAffectationChecker;
|
||||
use App\Scolarite\Domain\Exception\DateEcheanceInvalideException;
|
||||
use App\Scolarite\Domain\Exception\DuplicationValidationException;
|
||||
use App\Scolarite\Domain\Exception\NonProprietaireDuDevoirException;
|
||||
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 App\Scolarite\Domain\Repository\HomeworkRepository;
|
||||
use App\Scolarite\Domain\Service\DueDateValidator;
|
||||
use App\Scolarite\Domain\Service\HomeworkDuplicator;
|
||||
use App\Scolarite\Domain\ValueObject\ClassValidationResult;
|
||||
use App\Shared\Domain\Clock;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
|
||||
use function array_filter;
|
||||
use function array_unique;
|
||||
use function array_values;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Exception;
|
||||
|
||||
use function in_array;
|
||||
|
||||
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
||||
use Throwable;
|
||||
|
||||
#[AsMessageHandler(bus: 'command.bus')]
|
||||
final readonly class DuplicateHomeworkHandler
|
||||
{
|
||||
public function __construct(
|
||||
private HomeworkRepository $homeworkRepository,
|
||||
private HomeworkAttachmentRepository $attachmentRepository,
|
||||
private EnseignantAffectationChecker $affectationChecker,
|
||||
private CurrentCalendarProvider $calendarProvider,
|
||||
private DueDateValidator $dueDateValidator,
|
||||
private HomeworkDuplicator $duplicator,
|
||||
private Clock $clock,
|
||||
private Connection $connection,
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(DuplicateHomeworkCommand $command): DuplicationResult
|
||||
{
|
||||
$tenantId = TenantId::fromString($command->tenantId);
|
||||
$homeworkId = HomeworkId::fromString($command->homeworkId);
|
||||
$teacherId = UserId::fromString($command->teacherId);
|
||||
$now = $this->clock->now();
|
||||
|
||||
$source = $this->homeworkRepository->get($homeworkId, $tenantId);
|
||||
|
||||
if ((string) $source->teacherId !== (string) $teacherId) {
|
||||
throw NonProprietaireDuDevoirException::withId($homeworkId);
|
||||
}
|
||||
|
||||
$calendar = $this->calendarProvider->forCurrentYear($tenantId);
|
||||
|
||||
// Dédupliquer et exclure la classe source
|
||||
$uniqueClassIds = array_values(array_unique($command->targetClassIds));
|
||||
$sourceClassIdStr = (string) $source->classId;
|
||||
|
||||
$targetClassIds = [];
|
||||
$dueDates = [];
|
||||
$validationResults = [];
|
||||
$seen = [];
|
||||
|
||||
foreach ($uniqueClassIds as $classIdStr) {
|
||||
if ($classIdStr === $sourceClassIdStr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($classIdStr, $seen, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$seen[] = $classIdStr;
|
||||
$classId = ClassId::fromString($classIdStr);
|
||||
|
||||
try {
|
||||
$dueDate = isset($command->dueDates[$classIdStr])
|
||||
? new DateTimeImmutable($command->dueDates[$classIdStr])
|
||||
: $source->dueDate;
|
||||
} catch (Exception) {
|
||||
$validationResults[] = ClassValidationResult::failure(
|
||||
$classId,
|
||||
"Date d'échéance invalide : {$command->dueDates[$classIdStr]}.",
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->affectationChecker->estAffecte($teacherId, $classId, $source->subjectId, $tenantId)) {
|
||||
$validationResults[] = ClassValidationResult::failure(
|
||||
$classId,
|
||||
"L'enseignant n'est pas affecté à cette classe pour cette matière.",
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->dueDateValidator->valider($dueDate, $now, $calendar);
|
||||
$validationResults[] = ClassValidationResult::success($classId);
|
||||
$targetClassIds[] = $classId;
|
||||
$dueDates[] = $dueDate;
|
||||
} catch (DateEcheanceInvalideException $e) {
|
||||
$validationResults[] = ClassValidationResult::failure($classId, $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$failures = array_filter($validationResults, static fn (ClassValidationResult $r): bool => !$r->valid);
|
||||
|
||||
if ($failures !== []) {
|
||||
throw DuplicationValidationException::withResults($validationResults);
|
||||
}
|
||||
|
||||
$duplicates = $this->duplicator->dupliquer($source, $targetClassIds, $dueDates, $now);
|
||||
|
||||
$sourceAttachments = $this->attachmentRepository->findByHomeworkId($source->id);
|
||||
|
||||
$this->connection->beginTransaction();
|
||||
|
||||
try {
|
||||
foreach ($duplicates as $duplicate) {
|
||||
$this->homeworkRepository->save($duplicate);
|
||||
|
||||
foreach ($sourceAttachments as $attachment) {
|
||||
$this->attachmentRepository->save(
|
||||
$duplicate->id,
|
||||
new HomeworkAttachment(
|
||||
id: HomeworkAttachmentId::generate(),
|
||||
filename: $attachment->filename,
|
||||
filePath: $attachment->filePath,
|
||||
fileSize: $attachment->fileSize,
|
||||
mimeType: $attachment->mimeType,
|
||||
uploadedAt: $now,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->connection->commit();
|
||||
} catch (Throwable $e) {
|
||||
$this->connection->rollBack();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$warnings = array_values(array_filter(
|
||||
$validationResults,
|
||||
static fn (ClassValidationResult $r): bool => $r->valid && $r->warning !== null,
|
||||
));
|
||||
|
||||
return new DuplicationResult($duplicates, $warnings);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Application\Command\DuplicateHomework;
|
||||
|
||||
use App\Scolarite\Domain\Model\Homework\Homework;
|
||||
use App\Scolarite\Domain\ValueObject\ClassValidationResult;
|
||||
|
||||
final readonly class DuplicationResult
|
||||
{
|
||||
/**
|
||||
* @param array<Homework> $homeworks
|
||||
* @param array<ClassValidationResult> $warnings
|
||||
*/
|
||||
public function __construct(
|
||||
public array $homeworks,
|
||||
public array $warnings,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Domain\Exception;
|
||||
|
||||
use App\Scolarite\Domain\ValueObject\ClassValidationResult;
|
||||
|
||||
use function array_filter;
|
||||
use function array_map;
|
||||
|
||||
use DomainException;
|
||||
|
||||
use function implode;
|
||||
|
||||
final class DuplicationValidationException extends DomainException
|
||||
{
|
||||
/** @param array<ClassValidationResult> $results */
|
||||
private function __construct(
|
||||
string $message,
|
||||
public readonly array $results,
|
||||
) {
|
||||
parent::__construct($message);
|
||||
}
|
||||
|
||||
/** @param array<ClassValidationResult> $results */
|
||||
public static function withResults(array $results): self
|
||||
{
|
||||
$failures = array_filter($results, static fn (ClassValidationResult $r): bool => !$r->valid);
|
||||
$messages = array_map(
|
||||
static fn (ClassValidationResult $r): string => "Classe {$r->classId} : {$r->error}",
|
||||
$failures,
|
||||
);
|
||||
|
||||
return new self(
|
||||
'Validation échouée pour certaines classes : ' . implode('; ', $messages),
|
||||
$results,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Domain\Repository;
|
||||
|
||||
use App\Scolarite\Domain\Model\Homework\HomeworkAttachment;
|
||||
use App\Scolarite\Domain\Model\Homework\HomeworkId;
|
||||
|
||||
interface HomeworkAttachmentRepository
|
||||
{
|
||||
/** @return array<HomeworkAttachment> */
|
||||
public function findByHomeworkId(HomeworkId $homeworkId): array;
|
||||
|
||||
public function save(HomeworkId $homeworkId, HomeworkAttachment $attachment): void;
|
||||
}
|
||||
44
backend/src/Scolarite/Domain/Service/HomeworkDuplicator.php
Normal file
44
backend/src/Scolarite/Domain/Service/HomeworkDuplicator.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Domain\Service;
|
||||
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassId;
|
||||
use App\Scolarite\Domain\Model\Homework\Homework;
|
||||
use DateTimeImmutable;
|
||||
|
||||
final readonly class HomeworkDuplicator
|
||||
{
|
||||
/**
|
||||
* @param array<ClassId> $targetClassIds
|
||||
* @param array<DateTimeImmutable> $dueDates Dates indexées par position (même ordre que $targetClassIds)
|
||||
*
|
||||
* @return array<Homework>
|
||||
*/
|
||||
public function dupliquer(
|
||||
Homework $source,
|
||||
array $targetClassIds,
|
||||
array $dueDates,
|
||||
DateTimeImmutable $now,
|
||||
): array {
|
||||
$duplicates = [];
|
||||
|
||||
foreach ($targetClassIds as $index => $classId) {
|
||||
$dueDate = $dueDates[$index] ?? $source->dueDate;
|
||||
|
||||
$duplicates[] = Homework::creer(
|
||||
tenantId: $source->tenantId,
|
||||
classId: $classId,
|
||||
subjectId: $source->subjectId,
|
||||
teacherId: $source->teacherId,
|
||||
title: $source->title,
|
||||
description: $source->description,
|
||||
dueDate: $dueDate,
|
||||
now: $now,
|
||||
);
|
||||
}
|
||||
|
||||
return $duplicates;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Domain\ValueObject;
|
||||
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassId;
|
||||
|
||||
final readonly class ClassValidationResult
|
||||
{
|
||||
private function __construct(
|
||||
public ClassId $classId,
|
||||
public bool $valid,
|
||||
public ?string $error,
|
||||
public ?string $warning,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function success(ClassId $classId, ?string $warning = null): self
|
||||
{
|
||||
return new self($classId, true, null, $warning);
|
||||
}
|
||||
|
||||
public static function failure(ClassId $classId, string $error): self
|
||||
{
|
||||
return new self($classId, false, $error, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Infrastructure\Api\Controller;
|
||||
|
||||
use App\Administration\Infrastructure\Security\SecurityUser;
|
||||
use App\Scolarite\Application\Command\DuplicateHomework\DuplicateHomeworkCommand;
|
||||
use App\Scolarite\Application\Command\DuplicateHomework\DuplicateHomeworkHandler;
|
||||
use App\Scolarite\Domain\Exception\DuplicationValidationException;
|
||||
use App\Scolarite\Domain\Exception\HomeworkNotFoundException;
|
||||
use App\Scolarite\Domain\Exception\NonProprietaireDuDevoirException;
|
||||
use App\Scolarite\Domain\Model\Homework\Homework;
|
||||
use App\Scolarite\Domain\ValueObject\ClassValidationResult;
|
||||
use App\Shared\Infrastructure\Tenant\TenantContext;
|
||||
|
||||
use function array_map;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
|
||||
use Ramsey\Uuid\Exception\InvalidUuidStringException;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
final readonly class DuplicateHomeworkController
|
||||
{
|
||||
public function __construct(
|
||||
private DuplicateHomeworkHandler $handler,
|
||||
private TenantContext $tenantContext,
|
||||
private Security $security,
|
||||
private MessageBusInterface $eventBus,
|
||||
) {
|
||||
}
|
||||
|
||||
#[Route('/api/homework/{id}/duplicate', name: 'api_homework_duplicate', methods: ['POST'])]
|
||||
public function __invoke(string $id, Request $request): JsonResponse
|
||||
{
|
||||
if (!$this->tenantContext->hasTenant()) {
|
||||
throw new UnauthorizedHttpException('Bearer', 'Tenant non défini.');
|
||||
}
|
||||
|
||||
$user = $this->security->getUser();
|
||||
|
||||
if (!$user instanceof SecurityUser) {
|
||||
throw new UnauthorizedHttpException('Bearer', 'Authentification requise.');
|
||||
}
|
||||
|
||||
$body = $request->toArray();
|
||||
|
||||
$targetClassIds = $body['targetClassIds'] ?? null;
|
||||
|
||||
if (!is_array($targetClassIds) || $targetClassIds === []) {
|
||||
throw new BadRequestHttpException('Au moins une classe cible est requise.');
|
||||
}
|
||||
|
||||
/** @var array<string, string> $dueDates */
|
||||
$dueDates = [];
|
||||
$rawDueDates = $body['dueDates'] ?? [];
|
||||
|
||||
if (is_array($rawDueDates)) {
|
||||
foreach ($rawDueDates as $classId => $date) {
|
||||
if (is_string($classId) && is_string($date)) {
|
||||
$dueDates[$classId] = $date;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$classIds = [];
|
||||
|
||||
foreach ($targetClassIds as $item) {
|
||||
if (!is_string($item)) {
|
||||
throw new BadRequestHttpException('Les identifiants de classe doivent être des chaînes.');
|
||||
}
|
||||
|
||||
$classIds[] = $item;
|
||||
}
|
||||
|
||||
try {
|
||||
$command = new DuplicateHomeworkCommand(
|
||||
tenantId: (string) $this->tenantContext->getCurrentTenantId(),
|
||||
homeworkId: $id,
|
||||
teacherId: $user->userId(),
|
||||
targetClassIds: $classIds,
|
||||
dueDates: $dueDates,
|
||||
);
|
||||
|
||||
$result = ($this->handler)($command);
|
||||
|
||||
foreach ($result->homeworks as $homework) {
|
||||
foreach ($homework->pullDomainEvents() as $event) {
|
||||
$this->eventBus->dispatch($event);
|
||||
}
|
||||
}
|
||||
|
||||
$response = [
|
||||
'data' => array_map(static fn (Homework $h): array => [
|
||||
'id' => (string) $h->id,
|
||||
'classId' => (string) $h->classId,
|
||||
'title' => $h->title,
|
||||
'dueDate' => $h->dueDate->format('Y-m-d'),
|
||||
'status' => $h->status->value,
|
||||
], $result->homeworks),
|
||||
];
|
||||
|
||||
if ($result->warnings !== []) {
|
||||
$response['warnings'] = array_map(static fn (ClassValidationResult $r): array => [
|
||||
'classId' => (string) $r->classId,
|
||||
'warning' => $r->warning,
|
||||
], $result->warnings);
|
||||
}
|
||||
|
||||
return new JsonResponse($response, Response::HTTP_CREATED);
|
||||
} catch (HomeworkNotFoundException $e) {
|
||||
throw new NotFoundHttpException($e->getMessage());
|
||||
} catch (NonProprietaireDuDevoirException $e) {
|
||||
throw new NotFoundHttpException($e->getMessage());
|
||||
} catch (DuplicationValidationException $e) {
|
||||
return new JsonResponse([
|
||||
'error' => $e->getMessage(),
|
||||
'results' => array_map(static fn (ClassValidationResult $r): array => [
|
||||
'classId' => (string) $r->classId,
|
||||
'valid' => $r->valid,
|
||||
'error' => $r->error,
|
||||
], $e->results),
|
||||
], Response::HTTP_BAD_REQUEST);
|
||||
} catch (InvalidUuidStringException $e) {
|
||||
throw new BadRequestHttpException('UUID invalide : ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ namespace App\Scolarite\Infrastructure\Api\Provider;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProviderInterface;
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassId;
|
||||
use App\Administration\Domain\Model\User\UserId;
|
||||
use App\Administration\Domain\Repository\ClassRepository;
|
||||
use App\Administration\Domain\Repository\SubjectRepository;
|
||||
@@ -15,10 +16,15 @@ use App\Scolarite\Domain\Repository\HomeworkRepository;
|
||||
use App\Scolarite\Infrastructure\Api\Resource\HomeworkResource;
|
||||
use App\Shared\Infrastructure\Tenant\TenantContext;
|
||||
|
||||
use function array_filter;
|
||||
use function array_map;
|
||||
use function array_values;
|
||||
use function is_string;
|
||||
|
||||
use Override;
|
||||
use Ramsey\Uuid\Exception\InvalidUuidStringException;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
|
||||
|
||||
/**
|
||||
@@ -54,6 +60,23 @@ final readonly class HomeworkCollectionProvider implements ProviderInterface
|
||||
|
||||
$homeworks = $this->homeworkRepository->findByTeacher($teacherId, $tenantId);
|
||||
|
||||
/** @var array<string, mixed> $filters */
|
||||
$filters = $context['filters'] ?? [];
|
||||
$classIdFilter = $filters['classId'] ?? null;
|
||||
|
||||
if (is_string($classIdFilter) && $classIdFilter !== '') {
|
||||
try {
|
||||
$filterClassId = ClassId::fromString($classIdFilter);
|
||||
} catch (InvalidUuidStringException $e) {
|
||||
throw new BadRequestHttpException('UUID de classe invalide : ' . $e->getMessage());
|
||||
}
|
||||
|
||||
$homeworks = array_values(array_filter(
|
||||
$homeworks,
|
||||
static fn (Homework $h): bool => $h->classId->equals($filterClassId),
|
||||
));
|
||||
}
|
||||
|
||||
return array_map(fn (Homework $homework) => HomeworkResource::fromDomain(
|
||||
$homework,
|
||||
$this->resolveClassName($homework),
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
<?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_map;
|
||||
|
||||
use DateTimeImmutable;
|
||||
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 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),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/** @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),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Infrastructure\Persistence\InMemory;
|
||||
|
||||
use App\Scolarite\Domain\Model\Homework\HomeworkAttachment;
|
||||
use App\Scolarite\Domain\Model\Homework\HomeworkId;
|
||||
use App\Scolarite\Domain\Repository\HomeworkAttachmentRepository;
|
||||
use Override;
|
||||
|
||||
final class InMemoryHomeworkAttachmentRepository implements HomeworkAttachmentRepository
|
||||
{
|
||||
/** @var array<string, array<HomeworkAttachment>> */
|
||||
private array $byHomeworkId = [];
|
||||
|
||||
#[Override]
|
||||
public function findByHomeworkId(HomeworkId $homeworkId): array
|
||||
{
|
||||
return $this->byHomeworkId[(string) $homeworkId] ?? [];
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function save(HomeworkId $homeworkId, HomeworkAttachment $attachment): void
|
||||
{
|
||||
$this->byHomeworkId[(string) $homeworkId][] = $attachment;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user