108 lines
3.8 KiB
PHP
108 lines
3.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Service\Pagination;
|
|
|
|
use Doctrine\ORM\QueryBuilder;
|
|
use Pagerfanta\Doctrine\ORM\QueryAdapter;
|
|
use Pagerfanta\Exception\NotValidCurrentPageException;
|
|
use Pagerfanta\Pagerfanta;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
|
|
/**
|
|
* Унифицированная обёртка над Pagerfanta + QueryAdapter.
|
|
*
|
|
* Соответствует существующему стилю проекта (см. PriceListController/SpecialistController):
|
|
* читает page/perPage из Request, ограничивает perPage и возвращает массив
|
|
* ['data' => [...], 'pagination' => [...]] в едином формате для новых list-контрактов.
|
|
*/
|
|
final class Paginator
|
|
{
|
|
public const DEFAULT_PER_PAGE = 50;
|
|
public const MAX_PER_PAGE = 500;
|
|
|
|
/**
|
|
* @return array{data: list<mixed>, pagination: array<string, int|bool>}
|
|
*/
|
|
public function paginate(
|
|
QueryBuilder $qb,
|
|
Request $request,
|
|
int $defaultPerPage = self::DEFAULT_PER_PAGE,
|
|
int $maxPerPage = self::MAX_PER_PAGE,
|
|
): array {
|
|
$page = max(1, $request->query->getInt('page', 1));
|
|
$perPage = min(
|
|
max(1, $request->query->getInt('perPage', $defaultPerPage)),
|
|
$maxPerPage,
|
|
);
|
|
|
|
$pagerfanta = (new Pagerfanta(new QueryAdapter($qb)))
|
|
->setMaxPerPage($perPage);
|
|
|
|
try {
|
|
$pagerfanta->setCurrentPage($page);
|
|
} catch (NotValidCurrentPageException) {
|
|
// выходим за пределы — возвращаем пустую страницу с корректным total
|
|
$pagerfanta->setCurrentPage(max(1, $pagerfanta->getNbPages()));
|
|
}
|
|
|
|
$data = iterator_to_array($pagerfanta->getCurrentPageResults(), false);
|
|
|
|
return [
|
|
'data' => $data,
|
|
'pagination' => [
|
|
'total' => $pagerfanta->getNbResults(),
|
|
'count' => count($data),
|
|
'per_page' => $pagerfanta->getMaxPerPage(),
|
|
'current_page' => $pagerfanta->getCurrentPage(),
|
|
'total_pages' => $pagerfanta->getNbPages(),
|
|
'has_previous_page' => $pagerfanta->hasPreviousPage(),
|
|
'has_next_page' => $pagerfanta->hasNextPage(),
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Legacy-формат для ArticleController.
|
|
*
|
|
* Старый контракт /article/list уже использовался клиентами:
|
|
* - размер страницы приходит в query-параметре limit;
|
|
* - метаданные лежат в ключе meta;
|
|
* - поля называются total/page/limit/totalPages.
|
|
*
|
|
* @return array{data: list<mixed>, meta: array{total: int, page: int, limit: int, totalPages: int}}
|
|
*/
|
|
public function paginateWithLegacyMeta(
|
|
QueryBuilder $qb,
|
|
Request $request,
|
|
int $defaultLimit = 20,
|
|
int $maxLimit = 100,
|
|
): array {
|
|
$page = max(1, $request->query->getInt('page', 1));
|
|
$limit = min(
|
|
max(1, $request->query->getInt('limit', $defaultLimit)),
|
|
$maxLimit,
|
|
);
|
|
|
|
$pagerfanta = (new Pagerfanta(new QueryAdapter($qb)))
|
|
->setMaxPerPage($limit);
|
|
|
|
try {
|
|
$pagerfanta->setCurrentPage($page);
|
|
} catch (NotValidCurrentPageException) {
|
|
$pagerfanta->setCurrentPage(max(1, $pagerfanta->getNbPages()));
|
|
}
|
|
|
|
return [
|
|
'data' => iterator_to_array($pagerfanta->getCurrentPageResults(), false),
|
|
'meta' => [
|
|
'total' => $pagerfanta->getNbResults(),
|
|
'page' => $pagerfanta->getCurrentPage(),
|
|
'limit' => $pagerfanta->getMaxPerPage(),
|
|
'totalPages' => $pagerfanta->getNbPages(),
|
|
],
|
|
];
|
|
}
|
|
}
|