Skip to content

Commit 404e3e1

Browse files
authored
Fix query args preservation in collection pagination links (#2120)
1 parent d7b8404 commit 404e3e1

File tree

3 files changed

+104
-18
lines changed

3 files changed

+104
-18
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: patch
2+
Type: fixed
3+
4+
Fix query args preservation in collection pagination links

includes/rest/trait-collection.php

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ trait Collection {
2727
*/
2828
public function prepare_collection_response( $response, $request ) {
2929
$page = $request->get_param( 'page' );
30-
$per_page = $request->get_param( 'per_page' );
31-
$max_pages = \ceil( $response['totalItems'] / $per_page );
30+
$max_pages = \ceil( $response['totalItems'] / $request->get_param( 'per_page' ) );
3231

3332
if ( $page > $max_pages ) {
3433
return new \WP_Error(
@@ -43,6 +42,7 @@ public function prepare_collection_response( $response, $request ) {
4342
return $response;
4443
}
4544

45+
$response['id'] = \add_query_arg( $request->get_query_params(), $response['id'] );
4646
$response['first'] = \add_query_arg( 'page', 1, $response['id'] );
4747
$response['last'] = \add_query_arg( 'page', $max_pages, $response['id'] );
4848

@@ -56,15 +56,14 @@ public function prepare_collection_response( $response, $request ) {
5656

5757
// Still here, so this is a Page request. Append the type.
5858
$response['type'] .= 'Page';
59-
$response['partOf'] = $response['id'];
60-
$response['id'] .= '?page=' . $page;
59+
$response['partOf'] = \remove_query_arg( 'page', $response['id'] );
6160

6261
if ( $max_pages > $page ) {
63-
$response['next'] = \add_query_arg( 'page', $page + 1, $response['id'] );
62+
$response['next'] = \add_query_arg( 'page', $page + 1, $response['partOf'] );
6463
}
6564

6665
if ( $page > 1 ) {
67-
$response['prev'] = \add_query_arg( 'page', $page - 1, $response['id'] );
66+
$response['prev'] = \add_query_arg( 'page', $page - 1, $response['partOf'] );
6867
}
6968

7069
return $response;

tests/includes/rest/class-test-trait-collection.php

Lines changed: 95 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ public function test_prepare_collection_response_collection() {
8080
$result = $this->instance->prepare_collection_response( $response, $request );
8181

8282
$this->assertEquals( 'Collection', $result['type'] );
83-
$this->assertEquals( 'https://example.org/collection?page=1', $result['first'] );
84-
$this->assertEquals( 'https://example.org/collection?page=3', $result['last'] );
83+
$this->assertEquals( 'https://example.org/collection?per_page=10&page=1', $result['first'] );
84+
$this->assertEquals( 'https://example.org/collection?per_page=10&page=3', $result['last'] );
8585
$this->assertArrayNotHasKey( 'items', $result );
8686
$this->assertArrayNotHasKey( 'orderedItems', $result );
8787
}
@@ -106,12 +106,12 @@ public function test_prepare_collection_response_collection_page() {
106106
$result = $this->instance->prepare_collection_response( $response, $request );
107107

108108
$this->assertEquals( 'CollectionPage', $result['type'] );
109-
$this->assertEquals( 'https://example.org/collection', $result['partOf'] );
110-
$this->assertEquals( 'https://example.org/collection?page=2', $result['id'] );
111-
$this->assertEquals( 'https://example.org/collection?page=1', $result['first'] );
112-
$this->assertEquals( 'https://example.org/collection?page=3', $result['last'] );
113-
$this->assertEquals( 'https://example.org/collection?page=3', $result['next'] );
114-
$this->assertEquals( 'https://example.org/collection?page=1', $result['prev'] );
109+
$this->assertEquals( 'https://example.org/collection?per_page=10', $result['partOf'] );
110+
$this->assertEquals( 'https://example.org/collection?page=2&per_page=10', $result['id'] );
111+
$this->assertEquals( 'https://example.org/collection?page=1&per_page=10', $result['first'] );
112+
$this->assertEquals( 'https://example.org/collection?page=3&per_page=10', $result['last'] );
113+
$this->assertEquals( 'https://example.org/collection?per_page=10&page=3', $result['next'] );
114+
$this->assertEquals( 'https://example.org/collection?per_page=10&page=1', $result['prev'] );
115115
}
116116

117117
/**
@@ -134,8 +134,8 @@ public function test_prepare_collection_response_first_page() {
134134
$result = $this->instance->prepare_collection_response( $response, $request );
135135

136136
$this->assertEquals( 'OrderedCollectionPage', $result['type'] );
137-
$this->assertEquals( 'https://example.org/collection?page=1', $result['id'] );
138-
$this->assertEquals( 'https://example.org/collection?page=2', $result['next'] );
137+
$this->assertEquals( 'https://example.org/collection?page=1&per_page=10', $result['id'] );
138+
$this->assertEquals( 'https://example.org/collection?per_page=10&page=2', $result['next'] );
139139
$this->assertArrayNotHasKey( 'prev', $result );
140140
}
141141

@@ -159,8 +159,8 @@ public function test_prepare_collection_response_last_page() {
159159
$result = $this->instance->prepare_collection_response( $response, $request );
160160

161161
$this->assertEquals( 'CollectionPage', $result['type'] );
162-
$this->assertEquals( 'https://example.org/collection?page=3', $result['id'] );
163-
$this->assertEquals( 'https://example.org/collection?page=2', $result['prev'] );
162+
$this->assertEquals( 'https://example.org/collection?page=3&per_page=10', $result['id'] );
163+
$this->assertEquals( 'https://example.org/collection?per_page=10&page=2', $result['prev'] );
164164
$this->assertArrayNotHasKey( 'next', $result );
165165
}
166166

@@ -196,4 +196,87 @@ public function test_prepare_collection_response_invalid_page() {
196196
$this->assertEquals( 'rest_post_invalid_page_number', $result->get_error_code() );
197197
$this->assertEquals( 400, $result->get_error_data()['status'] );
198198
}
199+
200+
/**
201+
* Test that pagination links preserve query parameters from original request.
202+
*
203+
* @covers ::prepare_collection_response
204+
*/
205+
public function test_prepare_collection_response_preserves_query_args() {
206+
$request = new \WP_REST_Request();
207+
$request->set_param( 'page', 2 );
208+
$request->set_param( 'per_page', 10 );
209+
$request->set_param( 'context', 'full' );
210+
$request->set_param( 'order', 'asc' );
211+
212+
$response = array(
213+
'type' => 'OrderedCollection',
214+
'id' => 'https://example.org/collection',
215+
'totalItems' => 35,
216+
'orderedItems' => array( 'item11', 'item12', 'item13' ),
217+
);
218+
219+
$result = $this->instance->prepare_collection_response( $response, $request );
220+
221+
$this->assertEquals( 'OrderedCollectionPage', $result['type'] );
222+
$this->assertEquals( 'https://example.org/collection?per_page=10&context=full&order=asc', $result['partOf'] );
223+
$this->assertEquals( 'https://example.org/collection?page=2&per_page=10&context=full&order=asc', $result['id'] );
224+
225+
// Check that query parameters are preserved in pagination links.
226+
$this->assertStringContainsString( 'context=full', $result['first'] );
227+
$this->assertStringContainsString( 'order=asc', $result['first'] );
228+
$this->assertStringContainsString( 'per_page=10', $result['first'] );
229+
$this->assertStringContainsString( 'page=1', $result['first'] );
230+
231+
$this->assertStringContainsString( 'context=full', $result['last'] );
232+
$this->assertStringContainsString( 'order=asc', $result['last'] );
233+
$this->assertStringContainsString( 'per_page=10', $result['last'] );
234+
$this->assertStringContainsString( 'page=4', $result['last'] );
235+
236+
$this->assertStringContainsString( 'context=full', $result['next'] );
237+
$this->assertStringContainsString( 'order=asc', $result['next'] );
238+
$this->assertStringContainsString( 'per_page=10', $result['next'] );
239+
$this->assertStringContainsString( 'page=3', $result['next'] );
240+
241+
$this->assertStringContainsString( 'context=full', $result['prev'] );
242+
$this->assertStringContainsString( 'order=asc', $result['prev'] );
243+
$this->assertStringContainsString( 'per_page=10', $result['prev'] );
244+
$this->assertStringContainsString( 'page=1', $result['prev'] );
245+
}
246+
247+
/**
248+
* Test that pagination links preserve query parameters for Collection (non-page) requests.
249+
*
250+
* @covers ::prepare_collection_response
251+
*/
252+
public function test_prepare_collection_response_preserves_query_args_for_collection() {
253+
$request = new \WP_REST_Request();
254+
$request->set_param( 'per_page', 2 );
255+
$request->set_param( 'context', 'full' );
256+
$request->set_param( 'order', 'desc' );
257+
258+
$response = array(
259+
'type' => 'OrderedCollection',
260+
'id' => 'https://example.org/collection',
261+
'totalItems' => 5,
262+
'items' => array( 'item1', 'item2', 'item3', 'item4', 'item5' ),
263+
);
264+
265+
$result = $this->instance->prepare_collection_response( $response, $request );
266+
267+
$this->assertEquals( 'OrderedCollection', $result['type'] );
268+
$this->assertArrayNotHasKey( 'items', $result );
269+
$this->assertArrayNotHasKey( 'orderedItems', $result );
270+
271+
// Check that query parameters are preserved in first and last links.
272+
$this->assertStringContainsString( 'context=full', $result['first'] );
273+
$this->assertStringContainsString( 'order=desc', $result['first'] );
274+
$this->assertStringContainsString( 'per_page=2', $result['first'] );
275+
$this->assertStringContainsString( 'page=1', $result['first'] );
276+
277+
$this->assertStringContainsString( 'context=full', $result['last'] );
278+
$this->assertStringContainsString( 'order=desc', $result['last'] );
279+
$this->assertStringContainsString( 'per_page=2', $result['last'] );
280+
$this->assertStringContainsString( 'page=3', $result['last'] );
281+
}
199282
}

0 commit comments

Comments
 (0)