Skip to content

Commit b5f3cf3

Browse files
committed
[FEATURE] Support JSON API friendly parameters (#924)
And have better JSON API compliance.
1 parent a4c5784 commit b5f3cf3

12 files changed

+125
-50
lines changed

src/Http/Request.php

+20
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,26 @@ public function getParsedInput() {
257257
return $this->parsedInput;
258258
}
259259

260+
/**
261+
* {@inheritdoc}
262+
*/
263+
public function getPagerInput() {
264+
$input = $this->getParsedInput();
265+
if (!isset($input['page'])) {
266+
$page = array('number' => 1);
267+
}
268+
else {
269+
$page = $input['page'];
270+
if (!is_array($page)) {
271+
$page = array('number' => $page);
272+
}
273+
}
274+
if (isset($input['range'])) {
275+
$page['size'] = $input['range'];
276+
}
277+
return $page + array('number' => 1);
278+
}
279+
260280
/**
261281
* {@inheritdoc}
262282
*/

src/Http/RequestInterface.php

+12
Original file line numberDiff line numberDiff line change
@@ -273,4 +273,16 @@ public function setViaRouter($via_router);
273273
*/
274274
public function getCookies();
275275

276+
/**
277+
* Get the normalized pager input.
278+
*
279+
* This is to support page=1&range=6 and page[number]=1&page[size]=6 at the
280+
* same time.
281+
*
282+
* @return array
283+
* An associative array with the pager information in the form of
284+
* page[number]=1&page[size]=6.
285+
*/
286+
public function getPagerInput();
287+
276288
}

src/Plugin/formatter/Formatter.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,8 @@ protected static function unprefixInputOptions($allowed_fields, $prefix) {
327327
protected function calculateItemsPerPage(ResourceInterface $resource) {
328328
$data_provider = $resource->getDataProvider();
329329
$max_range = $data_provider->getRange();
330-
$original_input = $resource->getRequest()->getParsedInput();
331-
$items_per_page = empty($original_input['range']) ? $max_range : $original_input['range'];
330+
$original_input = $resource->getRequest()->getPagerInput();
331+
$items_per_page = empty($original_input['size']) ? $max_range : $original_input['size'];
332332
return $items_per_page > $max_range ? $max_range : $items_per_page;
333333
}
334334

src/Plugin/formatter/FormatterHalJson.php

+4-4
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,11 @@ protected function addHateoas(array &$data) {
120120
'href' => $resource->versionedUrl($resource->getPath()),
121121
);
122122

123-
$input = $request->getParsedInput();
124-
$page = !empty($input['page']) ? $input['page'] : 1;
123+
$input = $request->getPagerInput();
124+
$page = $input['number'];
125125

126126
if ($page > 1) {
127-
$input['page'] = $page - 1;
127+
$input['number'] = $page - 1;
128128
$data['_links']['previous'] = array(
129129
'title' => 'Previous',
130130
'href' => $resource->getUrl(),
@@ -140,7 +140,7 @@ protected function addHateoas(array &$data) {
140140
$items_per_page = $this->calculateItemsPerPage($resource);
141141
$previous_items = ($page - 1) * $items_per_page;
142142
if (isset($data['count']) && $data['count'] > $listed_items + $previous_items) {
143-
$input['page'] = $page + 1;
143+
$input['number'] = $page + 1;
144144
$data['_links']['next'] = array(
145145
'title' => 'Next',
146146
'href' => $resource->getUrl(),

src/Plugin/formatter/FormatterJson.php

+8-3
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,14 @@ protected function addHateoas(array &$data) {
159159
);
160160

161161
$input = $request->getParsedInput();
162-
$page = !empty($input['page']) ? $input['page'] : 1;
162+
unset($input['page']);
163+
unset($input['range']);
164+
$input['page'] = $request->getPagerInput();
165+
$page = $input['page']['number'];
163166

164167
if ($page > 1) {
165-
$query = array('page' => $page - 1) + $input;
168+
$query = $input;
169+
$query['page']['number'] = $page - 1;
166170
$data['previous'] = array(
167171
'title' => 'Previous',
168172
'href' => $resource->versionedUrl('', array('query' => $query), TRUE),
@@ -175,7 +179,8 @@ protected function addHateoas(array &$data) {
175179
$items_per_page = $this->calculateItemsPerPage($resource);
176180
$previous_items = ($page - 1) * $items_per_page;
177181
if (isset($data['count']) && $data['count'] > count($data['data']) + $previous_items) {
178-
$query = array('page' => $page + 1) + $input;
182+
$query = $input;
183+
$query['page']['number'] = $page + 1;
179184
$data['next'] = array(
180185
'title' => 'Next',
181186
'href' => $resource->versionedUrl('', array('query' => $query), TRUE),

src/Plugin/formatter/FormatterJsonApi.php

+11-5
Original file line numberDiff line numberDiff line change
@@ -193,34 +193,40 @@ protected function addHateoas(array &$data, ResourceInterface $resource = NULL,
193193
}
194194

195195
$input = $original_input = $request->getParsedInput();
196+
unset($input['page']);
197+
unset($input['range']);
198+
unset($original_input['page']);
199+
unset($original_input['range']);
200+
$input['page'] = $request->getPagerInput();
201+
$original_input['page'] = $request->getPagerInput();
196202

197203
// Get self link.
198204
$options = $top_level ? array('query' => $input) : array();
199205
$data['links']['self'] = $resource->versionedUrl($path, $options);
200206

201-
$page = !empty($input['page']) ? $input['page'] : 1;
207+
$page = $input['page']['number'];
202208

203209
// We know that there are more pages if the total count is bigger than the
204210
// number of items of the current request plus the number of items in
205211
// previous pages.
206212
$items_per_page = $this->calculateItemsPerPage($resource);
207213
if (isset($data['meta']['count']) && $data['meta']['count'] > $items_per_page) {
208214
$num_pages = ceil($data['meta']['count'] / $items_per_page);
209-
unset($input['page']);
215+
unset($input['page']['number']);
210216
$data['links']['first'] = $resource->getUrl(array('query' => $input), FALSE);
211217

212218
if ($page > 1) {
213219
$input = $original_input;
214-
$input['page'] = $page - 1;
220+
$input['page']['number'] = $page - 1;
215221
$data['links']['previous'] = $resource->getUrl(array('query' => $input), FALSE);
216222
}
217223
if ($num_pages > 1) {
218224
$input = $original_input;
219-
$input['page'] = $num_pages;
225+
$input['page']['number'] = $num_pages;
220226
$data['links']['last'] = $resource->getUrl(array('query' => $input), FALSE);
221227
if ($page != $num_pages) {
222228
$input = $original_input;
223-
$input['page'] = $page + 1;
229+
$input['page']['number'] = $page + 1;
224230
$data['links']['next'] = $resource->getUrl(array('query' => $input), FALSE);
225231
}
226232
}

src/Plugin/resource/DataProvider/DataProvider.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -416,14 +416,14 @@ protected function parseRequestForListFilter() {
416416
* @throws UnprocessableEntityException
417417
*/
418418
protected function parseRequestForListPagination() {
419-
$input = $this->getRequest()->getParsedInput();
419+
$pager_input = $this->getRequest()->getPagerInput();
420420

421-
$page = isset($input['page']) ? (int) $input['page'] : 1;
421+
$page = $pager_input['number'];
422422
if (!ctype_digit((string) $page) || $page < 1) {
423423
throw new BadRequestException('"Page" property should be numeric and equal or higher than 1.');
424424
}
425425

426-
$range = isset($input['range']) ? (int) $input['range'] : $this->getRange();
426+
$range = isset($pager_input['size']) ? (int) $pager_input['size'] : $this->getRange();
427427
$range = $range > $this->getRange() ? $this->getRange() : $range;
428428
if (!ctype_digit((string) $range) || $range < 1) {
429429
throw new BadRequestException('"Range" property should be numeric and equal or higher than 1.');

src/Plugin/resource/Field/ResourceFieldEntity.php

-1
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,6 @@ protected function nestedDottedChildren($key) {
570570
* The processed filters.
571571
*/
572572
protected function nestedDottedFilters() {
573-
574573
$input = $this
575574
->getRequest()
576575
->getParsedInput();

src/Plugin/resource/Resource.php

+5-2
Original file line numberDiff line numberDiff line change
@@ -510,15 +510,18 @@ public function versionedUrl($path = '', $options = array(), $version_string = T
510510
* {@inheritdoc}
511511
*/
512512
public function getUrl(array $options = array(), $keep_query = TRUE, RequestInterface $request = NULL) {
513-
$request = $request ?: $this->getRequest();
514-
$input = $request->getParsedInput();
515513
// By default set URL to be absolute.
516514
$options += array(
517515
'absolute' => TRUE,
518516
'query' => array(),
519517
);
520518

521519
if ($keep_query) {
520+
$request = $request ?: $this->getRequest();
521+
$input = $request->getParsedInput();
522+
unset($input['page']);
523+
unset($input['range']);
524+
$input['page'] = $request->getPagerInput();
522525
// Remove special params.
523526
unset($input['q']);
524527

tests/RestfulJsonApiTestCase.test

+54-26
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
* Contains \RestfulJsonApiTestCase.
66
*/
77

8-
use Drupal\restful\Exception\UnprocessableEntityException;
98
use Drupal\restful\Http\Request;
109

1110
/**
@@ -144,7 +143,9 @@ class RestfulJsonApiTestCase extends RestfulCurlBaseTestCase {
144143
$expected['text_multiple'][] = $text_multiple_wrapper->value();
145144
}
146145
$this->assertEqual(array_filter($expected), array_filter($attributes));
147-
$this->assertEqual($this->handler->versionedUrl($wrapper->getIdentifier()), $result['links']['self']);
146+
$this->assertEqual($this->handler->versionedUrl($wrapper->getIdentifier(), array(
147+
'query' => array('page' => array('number' => 1)),
148+
)), $result['links']['self']);
148149

149150
// 2. Assert the relationships.
150151
$relationships = $result['data']['relationships'];
@@ -470,18 +471,29 @@ class RestfulJsonApiTestCase extends RestfulCurlBaseTestCase {
470471
$result = drupal_json_decode($this->formatter->format($resource_field_collections));
471472
$links = $result['links'];
472473
$expected_links = array(
473-
'self' => $this->handler->versionedUrl('', array('query' => array('range' => $range))),
474-
'first' => $this->handler->versionedUrl('', array('query' => array('range' => $range))),
474+
'self' => $this->handler->versionedUrl('', array(
475+
'query' => array(
476+
'page' => array(
477+
'number' => 1,
478+
'size' => $range,
479+
),
480+
),
481+
)),
482+
'first' => $this->handler->versionedUrl('', array('query' => array('page' => array('size' => $range)))),
475483
'last' => $this->handler->versionedUrl('', array(
476484
'query' => array(
477-
'range' => $range,
478-
'page' => 5,
485+
'page' => array(
486+
'number' => 5,
487+
'size' => $range,
488+
),
479489
),
480490
)),
481491
'next' => $this->handler->versionedUrl('', array(
482492
'query' => array(
483-
'range' => $range,
484-
'page' => 2,
493+
'page' => array(
494+
'number' => 2,
495+
'size' => $range,
496+
),
485497
),
486498
)),
487499
);
@@ -501,27 +513,35 @@ class RestfulJsonApiTestCase extends RestfulCurlBaseTestCase {
501513
$expected_links = array(
502514
'self' => $this->handler->versionedUrl('', array(
503515
'query' => array(
504-
'range' => $range,
505-
'page' => 2,
516+
'page' => array(
517+
'number' => 2,
518+
'size' => $range,
519+
),
506520
),
507521
)),
508-
'first' => $this->handler->versionedUrl('', array('query' => array('range' => $range))),
522+
'first' => $this->handler->versionedUrl('', array('query' => array('page' => array('size' => $range)))),
509523
'previous' => $this->handler->versionedUrl('', array(
510524
'query' => array(
511-
'range' => $range,
512-
'page' => 1,
525+
'page' => array(
526+
'number' => 1,
527+
'size' => $range,
528+
),
513529
),
514530
)),
515531
'last' => $this->handler->versionedUrl('', array(
516532
'query' => array(
517-
'range' => $range,
518-
'page' => 5,
533+
'page' => array(
534+
'number' => 5,
535+
'size' => $range,
536+
),
519537
),
520538
)),
521539
'next' => $this->handler->versionedUrl('', array(
522540
'query' => array(
523-
'range' => $range,
524-
'page' => 3,
541+
'page' => array(
542+
'number' => 3,
543+
'size' => $range,
544+
),
525545
),
526546
)),
527547
);
@@ -530,8 +550,10 @@ class RestfulJsonApiTestCase extends RestfulCurlBaseTestCase {
530550
// 3. Test pagination links in the last page.
531551
$range = 2;
532552
$this->handler->setRequest(Request::create('', array(
533-
'range' => $range,
534-
'page' => 5,
553+
'page' => array(
554+
'size' => $range,
555+
'number' => 5,
556+
),
535557
)));
536558
$this->handler->setPath('');
537559
$this->formatter->setResource($this->handler);
@@ -541,21 +563,27 @@ class RestfulJsonApiTestCase extends RestfulCurlBaseTestCase {
541563
$expected_links = array(
542564
'self' => $this->handler->versionedUrl('', array(
543565
'query' => array(
544-
'range' => $range,
545-
'page' => 5,
566+
'page' => array(
567+
'size' => $range,
568+
'number' => 5,
569+
),
546570
),
547571
)),
548-
'first' => $this->handler->versionedUrl('', array('query' => array('range' => $range))),
572+
'first' => $this->handler->versionedUrl('', array('query' => array('page' => array('size' => $range)))),
549573
'previous' => $this->handler->versionedUrl('', array(
550574
'query' => array(
551-
'range' => $range,
552-
'page' => 4,
575+
'page' => array(
576+
'size' => $range,
577+
'number' => 4,
578+
),
553579
),
554580
)),
555581
'last' => $this->handler->versionedUrl('', array(
556582
'query' => array(
557-
'range' => $range,
558-
'page' => 5,
583+
'page' => array(
584+
'size' => $range,
585+
'number' => 5,
586+
),
559587
),
560588
)),
561589
);

tests/RestfulListTestCase.test

+2-2
Original file line numberDiff line numberDiff line change
@@ -690,11 +690,11 @@ class RestfulListTestCase extends RestfulCurlBaseTestCase {
690690
$result = drupal_json_decode($formatter_handler->format($handler->process()));
691691
$this->assertTrue($result['next'], '"Next" link exists on the middle page.');
692692
$this->assertEqual($result['next']['href'], $handler->versionedUrl('', array(
693-
'query' => array('page' => 3),
693+
'query' => array('page' => array('number' => 3)),
694694
)));
695695
$this->assertTrue($result['previous'], '"Previous" link exists on the middle page.');
696696
$this->assertEqual($result['previous']['href'], $handler->versionedUrl('', array(
697-
'query' => array('page' => 1),
697+
'query' => array('page' => array('number' => 1)),
698698
)));
699699

700700
// Check pagination of last page.

tests/RestfulSimpleJsonTestCase.test

+4-2
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,10 @@ class RestfulSimpleJsonTestCase extends RestfulCurlBaseTestCase {
103103
// Test the max cap for the range.
104104
$handler->getDataProvider()->setRange(1);
105105
$handler->setRequest(Request::create('/api/v1.5/articles', array(
106-
'range' => 1000,
107-
'page' => 2,
106+
'page' => array(
107+
'size' => 1000,
108+
'number' => 2,
109+
),
108110
)));
109111
$handler->setPath('');
110112
$results = $handler->process();

0 commit comments

Comments
 (0)