Skip to content

Commit ddbec0b

Browse files
committed
Allow passing itemsPerPage=0
1 parent bd9aea7 commit ddbec0b

File tree

4 files changed

+97
-9
lines changed

4 files changed

+97
-9
lines changed

features/hydra/collection.feature

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,3 +351,45 @@ Feature: Collections support
351351
"additionalProperties": false
352352
}
353353
"""
354+
355+
@dropSchema
356+
@createSchema
357+
Scenario: Allow passing "0" to `itemsPerPage`
358+
When I send a "GET" request to "/dummies?itemsPerPage=0"
359+
Then the response status code should be 200
360+
And the response should be in JSON
361+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
362+
And the JSON should be valid according to this schema:
363+
"""
364+
{
365+
"type": "object",
366+
"properties": {
367+
"@context": {"pattern": "^/contexts/Dummy$"},
368+
"@id": {"pattern": "^/dummies$"},
369+
"@type": {"pattern": "^hydra:Collection$"},
370+
"hydra:totalItems": {"type":"number", "maximum": 30},
371+
"hydra:member": {
372+
"type": "array",
373+
"minItems": 0,
374+
"maxItems": 0
375+
},
376+
"hydra:view": {
377+
"type": "object",
378+
"properties": {
379+
"@id": {"pattern": "^/dummies\\?itemsPerPage=0$"},
380+
"@type": {"pattern": "^hydra:PartialCollectionView$"},
381+
"hydra:first": {"pattern": "^/dummies\\?itemsPerPage=0&page=1$"},
382+
"hydra:last": {"pattern": "^/dummies\\?itemsPerPage=0&page=1$"},
383+
"hydra:previous": {"pattern": "^/dummies\\?itemsPerPage=0&page=1$"},
384+
"hydra:next": {"pattern": "^/dummies\\?itemsPerPage=0&page=1$"}
385+
}
386+
},
387+
"hydra:search": {}
388+
},
389+
"additionalProperties": false
390+
}
391+
"""
392+
393+
When I send a "GET" request to "/dummies?itemsPerPage=0&page=2"
394+
Then the response status code should be 400
395+
And the JSON node "hydra:description" should be equal to "Page should not be greater than 1 if itemsPegPage is equal to 0"

src/Bridge/Doctrine/Orm/Extension/PaginationExtension.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,18 @@ public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGenerator
8181
$itemsPerPage = (null !== $this->maximumItemPerPage && $itemsPerPage >= $this->maximumItemPerPage ? $this->maximumItemPerPage : $itemsPerPage);
8282
}
8383

84-
if (0 >= $itemsPerPage) {
85-
throw new InvalidArgumentException('Item per page parameter should not be less than or equal to 0');
84+
if (0 > $itemsPerPage) {
85+
throw new InvalidArgumentException('Item per page parameter should not be less than 0');
86+
}
87+
88+
$page = $request->query->get($this->pageParameterName, 1);
89+
90+
if (0 === $itemsPerPage && 1 < $page) {
91+
throw new InvalidArgumentException('Page should not be greater than 1 if itemsPegPage is equal to 0');
8692
}
8793

8894
$queryBuilder
89-
->setFirstResult(($request->query->get($this->pageParameterName, 1) - 1) * $itemsPerPage)
95+
->setFirstResult(($page - 1) * $itemsPerPage)
9096
->setMaxResults($itemsPerPage);
9197
}
9298

src/Bridge/Doctrine/Orm/Paginator.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ public function __construct(DoctrineOrmPaginator $paginator)
5959
*/
6060
public function getCurrentPage(): float
6161
{
62+
if (0 >= $this->maxResults) {
63+
return 1;
64+
}
65+
6266
return floor($this->firstResult / $this->maxResults) + 1.;
6367
}
6468

@@ -67,6 +71,10 @@ public function getCurrentPage(): float
6771
*/
6872
public function getLastPage(): float
6973
{
74+
if (0 >= $this->maxResults) {
75+
return 1;
76+
}
77+
7078
return ceil($this->totalItems / $this->maxResults) ?: 1.;
7179
}
7280

tests/Bridge/Doctrine/Orm/Extension/PaginationExtensionTest.php

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,43 @@ public function testApplyToCollection()
6262
$extension->applyToCollection($queryBuilder, new QueryNameGenerator(), 'Foo', 'op');
6363
}
6464

65+
public function testApplyToCollectionWithItemPerPageZero()
66+
{
67+
$requestStack = new RequestStack();
68+
$requestStack->push(new Request(['pagination' => true, 'itemsPerPage' => 0, '_page' => 1]));
69+
70+
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
71+
$attributes = [
72+
'pagination_enabled' => true,
73+
'pagination_client_enabled' => true,
74+
'pagination_items_per_page' => 0,
75+
];
76+
$resourceMetadataFactoryProphecy->create('Foo')->willReturn(new ResourceMetadata(null, null, null, [], [], $attributes))->shouldBeCalled();
77+
$resourceMetadataFactory = $resourceMetadataFactoryProphecy->reveal();
78+
79+
$queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
80+
$queryBuilderProphecy->setFirstResult(0)->willReturn($queryBuilderProphecy)->shouldBeCalled();
81+
$queryBuilderProphecy->setMaxResults(0)->shouldBeCalled();
82+
$queryBuilder = $queryBuilderProphecy->reveal();
83+
84+
$extension = new PaginationExtension(
85+
$this->prophesize(ManagerRegistry::class)->reveal(),
86+
$requestStack,
87+
$resourceMetadataFactory,
88+
true,
89+
false,
90+
false,
91+
0,
92+
'_page'
93+
);
94+
$extension->applyToCollection($queryBuilder, new QueryNameGenerator(), 'Foo', 'op');
95+
}
96+
6597
/**
6698
* @expectedException \ApiPlatform\Core\Exception\InvalidArgumentException
67-
* @expectedExceptionMessage Item per page parameter should not be less than or equal to 0
99+
* @expectedExceptionMessage Page should not be greater than 1 if itemsPegPage is equal to 0
68100
*/
69-
public function testApplyToCollectionWithItemPerPageZero()
101+
public function testApplyToCollectionWithItemPerPageZeroAndPage2()
70102
{
71103
$requestStack = new RequestStack();
72104
$requestStack->push(new Request(['pagination' => true, 'itemsPerPage' => 0, '_page' => 2]));
@@ -81,8 +113,8 @@ public function testApplyToCollectionWithItemPerPageZero()
81113
$resourceMetadataFactory = $resourceMetadataFactoryProphecy->reveal();
82114

83115
$queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
84-
$queryBuilderProphecy->setFirstResult(40)->willReturn($queryBuilderProphecy)->shouldNotBeCalled();
85-
$queryBuilderProphecy->setMaxResults(40)->shouldNotBeCalled();
116+
$queryBuilderProphecy->setFirstResult(0)->willReturn($queryBuilderProphecy)->shouldNotBeCalled();
117+
$queryBuilderProphecy->setMaxResults(0)->shouldNotBeCalled();
86118
$queryBuilder = $queryBuilderProphecy->reveal();
87119

88120
$extension = new PaginationExtension(
@@ -100,7 +132,7 @@ public function testApplyToCollectionWithItemPerPageZero()
100132

101133
/**
102134
* @expectedException \ApiPlatform\Core\Exception\InvalidArgumentException
103-
* @expectedExceptionMessage Item per page parameter should not be less than or equal to 0
135+
* @expectedExceptionMessage Item per page parameter should not be less than 0
104136
*/
105137
public function testApplyToCollectionWithItemPerPageLessThen0()
106138
{
@@ -111,7 +143,7 @@ public function testApplyToCollectionWithItemPerPageLessThen0()
111143
$attributes = [
112144
'pagination_enabled' => true,
113145
'pagination_client_enabled' => true,
114-
'pagination_items_per_page' => 0,
146+
'pagination_items_per_page' => -20,
115147
];
116148
$resourceMetadataFactoryProphecy->create('Foo')->willReturn(new ResourceMetadata(null, null, null, [], [], $attributes))->shouldBeCalled();
117149
$resourceMetadataFactory = $resourceMetadataFactoryProphecy->reveal();

0 commit comments

Comments
 (0)