Les listes admin (utilisateurs, classes, matières, affectations) chargeaient toutes les données d'un coup, ce qui dégradait l'expérience avec un volume croissant. La pagination côté serveur existait dans la config API Platform mais aucun Provider ne l'exploitait. Cette implémentation ajoute la pagination serveur (30 items/page, max 100) avec recherche textuelle sur toutes les sections, des composants frontend réutilisables (Pagination + SearchInput avec debounce), et la synchronisation URL pour le partage de liens filtrés. Les Query valident leurs paramètres (clamp page/limit, trim search) pour éviter les abus. Les affectations utilisent des lookup maps pour résoudre les noms sans N+1 queries. Les pages admin gèrent les race conditions via AbortController.
84 lines
2.5 KiB
PHP
84 lines
2.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Administration\Application\Query\GetUsers;
|
|
|
|
use App\Administration\Application\Dto\PaginatedResult;
|
|
use App\Administration\Domain\Model\User\Role;
|
|
use App\Administration\Domain\Model\User\StatutCompte;
|
|
use App\Administration\Domain\Repository\UserRepository;
|
|
use App\Shared\Domain\Clock;
|
|
use App\Shared\Domain\Tenant\TenantId;
|
|
|
|
use function array_slice;
|
|
use function count;
|
|
|
|
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
|
|
|
#[AsMessageHandler(bus: 'query.bus')]
|
|
final readonly class GetUsersHandler
|
|
{
|
|
public function __construct(
|
|
private UserRepository $userRepository,
|
|
private Clock $clock,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* @return PaginatedResult<UserDto>
|
|
*/
|
|
public function __invoke(GetUsersQuery $query): PaginatedResult
|
|
{
|
|
$users = $this->userRepository->findAllByTenant(
|
|
TenantId::fromString($query->tenantId),
|
|
);
|
|
|
|
if ($query->role !== null) {
|
|
$filterRole = Role::tryFrom($query->role);
|
|
if ($filterRole !== null) {
|
|
$users = array_filter(
|
|
$users,
|
|
static fn ($user) => $user->aLeRole($filterRole),
|
|
);
|
|
}
|
|
}
|
|
|
|
if ($query->statut !== null) {
|
|
$filterStatut = StatutCompte::tryFrom($query->statut);
|
|
if ($filterStatut !== null) {
|
|
$users = array_filter(
|
|
$users,
|
|
static fn ($user) => $user->statut === $filterStatut,
|
|
);
|
|
}
|
|
}
|
|
|
|
if ($query->search !== null && $query->search !== '') {
|
|
$searchLower = mb_strtolower($query->search);
|
|
$users = array_filter(
|
|
$users,
|
|
static fn ($user) => str_contains(mb_strtolower($user->firstName), $searchLower)
|
|
|| str_contains(mb_strtolower($user->lastName), $searchLower)
|
|
|| str_contains(mb_strtolower((string) $user->email), $searchLower),
|
|
);
|
|
}
|
|
|
|
$users = array_values($users);
|
|
$total = count($users);
|
|
|
|
$offset = ($query->page - 1) * $query->limit;
|
|
$items = array_slice($users, $offset, $query->limit);
|
|
|
|
return new PaginatedResult(
|
|
items: array_map(
|
|
fn ($user) => UserDto::fromDomain($user, $this->clock),
|
|
$items,
|
|
),
|
|
total: $total,
|
|
page: $query->page,
|
|
limit: $query->limit,
|
|
);
|
|
}
|
|
}
|