diff --git a/config/services.yaml b/config/services.yaml index cc9dd48..76970d4 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -5,7 +5,7 @@ services: public: true MalteHuebner\DataQueryBundle\: - resource: '../src/{DataQueryManager,Factory,FieldList,FinderFactory,Manager,Parameter,Query,RequestParameterList,Validator}' + resource: '../src/{DataQueryManager,Factory,FieldList,FinderFactory,Manager,PaginatedResult,Parameter,Query,RequestParameterList,Validator}' exclude: '../src/{DependencyInjection,Factory/ConflictResolver,Factory/ValueAssigner/ValueType.php,RequestParameterList/ArrayToListConverter.php,RequestParameterList/QueryStringToListConverter.php,RequestParameterList/RequestToListConverter.php,tests,MalteHuebnerDataQueryBundle}' Psr\Container\ContainerInterface: diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index a527b6a..5bcf714 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -55,6 +55,11 @@ parameters: count: 2 path: src/FieldList/QueryFieldList/QueryFieldListFactory.php + - + message: "#^Call to method createPaginatorAdapter\\(\\) on an unknown class FOS\\\\ElasticaBundle\\\\Repository\\.$#" + count: 1 + path: src/Finder/Finder.php + - message: "#^Call to method find\\(\\) on an unknown class FOS\\\\ElasticaBundle\\\\Repository\\.$#" count: 1 diff --git a/src/DataQueryManager/DataQueryManager.php b/src/DataQueryManager/DataQueryManager.php index 0666283..1389805 100644 --- a/src/DataQueryManager/DataQueryManager.php +++ b/src/DataQueryManager/DataQueryManager.php @@ -5,6 +5,9 @@ use MalteHuebner\DataQueryBundle\Factory\ParameterFactory\ParameterFactoryInterface; use MalteHuebner\DataQueryBundle\Factory\QueryFactory\QueryFactoryInterface; use MalteHuebner\DataQueryBundle\FinderFactory\FinderFactoryInterface; +use MalteHuebner\DataQueryBundle\PaginatedResult\PaginatedResult; +use MalteHuebner\DataQueryBundle\Parameter\PageParameter; +use MalteHuebner\DataQueryBundle\Parameter\SizeParameter; use MalteHuebner\DataQueryBundle\RequestParameterList\RequestParameterList; class DataQueryManager implements DataQueryManagerInterface @@ -27,4 +30,28 @@ public function query(RequestParameterList $requestParameterList, string $entity return $finder->executeQuery($queryList, $parameterList); } + + #[\Override] + public function paginatedQuery(RequestParameterList $requestParameterList, string $entityFqcn): PaginatedResult + { + $queryList = $this->queryFactory->setEntityFqcn($entityFqcn)->createFromList($requestParameterList); + $parameterList = $this->parameterFactory->setEntityFqcn($entityFqcn)->createFromList($requestParameterList); + + $page = 0; + $size = 10; + + foreach ($parameterList as $parameter) { + if ($parameter instanceof PageParameter) { + $page = $parameter->getPage(); + } + + if ($parameter instanceof SizeParameter) { + $size = $parameter->getSize(); + } + } + + $finder = $this->finderFactory->createFinderForFqcn($entityFqcn); + + return $finder->executePaginatedQuery($queryList, $parameterList, $page, $size); + } } diff --git a/src/DataQueryManager/DataQueryManagerInterface.php b/src/DataQueryManager/DataQueryManagerInterface.php index b41801e..35cd935 100644 --- a/src/DataQueryManager/DataQueryManagerInterface.php +++ b/src/DataQueryManager/DataQueryManagerInterface.php @@ -2,9 +2,11 @@ namespace MalteHuebner\DataQueryBundle\DataQueryManager; +use MalteHuebner\DataQueryBundle\PaginatedResult\PaginatedResult; use MalteHuebner\DataQueryBundle\RequestParameterList\RequestParameterList; interface DataQueryManagerInterface { public function query(RequestParameterList $requestParameterList, string $entityFqcn): array; + public function paginatedQuery(RequestParameterList $requestParameterList, string $entityFqcn): PaginatedResult; } diff --git a/src/Finder/Finder.php b/src/Finder/Finder.php index 10c8f94..fa89099 100644 --- a/src/Finder/Finder.php +++ b/src/Finder/Finder.php @@ -3,7 +3,11 @@ namespace MalteHuebner\DataQueryBundle\Finder; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Tools\Pagination\Paginator; use FOS\ElasticaBundle\Repository; +use MalteHuebner\DataQueryBundle\PaginatedResult\PaginatedResult; +use MalteHuebner\DataQueryBundle\Parameter\FromParameter; +use MalteHuebner\DataQueryBundle\Parameter\PageParameter; use MalteHuebner\DataQueryBundle\Parameter\ParameterInterface; use MalteHuebner\DataQueryBundle\Parameter\SizeParameter; use MalteHuebner\DataQueryBundle\Query\ElasticQueryInterface; @@ -96,4 +100,82 @@ protected function executeOrmQuery(array $queryList, array $parameterList): arra return $qb->getQuery()->getResult(); } + + #[\Override] + public function executePaginatedQuery(array $queryList, array $parameterList, int $page, int $size): PaginatedResult + { + if ($this->entityManager) { + return $this->executePaginatedOrmQuery($queryList, $parameterList, $page, $size); + } + + if ($this->repository) { + return $this->executePaginatedElasticQuery($queryList, $parameterList, $page, $size); + } + + return new PaginatedResult([], $page, $size, 0); + } + + protected function executePaginatedElasticQuery(array $queryList, array $parameterList, int $page, int $size): PaginatedResult + { + $boolQuery = new \Elastica\Query\BoolQuery(); + + /** @var ElasticQueryInterface $query */ + foreach ($queryList as $query) { + if ($query instanceof QueryInterface) { + $boolQuery->addMust($query->createElasticQuery()); + } + } + + $query = new \Elastica\Query($boolQuery); + $query->setFrom($page * $size); + $query->setSize($size); + + /** @var ParameterInterface $parameter */ + foreach ($parameterList as $parameter) { + if ($parameter instanceof ParameterInterface) { + $query = $parameter->addToElasticQuery($query); + } + } + + $paginatorAdapter = $this->repository->createPaginatorAdapter($query); + $totalItems = $paginatorAdapter->getNbResults(); + $results = $paginatorAdapter->getSlice(0, $size)->toArray(); + + return new PaginatedResult($results, $page, $size, $totalItems); + } + + protected function executePaginatedOrmQuery(array $queryList, array $parameterList, int $page, int $size): PaginatedResult + { + $qb = $this->entityManager->createQueryBuilder() + ->select('e') + ->from($this->fqcn, 'e') + ; + + /** @var OrmQueryInterface $query */ + foreach ($queryList as $query) { + if ($query instanceof OrmQueryInterface) { + $qb = $query->createOrmQuery($qb); + } + } + + /** @var ParameterInterface $parameter */ + foreach ($parameterList as $parameter) { + if ($parameter instanceof SizeParameter || $parameter instanceof FromParameter || $parameter instanceof PageParameter) { + continue; + } + + if ($parameter instanceof ParameterInterface && method_exists($parameter, 'addToOrmQuery')) { + $parameter->addToOrmQuery($qb); + } + } + + $qb->setFirstResult($page * $size); + $qb->setMaxResults($size); + + $paginator = new Paginator($qb->getQuery()); + $totalItems = count($paginator); + $data = iterator_to_array($paginator); + + return new PaginatedResult($data, $page, $size, $totalItems); + } } diff --git a/src/Finder/FinderInterface.php b/src/Finder/FinderInterface.php index 6665b08..00bf72e 100644 --- a/src/Finder/FinderInterface.php +++ b/src/Finder/FinderInterface.php @@ -2,7 +2,10 @@ namespace MalteHuebner\DataQueryBundle\Finder; +use MalteHuebner\DataQueryBundle\PaginatedResult\PaginatedResult; + interface FinderInterface { public function executeQuery(array $queryList, array $parameterList): array; + public function executePaginatedQuery(array $queryList, array $parameterList, int $page, int $size): PaginatedResult; } diff --git a/src/PaginatedResult/PaginatedResult.php b/src/PaginatedResult/PaginatedResult.php new file mode 100644 index 0000000..59bfc52 --- /dev/null +++ b/src/PaginatedResult/PaginatedResult.php @@ -0,0 +1,44 @@ + */ + public function getData(): iterable + { + return $this->data; + } + + public function getPage(): int + { + return $this->page; + } + + public function getSize(): int + { + return $this->size; + } + + public function getTotalItems(): int + { + return $this->totalItems; + } + + public function getTotalPages(): int + { + if ($this->size === 0) { + return 0; + } + + return (int) ceil($this->totalItems / $this->size); + } +} diff --git a/src/Parameter/PageParameter.php b/src/Parameter/PageParameter.php new file mode 100644 index 0000000..6675236 --- /dev/null +++ b/src/Parameter/PageParameter.php @@ -0,0 +1,51 @@ +page = $page; + return $this; + } + + public function getPage(): int + { + return $this->page; + } + + public function setPageSize(int $size): PageParameter + { + $this->size = $size; + return $this; + } + + #[\Override] + public function addToElasticQuery(Query $query): Query + { + return $query->setFrom($this->page * $this->size); + } + + #[\Override] + public function addToOrmQuery(QueryBuilder $queryBuilder): AbstractOrmQuery + { + $queryBuilder->setFirstResult($this->page * $this->size); + + return $queryBuilder->getQuery(); + } +} diff --git a/src/Parameter/SizeParameter.php b/src/Parameter/SizeParameter.php index 19b9fed..dddd07d 100644 --- a/src/Parameter/SizeParameter.php +++ b/src/Parameter/SizeParameter.php @@ -23,6 +23,11 @@ public function setSize(int $size): SizeParameter return $this; } + public function getSize(): int + { + return $this->size; + } + #[\Override] public function addToElasticQuery(Query $query): Query {