*/ public function findPaginated( string $tenantId, string $academicYearId, ?string $search, int $page, int $limit, ): PaginatedResult { $params = [ 'tenant_id' => $tenantId, 'academic_year_id' => $academicYearId, 'status' => ClassStatus::ACTIVE->value, ]; $whereClause = 'sc.tenant_id = :tenant_id AND sc.academic_year_id = :academic_year_id AND sc.status = :status'; if ($search !== null && $search !== '') { $whereClause .= ' AND (sc.name ILIKE :search OR sc.level ILIKE :search)'; $params['search'] = '%' . $search . '%'; } $countSql = "SELECT COUNT(*) FROM school_classes sc WHERE {$whereClause}"; /** @var int|string|false $totalRaw */ $totalRaw = $this->connection->fetchOne($countSql, $params); $total = (int) $totalRaw; $offset = ($page - 1) * $limit; $selectSql = <<connection->fetchAllAssociative($selectSql, $params); $items = array_map(static function (array $row): ClassDto { /** @var string $id */ $id = $row['id']; /** @var string $name */ $name = $row['name']; /** @var string|null $level */ $level = $row['level']; /** @var int|string|null $capacityRaw */ $capacityRaw = $row['capacity']; $capacity = $capacityRaw !== null ? (int) $capacityRaw : null; /** @var string $status */ $status = $row['status']; /** @var string|null $description */ $description = $row['description']; /** @var string $createdAt */ $createdAt = $row['created_at']; /** @var string $updatedAt */ $updatedAt = $row['updated_at']; return new ClassDto( id: $id, name: $name, level: $level, capacity: $capacity, status: $status, description: $description, createdAt: new DateTimeImmutable($createdAt), updatedAt: new DateTimeImmutable($updatedAt), ); }, $rows); return new PaginatedResult( items: $items, total: $total, page: $page, limit: $limit, ); } }