Les administrateurs ont besoin d'un moyen simple pour inviter les parents à rejoindre la plateforme. Cette fonctionnalité permet de générer des codes d'invitation uniques (8 caractères alphanumériques) avec une validité de 48h, de les envoyer par email, et de les activer via une page publique dédiée qui crée automatiquement le compte parent. L'interface d'administration offre l'envoi unitaire et en masse, le renvoi, le filtrage par statut, ainsi que la visualisation de l'état de chaque invitation (en attente, activée, expirée).
133 lines
4.4 KiB
PHP
133 lines
4.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Administration\Application\Query\GetParentInvitations;
|
|
|
|
use App\Administration\Application\Dto\PaginatedResult;
|
|
use App\Administration\Domain\Model\Invitation\InvitationStatus;
|
|
use App\Administration\Domain\Model\User\UserId;
|
|
use App\Administration\Domain\Repository\ParentInvitationRepository;
|
|
use App\Administration\Domain\Repository\UserRepository;
|
|
use App\Shared\Domain\Tenant\TenantId;
|
|
|
|
use function array_filter;
|
|
use function array_map;
|
|
use function array_slice;
|
|
use function array_values;
|
|
use function count;
|
|
use function mb_strtolower;
|
|
use function str_contains;
|
|
|
|
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
|
use Throwable;
|
|
|
|
#[AsMessageHandler(bus: 'query.bus')]
|
|
final readonly class GetParentInvitationsHandler
|
|
{
|
|
public function __construct(
|
|
private ParentInvitationRepository $invitationRepository,
|
|
private UserRepository $userRepository,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* @return PaginatedResult<ParentInvitationDto>
|
|
*/
|
|
public function __invoke(GetParentInvitationsQuery $query): PaginatedResult
|
|
{
|
|
$tenantId = TenantId::fromString($query->tenantId);
|
|
|
|
$invitations = $this->invitationRepository->findAllByTenant($tenantId);
|
|
|
|
if ($query->status !== null) {
|
|
$filterStatus = InvitationStatus::tryFrom($query->status);
|
|
if ($filterStatus !== null) {
|
|
$invitations = array_filter(
|
|
$invitations,
|
|
static fn ($inv) => $inv->status === $filterStatus,
|
|
);
|
|
}
|
|
}
|
|
|
|
if ($query->studentId !== null) {
|
|
$filterStudentId = UserId::fromString($query->studentId);
|
|
$invitations = array_filter(
|
|
$invitations,
|
|
static fn ($inv) => $inv->studentId->equals($filterStudentId),
|
|
);
|
|
}
|
|
|
|
// Build a student name cache for search and DTO enrichment
|
|
$studentNames = $this->loadStudentNames($invitations);
|
|
|
|
if ($query->search !== null && $query->search !== '') {
|
|
$searchLower = mb_strtolower($query->search);
|
|
$invitations = array_filter(
|
|
$invitations,
|
|
static function ($inv) use ($searchLower, $studentNames) {
|
|
$studentId = (string) $inv->studentId;
|
|
$firstName = $studentNames[$studentId]['firstName'] ?? '';
|
|
$lastName = $studentNames[$studentId]['lastName'] ?? '';
|
|
|
|
return str_contains(mb_strtolower((string) $inv->parentEmail), $searchLower)
|
|
|| str_contains(mb_strtolower($firstName), $searchLower)
|
|
|| str_contains(mb_strtolower($lastName), $searchLower);
|
|
},
|
|
);
|
|
}
|
|
|
|
$invitations = array_values($invitations);
|
|
$total = count($invitations);
|
|
|
|
$offset = ($query->page - 1) * $query->limit;
|
|
$items = array_slice($invitations, $offset, $query->limit);
|
|
|
|
return new PaginatedResult(
|
|
items: array_map(
|
|
static function ($inv) use ($studentNames) {
|
|
$studentId = (string) $inv->studentId;
|
|
|
|
return ParentInvitationDto::fromDomain(
|
|
$inv,
|
|
$studentNames[$studentId]['firstName'] ?? null,
|
|
$studentNames[$studentId]['lastName'] ?? null,
|
|
);
|
|
},
|
|
$items,
|
|
),
|
|
total: $total,
|
|
page: $query->page,
|
|
limit: $query->limit,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param iterable<\App\Administration\Domain\Model\Invitation\ParentInvitation> $invitations
|
|
*
|
|
* @return array<string, array{firstName: string, lastName: string}>
|
|
*/
|
|
private function loadStudentNames(iterable $invitations): array
|
|
{
|
|
$studentIds = [];
|
|
foreach ($invitations as $inv) {
|
|
$studentIds[(string) $inv->studentId] = true;
|
|
}
|
|
|
|
$names = [];
|
|
foreach ($studentIds as $id => $_) {
|
|
try {
|
|
$student = $this->userRepository->get(UserId::fromString($id));
|
|
$names[$id] = [
|
|
'firstName' => $student->firstName,
|
|
'lastName' => $student->lastName,
|
|
];
|
|
} catch (Throwable) {
|
|
$names[$id] = ['firstName' => '', 'lastName' => ''];
|
|
}
|
|
}
|
|
|
|
return $names;
|
|
}
|
|
}
|