Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions src/wp-includes/class-wp-term-query.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class WP_Term_Query {
* @since 5.1.0 Introduced the 'meta_compare_key' parameter.
* @since 5.3.0 Introduced the 'meta_type_key' parameter.
* @since 6.4.0 Introduced the 'cache_results' parameter.
* @since 6.9.0 Added support for array of orderby fields.
*
* @param string|array $query {
* Optional. Array or query string of term query parameters. Default empty.
Expand All @@ -100,7 +101,7 @@ class WP_Term_Query {
* should be limited.
* @type int|int[] $object_ids Object ID, or array of object IDs. Results will be
* limited to terms associated with these objects.
* @type string $orderby Field(s) to order terms by. Accepts:
* @type string|array $orderby Field(s) to order terms by. Accepts:
* - Term fields ('name', 'slug', 'term_group', 'term_id', 'id',
* 'description', 'parent', 'term_order'). Unless `$object_ids`
* is not empty, 'term_order' is treated the same as 'term_id'.
Expand All @@ -112,6 +113,7 @@ class WP_Term_Query {
* - The value of `$meta_key`.
* - The array keys of `$meta_query`.
* - 'none' to omit the ORDER BY clause.
* - Array of orderby fields as keys with order ('ASC'/'DESC') as values.
* Default 'name'.
* @type string $order Whether to order terms in ascending or descending order.
* Accepts 'ASC' (ascending) or 'DESC' (descending).
Expand Down Expand Up @@ -445,7 +447,24 @@ public function get_terms() {
$_orderby = 'term_id';
}

$orderby = $this->parse_orderby( $_orderby );
if ( is_array( $_orderby ) ) {
$orderby_array = array();

foreach ( $_orderby as $orderby_field => $order ) {
$parsed_orderby = $this->parse_orderby( $orderby_field );

if ( ! $parsed_orderby ) {
continue;
}

$order = $this->parse_order( $order );
$orderby_array[] = "{$parsed_orderby} {$order}";
}

$orderby = implode( ', ', $orderby_array );
} else {
$orderby = $this->parse_orderby( $_orderby );
}

if ( $orderby ) {
$orderby = "ORDER BY $orderby";
Expand Down Expand Up @@ -746,7 +765,7 @@ public function get_terms() {

$this->sql_clauses['select'] = "SELECT $distinct $fields";
$this->sql_clauses['from'] = "FROM $wpdb->terms AS t $join";
$this->sql_clauses['orderby'] = $orderby ? "$orderby $order" : '';
$this->sql_clauses['orderby'] = $orderby ? ( is_array( $this->query_vars['orderby'] ) ? $orderby : "$orderby $order" ) : '';
$this->sql_clauses['limits'] = $limits;

// Beginning of the string is on a new line to prevent leading whitespace. See https://core.trac.wordpress.org/ticket/56841.
Expand Down
152 changes: 152 additions & 0 deletions tests/phpunit/tests/term/query.php
Original file line number Diff line number Diff line change
Expand Up @@ -1185,4 +1185,156 @@ public function test_query_does_not_have_leading_whitespace() {

$this->assertSame( ltrim( $q->request ), $q->request, 'The query has leading whitespace' );
}

/**
* @ticket 63890
*/
public function test_orderby_array_multiple_fields() {
register_taxonomy( 'wptests_tax_orderby', 'post' );

$terms = self::factory()->term->create_many(
4,
array(
'taxonomy' => 'wptests_tax_orderby',
)
);

add_term_meta( $terms[0], 'priority', 2 );
add_term_meta( $terms[1], 'priority', 1 );
add_term_meta( $terms[2], 'priority', 2 );
add_term_meta( $terms[3], 'priority', 1 );

$q = new WP_Term_Query(
array(
'taxonomy' => 'wptests_tax_orderby',
'orderby' => array(
'meta_value_num' => 'DESC',
'term_id' => 'ASC',
),
'meta_key' => 'priority',
'hide_empty' => false,
'fields' => 'ids',
)
);

$expected = array( $terms[0], $terms[2], $terms[1], $terms[3] );
$this->assertSame( $expected, $q->terms );
}

/**
* @ticket 63890
*/
public function test_orderby_array_with_name_and_count() {
register_taxonomy( 'wptests_tax_orderby_name', 'post' );

$term1 = self::factory()->term->create(
array(
'taxonomy' => 'wptests_tax_orderby_name',
'name' => 'Beta Term',
)
);
$term2 = self::factory()->term->create(
array(
'taxonomy' => 'wptests_tax_orderby_name',
'name' => 'Zeta Term',
)
);
$term3 = self::factory()->term->create(
array(
'taxonomy' => 'wptests_tax_orderby_name',
'name' => 'Alpha Term',
)
);

$post1 = self::factory()->post->create();
$post2 = self::factory()->post->create();
$post3 = self::factory()->post->create();
$post4 = self::factory()->post->create();

wp_set_object_terms( $post1, array( $term1 ), 'wptests_tax_orderby_name' );
wp_set_object_terms( $post2, array( $term1 ), 'wptests_tax_orderby_name' );
wp_set_object_terms( $post3, array( $term2 ), 'wptests_tax_orderby_name' );
wp_set_object_terms( $post4, array( $term3 ), 'wptests_tax_orderby_name' );

$q = new WP_Term_Query(
array(
'taxonomy' => 'wptests_tax_orderby_name',
'orderby' => array(
'count' => 'DESC',
'name' => 'ASC',
),
'hide_empty' => false,
'fields' => 'ids',
)
);

$expected = array( $term1, $term3, $term2 );
$this->assertSame( $expected, $q->terms );
}

/**
* @ticket 63890
*/
public function test_orderby_array_backward_compatibility() {
register_taxonomy( 'wptests_tax_compat', 'post' );

$terms = self::factory()->term->create_many(
3,
array(
'taxonomy' => 'wptests_tax_compat',
)
);

$q1 = new WP_Term_Query(
array(
'taxonomy' => 'wptests_tax_compat',
'orderby' => 'term_id',
'order' => 'ASC',
'hide_empty' => false,
'fields' => 'ids',
)
);

$this->assertSame( $terms, $q1->terms );

$q2 = new WP_Term_Query(
array(
'taxonomy' => 'wptests_tax_compat',
'orderby' => array( 'term_id' => 'ASC' ),
'hide_empty' => false,
'fields' => 'ids',
)
);

$this->assertSame( $terms, $q2->terms );
}

/**
* @ticket 63890
*/
public function test_orderby_array_all_invalid_fields_fallback() {
register_taxonomy( 'wptests_tax_fallback', 'post' );

$terms = self::factory()->term->create_many(
2,
array(
'taxonomy' => 'wptests_tax_fallback',
)
);

$q = new WP_Term_Query(
array(
'taxonomy' => 'wptests_tax_fallback',
'orderby' => array(
'invalid_field1' => 'ASC',
'invalid_field2' => 'DESC',
),
'hide_empty' => false,
'fields' => 'ids',
)
);

$this->assertNotEmpty( $q->terms );
$this->assertCount( 2, $q->terms );
}
}
Loading