Opened 7 months ago
Last modified 2 months ago
#63890 new enhancement
Add multiple order conditions to WP_Term_Query
| Reported by: |
|
Owned by: | |
|---|---|---|---|
| Milestone: | Awaiting Review | Priority: | normal |
| Severity: | normal | Version: | |
| Component: | Taxonomy | Keywords: | has-patch dev-feedback |
| Focuses: | Cc: |
Description
Now WP_Term_Query accepts orderby only as a string, as WP_Query accepts an array.
Use case: I need to sort by term meta plus title. I cannot sort after getting the terms because I am not getting meta with terms, and requesting meta for each term will be unproductive.
Change History (5)
This ticket was mentioned in PR #9658 on WordPress/wordpress-develop by @abcd95.
7 months ago
#1
- Keywords has-patch added
This ticket was mentioned in Slack in #core-test by sajib1223. View the logs.
2 months ago
#4
@
2 months ago
- Keywords needs-testing removed
Patch Testing Report
Patch tested: https://github.com/WordPress/wordpress-develop/pull/9658
Environment
- WordPress: 7.0-alpha-61215-src
- PHP: 8.2.29
- Server: nginx/1.27.5
- Database: mysqli (Server: 8.4.7 / Client: mysqlnd 8.2.29)
- Browser: Firefox 147.0
- OS: Windows 10/11
- Theme: Twenty Twenty-Five 1.4
- MU Plugins: None activated
- Plugins:
- Test Reports 1.2.1
- Test Ticket 63890 - Array Orderby 1.0.0
Steps taken
- Applied patch using
git checkout -b test/10445-auto-update&gh pr diff 9658 | git apply - Added following code to a custom plugin and visit.
<?php /** * Plugin Name: Test Ticket 63890 - Array Orderby * Description: Tests array orderby support in WP_Term_Query * Version: 1.0.0 */ if ( ! defined( 'ABSPATH' ) ) { die(); } class Test_Ticket_63890 { private $taxonomy = 'wptests_tax_63890'; private $result = null; public function __construct() { add_action( 'admin_menu', array( $this, 'add_admin_menu' ) ); add_action( 'admin_init', array( $this, 'maybe_run_test' ) ); } public function add_admin_menu() { add_menu_page( 'Test 63890', 'Test 63890', 'manage_options', 'test-ticket-63890', array( $this, 'render_page' ), 'dashicons-yes-alt' ); } public function maybe_run_test() { if ( isset( $_GET['page'] ) && $_GET['page'] === 'test-ticket-63890' ) { $this->setup(); $this->cleanup(); $this->result = $this->test_orderby_array_multiple_fields(); } } private function setup() { // Register taxonomy if not already registered if ( ! taxonomy_exists( $this->taxonomy ) ) { register_taxonomy( $this->taxonomy, 'post', array( 'public' => false ) ); } } private function cleanup() { // Only delete terms, keep taxonomy registered if ( taxonomy_exists( $this->taxonomy ) ) { $terms = get_terms( array( 'taxonomy' => $this->taxonomy, 'hide_empty' => false, 'fields' => 'ids' ) ); if ( ! is_wp_error( $terms ) && ! empty( $terms ) ) { foreach ( $terms as $term_id ) { wp_delete_term( $term_id, $this->taxonomy ); } } } } private function test_orderby_array_multiple_fields() { // Create 4 terms with unique names $timestamp = time(); $terms = array(); for ( $i = 0; $i < 4; $i++ ) { $term = wp_insert_term( 'Term ' . ( $i + 1 ) . ' ' . $timestamp, $this->taxonomy ); if ( ! is_wp_error( $term ) ) { $terms[] = $term['term_id']; } } // Check if we have all 4 terms if ( count( $terms ) !== 4 ) { return array( 'passed' => false, 'expected' => array(), 'actual' => array(), 'terms' => array(), 'sql' => 'Error: Could not create all 4 test terms', ); } // Add priority meta 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 ); // Run query with array orderby $q = new WP_Term_Query( array( 'taxonomy' => $this->taxonomy, 'orderby' => array( 'meta_value_num' => 'DESC', 'term_id' => 'ASC', ), 'meta_key' => 'priority', 'hide_empty' => false, 'fields' => 'ids', ) ); // Expected order: priority 2 first (DESC), then by term_id (ASC) $expected = array( $terms[0], $terms[2], $terms[1], $terms[3] ); $actual = $q->terms; $term_details = array(); foreach ( $terms as $term_id ) { $term = get_term( $term_id, $this->taxonomy ); if ( ! is_wp_error( $term ) && $term ) { $term_details[] = array( 'id' => $term_id, 'name' => $term->name, 'priority' => get_term_meta( $term_id, 'priority', true ), ); } } return array( 'passed' => $expected === $actual, 'expected' => $expected, 'actual' => $actual, 'terms' => $term_details, 'sql' => $q->request, ); } public function render_page() { $result = $this->result; if ( ! $result || ! isset( $result['passed'] ) ) { echo '<div class="wrap"><h1>Test 63890</h1><p>Error running test. Please try again.</p></div>'; return; } $status = $result['passed'] ? 'PASS' : 'FAIL'; $color = $result['passed'] ? '#46b450' : '#dc3232'; ?> <div class="wrap"> <h1>Test Ticket #63890</h1> <p>Testing: Array orderby with multiple fields (meta_value_num DESC + term_id ASC)</p> <style> .test-box { background: #fff; border-left: 4px solid <?php echo esc_attr( $color ); ?>; padding: 20px; margin: 20px 0; box-shadow: 0 1px 3px rgba(0,0,0,0.1); } .status { display: inline-block; background: <?php echo esc_attr( $color ); ?>; color: #fff; padding: 5px 15px; border-radius: 3px; font-weight: bold; } table { width: 100%; border-collapse: collapse; margin: 15px 0; } th, td { padding: 10px; text-align: left; border: 1px solid #ddd; } th { background: #f5f5f5; font-weight: bold; } pre { background: #f5f5f5; padding: 10px; border-radius: 3px; overflow-x: auto; } </style> <div class="test-box"> <h2>Result: <span class="status"><?php echo esc_html( $status ); ?></span></h2> <h3>Expected vs Actual</h3> <table> <tr> <th>Expected</th> <td><?php echo esc_html( implode( ', ', $result['expected'] ) ); ?></td> </tr> <tr> <th>Actual</th> <td><?php echo esc_html( implode( ', ', $result['actual'] ) ); ?></td> </tr> <tr> <th>Match</th> <td><?php echo $result['passed'] ? '✓ YES' : '✗ NO'; ?></td> </tr> </table> <h3>Term Details</h3> <?php if ( ! empty( $result['terms'] ) ) : ?> <table> <thead> <tr> <th>Term ID</th> <th>Name</th> <th>Priority (Meta)</th> </tr> </thead> <tbody> <?php foreach ( $result['terms'] as $term ) : ?> <tr> <td><?php echo esc_html( $term['id'] ); ?></td> <td><?php echo esc_html( $term['name'] ); ?></td> <td><?php echo esc_html( $term['priority'] ); ?></td> </tr> <?php endforeach; ?> </tbody> </table> <?php else : ?> <p>No term details available.</p> <?php endif; ?> <h3>Generated SQL</h3> <pre><?php echo esc_html( $result['sql'] ); ?></pre> </div> <p> <a href="<?php echo esc_url( admin_url( 'admin.php?page=test-ticket-63890' ) ); ?>" class="button button-primary"> Re-run Test </a> </p> </div> <?php } } new Test_Ticket_63890();
- Went to the "Test 63890" Menu.
- It showed "Result: PASS" & some metadata.
- ✅ Patch enables multiple order condition for terms.
Alternative Steps taken
- Applied patch using
git checkout -b test/10445-auto-update&gh pr diff 9658 | git apply. - Run
npm run test:php -- --filter test_orderby_array_multiple_fields. - It produced "OK (1 test, 1 assertion)"
- ✅ Patch enables multiple order condition for terms.
Expected result
Patch enables multiple order condition for terms.
Screenshots with results
Test results page:
Note: See
TracTickets for help on using
tickets.

Thanks, @oglekler, for the enhancement suggestion.
I observed the WP_Query structure closely and attempted to replicate something similar with the WP_Term_Query. I have tested all edge cases as much as I could. I'm gonna go ahead and add the needs-testing label so that others can also take a swing at this and see if this breaks anything else while I am preparing automated tests for this.