Skip to content

Commit 7e39512

Browse files
authored
Merge pull request #26 from maltehuebner/feature/paginated-query-support
Add paginated query support with metadata
2 parents d6fe344 + 7e9e293 commit 7e39512

9 files changed

Lines changed: 220 additions & 1 deletion

File tree

config/services.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ services:
55
public: true
66

77
MalteHuebner\DataQueryBundle\:
8-
resource: '../src/{DataQueryManager,Factory,FieldList,FinderFactory,Manager,Parameter,Query,RequestParameterList,Validator}'
8+
resource: '../src/{DataQueryManager,Factory,FieldList,FinderFactory,Manager,PaginatedResult,Parameter,Query,RequestParameterList,Validator}'
99
exclude: '../src/{DependencyInjection,Factory/ConflictResolver,Factory/ValueAssigner/ValueType.php,RequestParameterList/ArrayToListConverter.php,RequestParameterList/QueryStringToListConverter.php,RequestParameterList/RequestToListConverter.php,tests,MalteHuebnerDataQueryBundle}'
1010

1111
Psr\Container\ContainerInterface:

phpstan-baseline.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ parameters:
5555
count: 2
5656
path: src/FieldList/QueryFieldList/QueryFieldListFactory.php
5757

58+
-
59+
message: "#^Call to method createPaginatorAdapter\\(\\) on an unknown class FOS\\\\ElasticaBundle\\\\Repository\\.$#"
60+
count: 1
61+
path: src/Finder/Finder.php
62+
5863
-
5964
message: "#^Call to method find\\(\\) on an unknown class FOS\\\\ElasticaBundle\\\\Repository\\.$#"
6065
count: 1

src/DataQueryManager/DataQueryManager.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
use MalteHuebner\DataQueryBundle\Factory\ParameterFactory\ParameterFactoryInterface;
66
use MalteHuebner\DataQueryBundle\Factory\QueryFactory\QueryFactoryInterface;
77
use MalteHuebner\DataQueryBundle\FinderFactory\FinderFactoryInterface;
8+
use MalteHuebner\DataQueryBundle\PaginatedResult\PaginatedResult;
9+
use MalteHuebner\DataQueryBundle\Parameter\PageParameter;
10+
use MalteHuebner\DataQueryBundle\Parameter\SizeParameter;
811
use MalteHuebner\DataQueryBundle\RequestParameterList\RequestParameterList;
912

1013
class DataQueryManager implements DataQueryManagerInterface
@@ -27,4 +30,28 @@ public function query(RequestParameterList $requestParameterList, string $entity
2730

2831
return $finder->executeQuery($queryList, $parameterList);
2932
}
33+
34+
#[\Override]
35+
public function paginatedQuery(RequestParameterList $requestParameterList, string $entityFqcn): PaginatedResult
36+
{
37+
$queryList = $this->queryFactory->setEntityFqcn($entityFqcn)->createFromList($requestParameterList);
38+
$parameterList = $this->parameterFactory->setEntityFqcn($entityFqcn)->createFromList($requestParameterList);
39+
40+
$page = 0;
41+
$size = 10;
42+
43+
foreach ($parameterList as $parameter) {
44+
if ($parameter instanceof PageParameter) {
45+
$page = $parameter->getPage();
46+
}
47+
48+
if ($parameter instanceof SizeParameter) {
49+
$size = $parameter->getSize();
50+
}
51+
}
52+
53+
$finder = $this->finderFactory->createFinderForFqcn($entityFqcn);
54+
55+
return $finder->executePaginatedQuery($queryList, $parameterList, $page, $size);
56+
}
3057
}

src/DataQueryManager/DataQueryManagerInterface.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
namespace MalteHuebner\DataQueryBundle\DataQueryManager;
44

5+
use MalteHuebner\DataQueryBundle\PaginatedResult\PaginatedResult;
56
use MalteHuebner\DataQueryBundle\RequestParameterList\RequestParameterList;
67

78
interface DataQueryManagerInterface
89
{
910
public function query(RequestParameterList $requestParameterList, string $entityFqcn): array;
11+
public function paginatedQuery(RequestParameterList $requestParameterList, string $entityFqcn): PaginatedResult;
1012
}

src/Finder/Finder.php

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
namespace MalteHuebner\DataQueryBundle\Finder;
44

55
use Doctrine\ORM\EntityManagerInterface;
6+
use Doctrine\ORM\Tools\Pagination\Paginator;
67
use FOS\ElasticaBundle\Repository;
8+
use MalteHuebner\DataQueryBundle\PaginatedResult\PaginatedResult;
9+
use MalteHuebner\DataQueryBundle\Parameter\FromParameter;
10+
use MalteHuebner\DataQueryBundle\Parameter\PageParameter;
711
use MalteHuebner\DataQueryBundle\Parameter\ParameterInterface;
812
use MalteHuebner\DataQueryBundle\Parameter\SizeParameter;
913
use MalteHuebner\DataQueryBundle\Query\ElasticQueryInterface;
@@ -96,4 +100,82 @@ protected function executeOrmQuery(array $queryList, array $parameterList): arra
96100

97101
return $qb->getQuery()->getResult();
98102
}
103+
104+
#[\Override]
105+
public function executePaginatedQuery(array $queryList, array $parameterList, int $page, int $size): PaginatedResult
106+
{
107+
if ($this->entityManager) {
108+
return $this->executePaginatedOrmQuery($queryList, $parameterList, $page, $size);
109+
}
110+
111+
if ($this->repository) {
112+
return $this->executePaginatedElasticQuery($queryList, $parameterList, $page, $size);
113+
}
114+
115+
return new PaginatedResult([], $page, $size, 0);
116+
}
117+
118+
protected function executePaginatedElasticQuery(array $queryList, array $parameterList, int $page, int $size): PaginatedResult
119+
{
120+
$boolQuery = new \Elastica\Query\BoolQuery();
121+
122+
/** @var ElasticQueryInterface $query */
123+
foreach ($queryList as $query) {
124+
if ($query instanceof QueryInterface) {
125+
$boolQuery->addMust($query->createElasticQuery());
126+
}
127+
}
128+
129+
$query = new \Elastica\Query($boolQuery);
130+
$query->setFrom($page * $size);
131+
$query->setSize($size);
132+
133+
/** @var ParameterInterface $parameter */
134+
foreach ($parameterList as $parameter) {
135+
if ($parameter instanceof ParameterInterface) {
136+
$query = $parameter->addToElasticQuery($query);
137+
}
138+
}
139+
140+
$paginatorAdapter = $this->repository->createPaginatorAdapter($query);
141+
$totalItems = $paginatorAdapter->getNbResults();
142+
$results = $paginatorAdapter->getSlice(0, $size)->toArray();
143+
144+
return new PaginatedResult($results, $page, $size, $totalItems);
145+
}
146+
147+
protected function executePaginatedOrmQuery(array $queryList, array $parameterList, int $page, int $size): PaginatedResult
148+
{
149+
$qb = $this->entityManager->createQueryBuilder()
150+
->select('e')
151+
->from($this->fqcn, 'e')
152+
;
153+
154+
/** @var OrmQueryInterface $query */
155+
foreach ($queryList as $query) {
156+
if ($query instanceof OrmQueryInterface) {
157+
$qb = $query->createOrmQuery($qb);
158+
}
159+
}
160+
161+
/** @var ParameterInterface $parameter */
162+
foreach ($parameterList as $parameter) {
163+
if ($parameter instanceof SizeParameter || $parameter instanceof FromParameter || $parameter instanceof PageParameter) {
164+
continue;
165+
}
166+
167+
if ($parameter instanceof ParameterInterface && method_exists($parameter, 'addToOrmQuery')) {
168+
$parameter->addToOrmQuery($qb);
169+
}
170+
}
171+
172+
$qb->setFirstResult($page * $size);
173+
$qb->setMaxResults($size);
174+
175+
$paginator = new Paginator($qb->getQuery());
176+
$totalItems = count($paginator);
177+
$data = iterator_to_array($paginator);
178+
179+
return new PaginatedResult($data, $page, $size, $totalItems);
180+
}
99181
}

src/Finder/FinderInterface.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
namespace MalteHuebner\DataQueryBundle\Finder;
44

5+
use MalteHuebner\DataQueryBundle\PaginatedResult\PaginatedResult;
6+
57
interface FinderInterface
68
{
79
public function executeQuery(array $queryList, array $parameterList): array;
10+
public function executePaginatedQuery(array $queryList, array $parameterList, int $page, int $size): PaginatedResult;
811
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MalteHuebner\DataQueryBundle\PaginatedResult;
4+
5+
class PaginatedResult
6+
{
7+
public function __construct(
8+
private readonly iterable $data,
9+
private readonly int $page,
10+
private readonly int $size,
11+
private readonly int $totalItems,
12+
) {
13+
}
14+
15+
/** @return iterable<object> */
16+
public function getData(): iterable
17+
{
18+
return $this->data;
19+
}
20+
21+
public function getPage(): int
22+
{
23+
return $this->page;
24+
}
25+
26+
public function getSize(): int
27+
{
28+
return $this->size;
29+
}
30+
31+
public function getTotalItems(): int
32+
{
33+
return $this->totalItems;
34+
}
35+
36+
public function getTotalPages(): int
37+
{
38+
if ($this->size === 0) {
39+
return 0;
40+
}
41+
42+
return (int) ceil($this->totalItems / $this->size);
43+
}
44+
}

src/Parameter/PageParameter.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace MalteHuebner\DataQueryBundle\Parameter;
4+
5+
use MalteHuebner\DataQueryBundle\Attribute\ParameterAttribute as DataQuery;
6+
use Doctrine\ORM\AbstractQuery as AbstractOrmQuery;
7+
use Doctrine\ORM\QueryBuilder;
8+
use Elastica\Query;
9+
use Symfony\Component\Validator\Constraints as Constraints;
10+
11+
class PageParameter extends AbstractParameter
12+
{
13+
#[Constraints\NotNull]
14+
#[Constraints\Type('int')]
15+
#[Constraints\Range(min: 0)]
16+
private int $page;
17+
18+
private int $size = 10;
19+
20+
#[DataQuery\RequiredParameter(parameterName: 'page')]
21+
public function setPage(int $page): PageParameter
22+
{
23+
$this->page = $page;
24+
return $this;
25+
}
26+
27+
public function getPage(): int
28+
{
29+
return $this->page;
30+
}
31+
32+
public function setPageSize(int $size): PageParameter
33+
{
34+
$this->size = $size;
35+
return $this;
36+
}
37+
38+
#[\Override]
39+
public function addToElasticQuery(Query $query): Query
40+
{
41+
return $query->setFrom($this->page * $this->size);
42+
}
43+
44+
#[\Override]
45+
public function addToOrmQuery(QueryBuilder $queryBuilder): AbstractOrmQuery
46+
{
47+
$queryBuilder->setFirstResult($this->page * $this->size);
48+
49+
return $queryBuilder->getQuery();
50+
}
51+
}

src/Parameter/SizeParameter.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ public function setSize(int $size): SizeParameter
2323
return $this;
2424
}
2525

26+
public function getSize(): int
27+
{
28+
return $this->size;
29+
}
30+
2631
#[\Override]
2732
public function addToElasticQuery(Query $query): Query
2833
{

0 commit comments

Comments
 (0)