Skip to content

Commit 3ce9e33

Browse files
committed
Added offset and limit to Criteria and QueryBuilder events
1 parent aa7ee06 commit 3ce9e33

File tree

7 files changed

+80
-16
lines changed

7 files changed

+80
-16
lines changed

docs/events.rst

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,19 @@ user, create a listener.
6565
}
6666
);
6767
68-
The ``QueryBuilder`` event has one function in addition to getters for
68+
Functions of the ``QueryBuilder`` event in addition to getters for
6969
all resolve parameters:
7070

7171
* ``getQueryBuilder`` - Will return a query builder with the user specified
7272
filters already applied.
73-
73+
* ``getOffset`` - Will return the offset for the query. The QueryBuilder passed
74+
to the event is not modified with the offset and limit yet. So if you have a
75+
large dataset and need to fetch it within the event, you may use this method
76+
to get the offset.
77+
* ``getLimit`` - Will return the limit for the query. The QueryBuilder passed
78+
to the event is not modified with the offset and limit yet. So if you have a
79+
large dataset and need to fetch it within the event, you may use this method
80+
to get the limit.
7481

7582
Criteria Event
7683
==============
@@ -148,6 +155,22 @@ all resolve parameters:
148155
if you need to fetch the collection to apply additional criteria.
149156
* ``setCollection`` - Will set the collection object. This is useful if you
150157
need to filter the collection directly.
158+
* ``getOffset`` - Will return the projected offset for the collection. The collection passed
159+
to the event is not modified with the offset and limit yet. So if you have a
160+
large dataset and need to fetch it within the event, you may use this method
161+
to get the expected offset.
162+
* ``getLimit`` - Will return the projectd limit for the collection. The collection passed
163+
to the event is not modified with the offset and limit yet. So if you have a
164+
large dataset and need to fetch it within the event, you may use this method
165+
to get the expected limit.
166+
167+
.. note::
168+
169+
The offset and limit is calculated before this event is fired and calculated
170+
again after the event. This is because the collection may be fetched and
171+
filtered before the limit is applied. The offset and limit are recalculated
172+
after the event is fired to ensure the correct data is returned.
173+
151174

152175
Modify an Entity Definition
153176
===========================

src/Event/Criteria.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public function __construct(
2525
protected readonly string $eventName,
2626
protected readonly DoctrineCriteria $criteria,
2727
protected Collection $collection,
28+
protected readonly int $offset,
29+
protected readonly int $limit,
2830
protected readonly mixed $objectValue,
2931
protected readonly array $args,
3032
protected readonly mixed $context,
@@ -54,6 +56,16 @@ public function setCollection(Collection $collection): void
5456
$this->collection = $collection;
5557
}
5658

59+
public function getOffset(): int
60+
{
61+
return $this->offset;
62+
}
63+
64+
public function getLimit(): int
65+
{
66+
return $this->limit;
67+
}
68+
5769
public function getObjectValue(): mixed
5870
{
5971
return $this->objectValue;

src/Event/QueryBuilder.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ class QueryBuilder implements
1616
{
1717
/** @param mixed[] $args */
1818
public function __construct(
19-
protected readonly DoctrineQueryBuilder $queryBuilder,
2019
protected readonly string $eventName,
20+
protected readonly DoctrineQueryBuilder $queryBuilder,
21+
protected readonly int $offset,
22+
protected readonly int $limit,
2123
protected readonly mixed $objectValue,
2224
protected readonly array $args,
2325
protected readonly mixed $context,
@@ -35,6 +37,16 @@ public function getQueryBuilder(): DoctrineQueryBuilder
3537
return $this->queryBuilder;
3638
}
3739

40+
public function getOffset(): int
41+
{
42+
return $this->offset;
43+
}
44+
45+
public function getLimit(): int
46+
{
47+
return $this->limit;
48+
}
49+
3850
public function getObjectValue(): mixed
3951
{
4052
return $this->objectValue;

src/Resolve/ResolveCollectionFactory.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ protected function buildPagination(
149149
$paginationFields[$field] = (int) base64_decode($value, true);
150150
}
151151

152+
// Calculate offset and limit
153+
$itemCount = count($collection->matching($criteria));
154+
$offsetAndLimit = $this->calculateOffsetAndLimit($resolve[3]->fieldName, $entityClassName, $targetClassName, $paginationFields, $itemCount);
155+
152156
/**
153157
* Fire the event dispatcher using the passed event name.
154158
*/
@@ -157,17 +161,20 @@ protected function buildPagination(
157161
$criteriaEventName,
158162
$criteria,
159163
$collection,
164+
$offsetAndLimit['offset'],
165+
$offsetAndLimit['limit'],
160166
...$resolve,
161167
);
162168

163169
$this->eventDispatcher->dispatch($event);
164170
$collection = $event->getCollection();
165171
}
166172

167-
$itemCount = count($collection->matching($criteria));
173+
// Recalculate offset and limit after Criteria event
174+
$itemCount = count($collection->matching($criteria));
175+
$offsetAndLimit = $this->calculateOffsetAndLimit($resolve[3]->fieldName, $entityClassName, $targetClassName, $paginationFields, $itemCount);
168176

169177
// Add offset and limit after Criteria event
170-
$offsetAndLimit = $this->calculateOffsetAndLimit($resolve[3]->fieldName, $entityClassName, $targetClassName, $paginationFields, $itemCount);
171178
if ($offsetAndLimit['offset']) {
172179
$criteria->setFirstResult($offsetAndLimit['offset']);
173180
}

src/Resolve/ResolveEntityFactory.php

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,28 +90,30 @@ public function buildPagination(
9090

9191
$offsetAndLimit = $this->calculateOffsetAndLimit($entity, $paginationFields);
9292

93-
if ($offsetAndLimit['offset']) {
94-
$queryBuilder->setFirstResult($offsetAndLimit['offset']);
95-
}
96-
97-
if ($offsetAndLimit['limit']) {
98-
$queryBuilder->setMaxResults($offsetAndLimit['limit']);
99-
}
100-
10193
/**
10294
* Fire the event dispatcher using the passed event name.
10395
* Include all resolve variables.
10496
*/
10597
if ($eventName) {
10698
$this->eventDispatcher->dispatch(
10799
new QueryBuilderEvent(
108-
$queryBuilder,
109100
$eventName,
101+
$queryBuilder,
102+
(int) $offsetAndLimit['offset'],
103+
(int) $offsetAndLimit['limit'],
110104
...$resolve,
111105
),
112106
);
113107
}
114108

109+
if ($offsetAndLimit['offset']) {
110+
$queryBuilder->setFirstResult($offsetAndLimit['offset']);
111+
}
112+
113+
if ($offsetAndLimit['limit']) {
114+
$queryBuilder->setMaxResults($offsetAndLimit['limit']);
115+
}
116+
115117
$edgesAndCursors = $this->buildEdgesAndCursors($queryBuilder, $offsetAndLimit, $paginationFields);
116118

117119
return [

test/Feature/Event/CriteriaTest.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,10 @@ function (CriteriaEvent $event): void {
9494

9595
public function testEventFilterCollection(): void
9696
{
97-
$driver = new Driver($this->getEntityManager(), new Config(['group' => 'CriteriaEvent']));
97+
$driver = new Driver($this->getEntityManager(), new Config([
98+
'group' => 'CriteriaEvent',
99+
'limit' => 25,
100+
]));
98101

99102
$driver->get(EventDispatcher::class)->subscribeTo(
100103
Artist::class . '.performances.criteria',
@@ -109,6 +112,8 @@ static function ($performance) {
109112
},
110113
));
111114

115+
$this->assertEquals(0, $event->getOffset());
116+
$this->assertEquals(25, $event->getLimit());
112117
$this->assertInstanceOf(Collection::class, $event->getCollection());
113118
$this->assertInstanceOf(Artist::class, $event->getObjectValue());
114119
$this->assertEquals('contextTest', $event->getContext());

test/Feature/Event/FilterQueryBuilderTest.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace ApiSkeletonsTest\Doctrine\ORM\GraphQL\Feature\Event;
66

7+
use ApiSkeletons\Doctrine\ORM\GraphQL\Config;
78
use ApiSkeletons\Doctrine\ORM\GraphQL\Driver;
89
use ApiSkeletons\Doctrine\ORM\GraphQL\Event\QueryBuilder as QueryBuilderEvent;
910
use ApiSkeletonsTest\Doctrine\ORM\GraphQL\AbstractTest;
@@ -18,11 +19,13 @@ class FilterQueryBuilderTest extends AbstractTest
1819
{
1920
public function testEvent(): void
2021
{
21-
$driver = new Driver($this->getEntityManager());
22+
$driver = new Driver($this->getEntityManager(), new Config(['limit' => 10]));
2223
$driver->get(EventDispatcher::class)->subscribeTo(
2324
'artist.querybuilder',
2425
function (QueryBuilderEvent $event): void {
2526
$this->assertInstanceOf(QueryBuilder::class, $event->getQueryBuilder());
27+
$this->assertEquals(0, $event->getOffset());
28+
$this->assertEquals(10, $event->getLimit());
2629
},
2730
);
2831

0 commit comments

Comments
 (0)