I have a project created with the latest version of symfony. There are a lot of entities with different relationships like OneToMany
, ManyToOne
, and ManyToMany
. Each of these entities have second level caching set and it’s tested and working accordinly.
However, when used in the context of pagination, caching only works partially. Query caching and result caching for example does not work; only second level cache is hit which is not enough.
This is my paginator class:
<?php
namespace AppPagination;
use DoctrineORMQueryBuilder as DoctrineQueryBuilder;
use DoctrineORMToolsPaginationCountWalker;
use DoctrineORMToolsPaginationPaginator as DoctrinePaginator;
final class Paginator
{
/**
* Use constants to define configuration options that rarely change instead
* of specifying them under parameters section in config/services.yaml file.
*
* See https://symfony.com/doc/current/best_practices.html#use-constants-to-define-options-that-rarely-change
*/
final public const PAGE_SIZE = 10;
private int $currentPage;
private int $numResults;
/**
* @var Traversable<array-key, object>
*/
private Traversable $results;
public function __construct(
private readonly DoctrineQueryBuilder $queryBuilder,
private readonly int $pageSize = self::PAGE_SIZE
) {}
public function paginate(int $page = 1, bool $useCache = false, bool $useL2Cache = false): self
{
$this->currentPage = max(1, $page);
$firstResult = ($this->currentPage - 1) * $this->pageSize;
$query = $this->queryBuilder
->setFirstResult($firstResult)
->setMaxResults($this->pageSize)
->getQuery();
if ($useCache) {
$query->useQueryCache(true);
$query->enableResultCache(500);
}
if ($useL2Cache) {
$query->setCacheable(true);
}
/** @var array<string, mixed> $joinDqlParts */
$joinDqlParts = $this->queryBuilder->getDQLPart('join');
if (0 === count($joinDqlParts)) {
$query->setHint(CountWalker::HINT_DISTINCT, false);
}
/** @var DoctrinePaginator<object> $paginator */
$paginator = new DoctrinePaginator($query, true);
/** @var array<string, mixed> $havingDqlParts */
$havingDqlParts = $this->queryBuilder->getDQLPart('having');
$useOutputWalkers = count($havingDqlParts ?: []) > 0;
$paginator->setUseOutputWalkers($useOutputWalkers);
$this->results = $paginator->getIterator();
$this->numResults = $paginator->count();
return $this;
}
public function getCurrentPage(): int
{
return $this->currentPage;
}
public function getLastPage(): int
{
return (int) ceil($this->numResults / $this->pageSize);
}
public function getPageSize(): int
{
return $this->pageSize;
}
public function hasPreviousPage(): bool
{
return $this->currentPage > 1;
}
public function getPreviousPage(): int
{
return max(1, $this->currentPage - 1);
}
public function hasNextPage(): bool
{
return $this->currentPage < $this->getLastPage();
}
public function getNextPage(): int
{
return min($this->getLastPage(), $this->currentPage + 1);
}
public function hasToPaginate(): bool
{
return $this->numResults > $this->pageSize;
}
public function getNumResults(): int
{
return $this->numResults;
}
/**
* @return Traversable<int, object>
*/
public function getResults(): Traversable
{
return $this->results;
}
}
I cannot figure out why the caching is not applied properly, can anyone advise please?
Thank you