*/ public function __invoke(GetStudentsWithClassQuery $query): PaginatedResult { $params = [ 'tenant_id' => $query->tenantId, 'academic_year_id' => $query->academicYearId, 'role' => json_encode([Role::ELEVE->value]), ]; $whereClause = 'u.tenant_id = :tenant_id AND u.roles::jsonb @> :role::jsonb'; if ($query->classId !== null) { $whereClause .= ' AND ca.school_class_id = :class_id'; $params['class_id'] = $query->classId; } if ($query->search !== null && $query->search !== '') { $whereClause .= ' AND (LOWER(u.last_name) LIKE :search OR LOWER(u.first_name) LIKE :search)'; $params['search'] = '%' . mb_strtolower($query->search) . '%'; } // Count total $countSql = <<connection->fetchOne($countSql, $params); $total = (int) $totalRaw; // Fetch paginated results $offset = ($query->page - 1) * $query->limit; $selectSql = <<limit; $params['offset'] = $offset; $rows = $this->connection->fetchAllAssociative($selectSql, $params); $items = array_map(static function (array $row): StudentWithClassDto { /** @var string $id */ $id = $row['id']; /** @var string $firstName */ $firstName = $row['first_name']; /** @var string $lastName */ $lastName = $row['last_name']; /** @var string|null $email */ $email = $row['email']; /** @var string $statut */ $statut = $row['statut']; /** @var string|null $studentNumber */ $studentNumber = $row['student_number']; /** @var string|null $dateNaissance */ $dateNaissance = $row['date_naissance']; /** @var string|null $classId */ $classId = $row['class_id']; /** @var string|null $className */ $className = $row['class_name']; /** @var string|null $classLevel */ $classLevel = $row['class_level']; return new StudentWithClassDto( id: $id, firstName: $firstName, lastName: $lastName, email: $email, statut: $statut, studentNumber: $studentNumber, dateNaissance: $dateNaissance, classId: $classId, className: $className, classLevel: $classLevel, ); }, $rows); return new PaginatedResult( items: $items, total: $total, page: $query->page, limit: $query->limit, ); } }