Skip to content

Commit b400d4b

Browse files
committed
Refactor WP_Query to ensure consistent ordering by appending ID as a secondary sort field in various scenarios. Update unit tests to reflect changes in expected SQL output for orderby cases, enhancing determinism in query results.
1 parent 9b7c8e2 commit b400d4b

File tree

2 files changed

+24
-13
lines changed

2 files changed

+24
-13
lines changed

src/wp-includes/class-wp-query.php

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2595,7 +2595,7 @@ public function get_posts() {
25952595
if ( empty( $orderby_array ) ) {
25962596
$orderby = "{$wpdb->posts}.post_date " . $query_vars['order'] . ', ' . "{$wpdb->posts}.ID " . $query_vars['order'];
25972597
} else {
2598-
$orderby = implode( ', ', $orderby_array );
2598+
$orderby = trim( implode( ', ', $orderby_array ) );
25992599
}
26002600
}
26012601

@@ -3311,7 +3311,18 @@ public function get_posts() {
33113311
}
33123312

33133313
if ( $query_vars['cache_results'] && $id_query_is_cacheable ) {
3314-
$new_request = str_replace( $fields, "{$wpdb->posts}.*", $this->request );
3314+
$new_request = $this->request;
3315+
// Split SQL into parts.
3316+
$parts = explode( 'ORDER BY', $new_request );
3317+
if ( count( $parts ) === 2 ) {
3318+
// Replace only in the SELECT part, preserve ORDER BY.
3319+
$select_part = str_replace( $fields, "{$wpdb->posts}.*", $parts[0] );
3320+
$new_request = $select_part . 'ORDER BY' . $parts[1];
3321+
} else {
3322+
// No ORDER BY clause, safe to replace.
3323+
$new_request = str_replace( $fields, "{$wpdb->posts}.*", $new_request );
3324+
}
3325+
33153326
$cache_key = $this->generate_cache_key( $query_vars, $new_request );
33163327

33173328
$cache_found = false;
@@ -5099,8 +5110,8 @@ protected function generate_cache_key( array $args, $sql ) {
50995110
}
51005111

51015112
// Add a default orderby value of date to ensure same cache key generation.
5102-
if ( ! isset( $q['orderby'] ) ) {
5103-
$args['orderby'] = 'date';
5113+
if ( ! isset( $args['orderby'] ) ) {
5114+
$args['orderby'] = 'date, ID';
51045115
}
51055116

51065117
$placeholder = $wpdb->placeholder_escape();

tests/phpunit/tests/rest-api/rest-posts-controller.php

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ public function test_get_items_include_query( $method ) {
488488
$this->assertSame( 2, $headers['X-WP-Total'], 'Failed asserting that the number of posts is correct.' );
489489
}
490490

491-
$this->assertPostsOrderedBy( '{posts}.post_date DESC' );
491+
$this->assertPostsOrderedBy( '{posts}.post_date DESC, {posts}.ID DESC' );
492492

493493
// 'orderby' => 'include'.
494494
$request->set_param( 'orderby', 'include' );
@@ -544,7 +544,7 @@ public function test_get_items_orderby_author_query() {
544544
$this->assertSame( self::$editor_id, $data[1]['author'] );
545545
$this->assertSame( self::$editor_id, $data[2]['author'] );
546546

547-
$this->assertPostsOrderedBy( '{posts}.post_author DESC' );
547+
$this->assertPostsOrderedBy( '{posts}.post_author DESC, {posts}.ID DESC' );
548548
}
549549

550550
public function test_get_items_orderby_modified_query() {
@@ -568,7 +568,7 @@ public function test_get_items_orderby_modified_query() {
568568
$this->assertSame( $id3, $data[1]['id'] );
569569
$this->assertSame( $id2, $data[2]['id'] );
570570

571-
$this->assertPostsOrderedBy( '{posts}.post_modified DESC' );
571+
$this->assertPostsOrderedBy( '{posts}.post_modified DESC, {posts}.ID DESC' );
572572
}
573573

574574
public function test_get_items_orderby_parent_query() {
@@ -606,7 +606,7 @@ public function test_get_items_orderby_parent_query() {
606606
$this->assertSame( 0, $data[1]['parent'] );
607607
$this->assertSame( 0, $data[2]['parent'] );
608608

609-
$this->assertPostsOrderedBy( '{posts}.post_parent DESC' );
609+
$this->assertPostsOrderedBy( '{posts}.post_parent DESC, {posts}.ID DESC' );
610610
}
611611

612612
public function test_get_items_exclude_query() {
@@ -976,14 +976,14 @@ public function test_get_items_order_and_orderby() {
976976
$response = rest_get_server()->dispatch( $request );
977977
$data = $response->get_data();
978978
$this->assertSame( 'Apple Sauce', $data[0]['title']['rendered'] );
979-
$this->assertPostsOrderedBy( '{posts}.post_title DESC' );
979+
$this->assertPostsOrderedBy( '{posts}.post_title DESC, {posts}.ID DESC' );
980980

981981
// 'order' => 'asc'.
982982
$request->set_param( 'order', 'asc' );
983983
$response = rest_get_server()->dispatch( $request );
984984
$data = $response->get_data();
985985
$this->assertSame( 'Apple Cobbler', $data[0]['title']['rendered'] );
986-
$this->assertPostsOrderedBy( '{posts}.post_title ASC' );
986+
$this->assertPostsOrderedBy( '{posts}.post_title ASC, {posts}.ID ASC' );
987987

988988
// 'order' => 'asc,id' should error.
989989
$request->set_param( 'order', 'asc,id' );
@@ -1068,7 +1068,7 @@ public function test_get_items_with_orderby_slug() {
10681068
// Default ORDER is DESC.
10691069
$this->assertSame( 'xyz', $data[0]['slug'] );
10701070
$this->assertSame( 'abc', $data[1]['slug'] );
1071-
$this->assertPostsOrderedBy( '{posts}.post_name DESC' );
1071+
$this->assertPostsOrderedBy( '{posts}.post_name DESC, {posts}.ID DESC' );
10721072
}
10731073

10741074
public function test_get_items_with_orderby_slugs() {
@@ -1120,7 +1120,7 @@ public function test_get_items_with_orderby_relevance() {
11201120
$this->assertCount( 2, $data );
11211121
$this->assertSame( $id1, $data[0]['id'] );
11221122
$this->assertSame( $id2, $data[1]['id'] );
1123-
$this->assertPostsOrderedBy( '{posts}.post_title LIKE \'%relevant%\' DESC, {posts}.post_date DESC' );
1123+
$this->assertPostsOrderedBy( '{posts}.post_title LIKE \'%relevant%\' DESC, {posts}.post_date DESC, {posts}.ID DESC' );
11241124
}
11251125

11261126
public function test_get_items_with_orderby_relevance_two_terms() {
@@ -1148,7 +1148,7 @@ public function test_get_items_with_orderby_relevance_two_terms() {
11481148
$this->assertCount( 2, $data );
11491149
$this->assertSame( $id1, $data[0]['id'] );
11501150
$this->assertSame( $id2, $data[1]['id'] );
1151-
$this->assertPostsOrderedBy( '(CASE WHEN {posts}.post_title LIKE \'%relevant content%\' THEN 1 WHEN {posts}.post_title LIKE \'%relevant%\' AND {posts}.post_title LIKE \'%content%\' THEN 2 WHEN {posts}.post_title LIKE \'%relevant%\' OR {posts}.post_title LIKE \'%content%\' THEN 3 WHEN {posts}.post_excerpt LIKE \'%relevant content%\' THEN 4 WHEN {posts}.post_content LIKE \'%relevant content%\' THEN 5 ELSE 6 END), {posts}.post_date DESC' );
1151+
$this->assertPostsOrderedBy( '(CASE WHEN {posts}.post_title LIKE \'%relevant content%\' THEN 1 WHEN {posts}.post_title LIKE \'%relevant%\' AND {posts}.post_title LIKE \'%content%\' THEN 2 WHEN {posts}.post_title LIKE \'%relevant%\' OR {posts}.post_title LIKE \'%content%\' THEN 3 WHEN {posts}.post_excerpt LIKE \'%relevant content%\' THEN 4 WHEN {posts}.post_content LIKE \'%relevant content%\' THEN 5 ELSE 6 END), {posts}.post_date DESC, {posts}.ID DESC' );
11521152
}
11531153

11541154
public function test_get_items_with_orderby_relevance_missing_search() {

0 commit comments

Comments
 (0)