feat: Permettre à l'élève de consulter ses devoirs
L'élève n'avait aucun moyen de voir les devoirs assignés à sa classe. Cette fonctionnalité ajoute la consultation complète : liste triée par échéance, détail avec pièces jointes, filtrage par matière, et marquage personnel « fait » en localStorage. Le dashboard élève affiche désormais les devoirs à venir avec ouverture du détail en modale, et un lien vers la page complète. L'accès API est sécurisé par vérification de la classe de l'élève (pas d'IDOR) et validation du chemin des pièces jointes (pas de path traversal).
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Application\Query\GetStudentHomework;
|
||||
|
||||
final readonly class AttachmentDto
|
||||
{
|
||||
public function __construct(
|
||||
public string $id,
|
||||
public string $filename,
|
||||
public int $fileSize,
|
||||
public string $mimeType,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Application\Query\GetStudentHomework;
|
||||
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassId;
|
||||
use App\Scolarite\Application\Port\ScheduleDisplayReader;
|
||||
use App\Scolarite\Application\Port\StudentClassReader;
|
||||
use App\Scolarite\Domain\Model\Homework\Homework;
|
||||
use App\Scolarite\Domain\Model\Homework\HomeworkId;
|
||||
use App\Scolarite\Domain\Repository\HomeworkAttachmentRepository;
|
||||
use App\Scolarite\Domain\Repository\HomeworkRepository;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
|
||||
use function array_filter;
|
||||
use function array_map;
|
||||
use function array_unique;
|
||||
use function array_values;
|
||||
|
||||
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
||||
|
||||
use function usort;
|
||||
|
||||
#[AsMessageHandler(bus: 'query.bus')]
|
||||
final readonly class GetStudentHomeworkHandler
|
||||
{
|
||||
public function __construct(
|
||||
private StudentClassReader $studentClassReader,
|
||||
private HomeworkRepository $homeworkRepository,
|
||||
private HomeworkAttachmentRepository $attachmentRepository,
|
||||
private ScheduleDisplayReader $displayReader,
|
||||
) {
|
||||
}
|
||||
|
||||
/** @return array<StudentHomeworkDto> */
|
||||
public function __invoke(GetStudentHomeworkQuery $query): array
|
||||
{
|
||||
$tenantId = TenantId::fromString($query->tenantId);
|
||||
|
||||
$classId = $this->studentClassReader->currentClassId($query->studentId, $tenantId);
|
||||
|
||||
if ($classId === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$homeworks = $this->homeworkRepository->findByClass(ClassId::fromString($classId), $tenantId);
|
||||
|
||||
if ($query->subjectId !== null) {
|
||||
$filterSubjectId = $query->subjectId;
|
||||
$homeworks = array_values(array_filter(
|
||||
$homeworks,
|
||||
static fn (Homework $h): bool => (string) $h->subjectId === $filterSubjectId,
|
||||
));
|
||||
}
|
||||
|
||||
usort($homeworks, static fn (Homework $a, Homework $b): int => $a->dueDate <=> $b->dueDate);
|
||||
|
||||
return $this->enrichHomeworks($homeworks, $query->tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<Homework> $homeworks
|
||||
*
|
||||
* @return array<StudentHomeworkDto>
|
||||
*/
|
||||
private function enrichHomeworks(array $homeworks, string $tenantId): array
|
||||
{
|
||||
if ($homeworks === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$subjectIds = array_values(array_unique(
|
||||
array_map(static fn (Homework $h): string => (string) $h->subjectId, $homeworks),
|
||||
));
|
||||
$teacherIds = array_values(array_unique(
|
||||
array_map(static fn (Homework $h): string => (string) $h->teacherId, $homeworks),
|
||||
));
|
||||
|
||||
$subjects = $this->displayReader->subjectDisplay($tenantId, ...$subjectIds);
|
||||
$teacherNames = $this->displayReader->teacherNames($tenantId, ...$teacherIds);
|
||||
|
||||
$homeworkIds = array_map(static fn (Homework $h): HomeworkId => $h->id, $homeworks);
|
||||
$attachmentMap = $this->attachmentRepository->hasAttachments(...$homeworkIds);
|
||||
|
||||
return array_map(
|
||||
static fn (Homework $h): StudentHomeworkDto => StudentHomeworkDto::fromDomain(
|
||||
$h,
|
||||
$subjects[(string) $h->subjectId]['name'] ?? '',
|
||||
$subjects[(string) $h->subjectId]['color'] ?? null,
|
||||
$teacherNames[(string) $h->teacherId] ?? '',
|
||||
$attachmentMap[(string) $h->id] ?? false,
|
||||
),
|
||||
$homeworks,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Application\Query\GetStudentHomework;
|
||||
|
||||
final readonly class GetStudentHomeworkQuery
|
||||
{
|
||||
public function __construct(
|
||||
public string $studentId,
|
||||
public string $tenantId,
|
||||
public ?string $subjectId = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Application\Query\GetStudentHomework;
|
||||
|
||||
use App\Scolarite\Domain\Model\Homework\Homework;
|
||||
use App\Scolarite\Domain\Model\Homework\HomeworkAttachment;
|
||||
|
||||
use function array_map;
|
||||
|
||||
final readonly class StudentHomeworkDetailDto
|
||||
{
|
||||
/**
|
||||
* @param array<AttachmentDto> $attachments
|
||||
*/
|
||||
public function __construct(
|
||||
public string $id,
|
||||
public string $subjectId,
|
||||
public string $subjectName,
|
||||
public ?string $subjectColor,
|
||||
public string $teacherId,
|
||||
public string $teacherName,
|
||||
public string $title,
|
||||
public ?string $description,
|
||||
public string $dueDate,
|
||||
public string $createdAt,
|
||||
public array $attachments,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<HomeworkAttachment> $attachments
|
||||
*/
|
||||
public static function fromDomain(
|
||||
Homework $homework,
|
||||
string $subjectName,
|
||||
?string $subjectColor,
|
||||
string $teacherName,
|
||||
array $attachments,
|
||||
): self {
|
||||
return new self(
|
||||
id: (string) $homework->id,
|
||||
subjectId: (string) $homework->subjectId,
|
||||
subjectName: $subjectName,
|
||||
subjectColor: $subjectColor,
|
||||
teacherId: (string) $homework->teacherId,
|
||||
teacherName: $teacherName,
|
||||
title: $homework->title,
|
||||
description: $homework->description,
|
||||
dueDate: $homework->dueDate->format('Y-m-d'),
|
||||
createdAt: $homework->createdAt->format('Y-m-d\TH:i:sP'),
|
||||
attachments: array_map(
|
||||
static fn (HomeworkAttachment $a): AttachmentDto => new AttachmentDto(
|
||||
id: (string) $a->id,
|
||||
filename: $a->filename,
|
||||
fileSize: $a->fileSize,
|
||||
mimeType: $a->mimeType,
|
||||
),
|
||||
$attachments,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Application\Query\GetStudentHomework;
|
||||
|
||||
use App\Scolarite\Domain\Model\Homework\Homework;
|
||||
|
||||
final readonly class StudentHomeworkDto
|
||||
{
|
||||
public function __construct(
|
||||
public string $id,
|
||||
public string $subjectId,
|
||||
public string $subjectName,
|
||||
public ?string $subjectColor,
|
||||
public string $teacherId,
|
||||
public string $teacherName,
|
||||
public string $title,
|
||||
public ?string $description,
|
||||
public string $dueDate,
|
||||
public string $createdAt,
|
||||
public bool $hasAttachments,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function fromDomain(
|
||||
Homework $homework,
|
||||
string $subjectName,
|
||||
?string $subjectColor,
|
||||
string $teacherName,
|
||||
bool $hasAttachments,
|
||||
): self {
|
||||
return new self(
|
||||
id: (string) $homework->id,
|
||||
subjectId: (string) $homework->subjectId,
|
||||
subjectName: $subjectName,
|
||||
subjectColor: $subjectColor,
|
||||
teacherId: (string) $homework->teacherId,
|
||||
teacherName: $teacherName,
|
||||
title: $homework->title,
|
||||
description: $homework->description,
|
||||
dueDate: $homework->dueDate->format('Y-m-d'),
|
||||
createdAt: $homework->createdAt->format('Y-m-d\TH:i:sP'),
|
||||
hasAttachments: $hasAttachments,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user