WordPress.org

Make WordPress Core

Changeset 31312


Ignore:
Timestamp:
01/31/2015 03:47:51 PM (3 years ago)
Author:
boonebgorges
Message:

Improve support for ordering WP_Query results by postmeta.

WP_Meta_Query clauses now support a 'name' parameter. When building a
WP_Query object, the value of 'orderby' can reference this 'name', so that
it's possible to order by any clause in a meta_query, not just the first one
(as when using 'orderby=meta_value'). This improvement also makes it possible
to order by multiple meta query clauses (or by any other eligible field plus
a meta query clause), using the array syntax for 'orderby' introduced in [29027].

Props Funkatronic, boonebgorges.
Fixes #31045.

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/meta.php

    r31212 r31312  
    935935
    936936    /**
     937     * A flat list of clauses, keyed by clause 'name'.
     938     *
     939     * @since 4.2.0
     940     * @var array
     941     */
     942    protected $clauses = array();
     943
     944    /**
    937945     * Constructor.
    938946     *
    939947     * @since 3.2.0
     948     * @since 4.2.0 Introduced the `$name` parameter, for improved `$orderby` support in the parent query.
     949     *
    940950     * @access public
    941951     *
     
    958968     *                               'DATETIME', 'DECIMAL', 'SIGNED', 'TIME', or 'UNSIGNED'.
    959969     *                               Default is 'CHAR'.
     970     *         @type string $name    Optional. A unique identifier for the clause. If provided, `$name` can be
     971     *                               referenced in the `$orderby` parameter of the parent query.
    960972     *     }
    961973     * }
     
    13751387        $clause['alias'] = $alias;
    13761388
     1389        // Determine the data type.
     1390        $_meta_type = isset( $clause['type'] ) ? $clause['type'] : '';
     1391        $meta_type  = $this->get_cast_for_type( $_meta_type );
     1392        $clause['cast'] = $meta_type;
     1393
     1394        // Store the clause in our flat array.
     1395        $clause_name = isset( $clause['name'] ) ? $clause['name'] : $clause['alias'];
     1396        $this->clauses[ $clause_name ] =& $clause;
     1397
    13771398        // Next, build the WHERE clause.
    13781399
     
    13891410        if ( array_key_exists( 'value', $clause ) ) {
    13901411            $meta_value = $clause['value'];
    1391             $meta_type = $this->get_cast_for_type( isset( $clause['type'] ) ? $clause['type'] : '' );
    13921412
    13931413            if ( in_array( $meta_compare, array( 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ) ) ) {
     
    14491469
    14501470        return $sql_chunks;
     1471    }
     1472
     1473    /**
     1474     * Get a flattened list of sanitized meta clauses, indexed by clause 'name'.
     1475     *
     1476     * This array should be used for clause lookup, as when the table alias and CAST type must be determined for
     1477     * a value of 'orderby' corresponding to a meta clause.
     1478     *
     1479     * @since 4.2.0
     1480     * @access public
     1481     *
     1482     * @return array
     1483     */
     1484    public function get_clauses() {
     1485        return $this->clauses;
    14511486    }
    14521487
  • trunk/src/wp-includes/query.php

    r31219 r31312  
    22342234        $primary_meta_key = '';
    22352235        $primary_meta_query = false;
    2236         if ( ! empty( $this->meta_query->queries ) ) {
    2237             $primary_meta_query = reset( $this->meta_query->queries );
     2236        $meta_clauses = $this->meta_query->get_clauses();
     2237        if ( ! empty( $meta_clauses ) ) {
     2238            $primary_meta_query = reset( $meta_clauses );
    22382239
    22392240            if ( ! empty( $primary_meta_query['key'] ) ) {
     
    22442245            $allowed_keys[] = 'meta_value';
    22452246            $allowed_keys[] = 'meta_value_num';
     2247            $allowed_keys   = array_merge( $allowed_keys, array_keys( $meta_clauses ) );
    22462248        }
    22472249
     
    22612263            case 'menu_order':
    22622264            case 'comment_count':
    2263                 $orderby = "$wpdb->posts.{$orderby}";
     2265                $orderby_clause = "$wpdb->posts.{$orderby}";
    22642266                break;
    22652267            case 'rand':
    2266                 $orderby = 'RAND()';
     2268                $orderby_clause = 'RAND()';
    22672269                break;
    22682270            case $primary_meta_key:
    22692271            case 'meta_value':
    22702272                if ( ! empty( $primary_meta_query['type'] ) ) {
    2271                     $sql_type = $this->meta_query->get_cast_for_type( $primary_meta_query['type'] );
    2272                     $orderby = "CAST($wpdb->postmeta.meta_value AS {$sql_type})";
     2273                    $orderby_clause = "CAST({$primary_meta_query['alias']}.meta_value AS {$primary_meta_query['cast']})";
    22732274                } else {
    2274                     $orderby = "$wpdb->postmeta.meta_value";
     2275                    $orderby_clause = "{$primary_meta_query['alias']}.meta_value";
    22752276                }
    22762277                break;
    22772278            case 'meta_value_num':
    2278                 $orderby = "$wpdb->postmeta.meta_value+0";
     2279                $orderby_clause = "{$primary_meta_query['alias']}.meta_value+0";
    22792280                break;
    22802281            default:
    2281                 $orderby = "$wpdb->posts.post_" . $orderby;
     2282                if ( array_key_exists( $orderby, $meta_clauses ) ) {
     2283                    // $orderby corresponds to a meta_query clause.
     2284                    $meta_clause = $meta_clauses[ $orderby ];
     2285                    $orderby_clause = "CAST({$meta_clause['alias']}.meta_value AS {$meta_clause['cast']})";
     2286                } else {
     2287                    // Default: order by post field.
     2288                    $orderby_clause = "$wpdb->posts.post_" . sanitize_key( $orderby );
     2289                }
     2290
    22822291                break;
    22832292        }
    22842293
    2285         return $orderby;
     2294        return $orderby_clause;
    22862295    }
    22872296
     
    28132822
    28142823        $where .= $search . $whichauthor . $whichmimetype;
     2824
     2825        if ( ! empty( $this->meta_query->queries ) ) {
     2826            $clauses = $this->meta_query->get_sql( 'post', $wpdb->posts, 'ID', $this );
     2827            $join   .= $clauses['join'];
     2828            $where  .= $clauses['where'];
     2829        }
    28152830
    28162831        $rand = ( isset( $q['orderby'] ) && 'rand' === $q['orderby'] );
     
    30293044
    30303045            $where .= ')';
    3031         }
    3032 
    3033         if ( !empty( $this->meta_query->queries ) ) {
    3034             $clauses = $this->meta_query->get_sql( 'post', $wpdb->posts, 'ID', $this );
    3035             $join .= $clauses['join'];
    3036             $where .= $clauses['where'];
    30373046        }
    30383047
  • trunk/tests/phpunit/tests/query/metaQuery.php

    r31286 r31312  
    15591559        $this->assertEqualSets( array( $post_id, $post_id3, $post_id4, $post_id5, $post_id6 ), $posts );
    15601560    }
     1561
     1562    /**
     1563     * @ticket 31045
     1564     */
     1565    public function test_orderby_name() {
     1566        $posts = $this->factory->post->create_many( 3 );
     1567        add_post_meta( $posts[0], 'foo', 'aaa' );
     1568        add_post_meta( $posts[1], 'foo', 'zzz' );
     1569        add_post_meta( $posts[2], 'foo', 'jjj' );
     1570
     1571        $q = new WP_Query( array(
     1572            'fields' => 'ids',
     1573            'meta_query' => array(
     1574                array(
     1575                    'name' => 'foo_name',
     1576                    'key' => 'foo',
     1577                    'compare' => 'EXISTS',
     1578                ),
     1579            ),
     1580            'orderby' => 'foo_name',
     1581            'order' => 'DESC',
     1582        ) );
     1583
     1584        $this->assertEquals( array( $posts[1], $posts[2], $posts[0] ), $q->posts );
     1585    }
     1586
     1587    /**
     1588     * @ticket 31045
     1589     */
     1590    public function test_orderby_name_as_secondary_sort() {
     1591        $p1 = $this->factory->post->create( array(
     1592            'post_date' => '2015-01-28 03:00:00',
     1593        ) );
     1594        $p2 = $this->factory->post->create( array(
     1595            'post_date' => '2015-01-28 05:00:00',
     1596        ) );
     1597        $p3 = $this->factory->post->create( array(
     1598            'post_date' => '2015-01-28 03:00:00',
     1599        ) );
     1600
     1601        add_post_meta( $p1, 'foo', 'jjj' );
     1602        add_post_meta( $p2, 'foo', 'zzz' );
     1603        add_post_meta( $p3, 'foo', 'aaa' );
     1604
     1605        $q = new WP_Query( array(
     1606            'fields' => 'ids',
     1607            'meta_query' => array(
     1608                array(
     1609                    'name' => 'foo_name',
     1610                    'key' => 'foo',
     1611                    'compare' => 'EXISTS',
     1612                ),
     1613            ),
     1614            'orderby' => array(
     1615                'post_date' => 'asc',
     1616                'foo_name' => 'asc',
     1617            ),
     1618        ) );
     1619
     1620        $this->assertEquals( array( $p3, $p1, $p2 ), $q->posts );
     1621    }
     1622
     1623    /**
     1624     * @ticket 31045
     1625     */
     1626    public function test_orderby_more_than_one_name() {
     1627        $posts = $this->factory->post->create_many( 3 );
     1628
     1629        add_post_meta( $posts[0], 'foo', 'jjj' );
     1630        add_post_meta( $posts[1], 'foo', 'zzz' );
     1631        add_post_meta( $posts[2], 'foo', 'jjj' );
     1632        add_post_meta( $posts[0], 'bar', 'aaa' );
     1633        add_post_meta( $posts[1], 'bar', 'ccc' );
     1634        add_post_meta( $posts[2], 'bar', 'bbb' );
     1635
     1636        $q = new WP_Query( array(
     1637            'fields' => 'ids',
     1638            'meta_query' => array(
     1639                array(
     1640                    'name' => 'foo_name',
     1641                    'key' => 'foo',
     1642                    'compare' => 'EXISTS',
     1643                ),
     1644                array(
     1645                    'name' => 'bar_name',
     1646                    'key' => 'bar',
     1647                    'compare' => 'EXISTS',
     1648                ),
     1649            ),
     1650            'orderby' => array(
     1651                'foo_name' => 'asc',
     1652                'bar_name' => 'desc',
     1653            ),
     1654        ) );
     1655
     1656        $this->assertEquals( array( $posts[2], $posts[0], $posts[1] ), $q->posts );
     1657    }
    15611658}
Note: See TracChangeset for help on using the changeset viewer.