Make WordPress Core

Opened 7 months ago

Last modified 2 months ago

#63890 new enhancement

Add multiple order conditions to WP_Term_Query

Reported by: oglekler's profile oglekler 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

#2 @abcd95
7 months ago

  • Keywords needs-testing added

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.

This ticket was mentioned in Slack in #core-test by sajib1223. View the logs.


2 months ago

#4 @sajib1223
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

  1. Applied patch using git checkout -b test/10445-auto-update & gh pr diff 9658 | git apply
  2. 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();
    
    
    
  1. Went to the "Test 63890" Menu.
  2. It showed "Result: PASS" & some metadata.
  3. ✅ Patch enables multiple order condition for terms.

Alternative Steps taken

  1. Applied patch using git checkout -b test/10445-auto-update & gh pr diff 9658 | git apply.
  2. Run npm run test:php -- --filter test_orderby_array_multiple_fields.
  3. It produced "OK (1 test, 1 assertion)"
  4. ✅ Patch enables multiple order condition for terms.

Expected result

Patch enables multiple order condition for terms.

Screenshots with results

Test results page:

https://files.catbox.moe/wcfq6r.png

#5 @SirLouen
2 months ago

  • Keywords dev-feedback added
Note: See TracTickets for help on using tickets.