WordPress.org

Make WordPress Core

Changeset 31663


Ignore:
Timestamp:
03/07/2015 04:05:11 PM (6 years ago)
Author:
boonebgorges
Message:

Improve 'orderby' syntax for WP_User_Query.

This changeset ports a number of 'orderby' features from WP_Query and
WP_Comment_Query:

  • Allow multiple 'orderby' values to be passed as a space-separated list.
  • Allow multiple 'orderby' values to be passed as a flat array.
  • Allow multi-dimensional 'orderby', with orderby fields as array keys and ASC/DESC as the corresponding values.

See #31265.

Location:
trunk
Files:
2 edited

Legend:

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

    r31608 r31663  
    501501     *
    502502     * @since 3.1.0
    503      * @since 4.2.0 Added 'meta_value_num' support for `$orderby` parameter.
     503     * @since 4.2.0 Added 'meta_value_num' support for `$orderby` parameter. Added multi-dimensional array syntax
     504     *              for `$orderby` parameter.
    504505     * @access public
    505506     *
     
    522523     *     @type array        $search_columns  Array of column names to be searched. Accepts 'ID', 'login',
    523524     *                                         'nicename', 'email', 'url'. Default empty array.
    524      *     @type string       $orderby         Field to sort the retrieved users by. Accepts 'ID', 'display_name',
    525      *                                         'login', 'nicename', 'email', 'url', 'registered', 'post_count',
    526      *                                         'meta_value' or 'meta_value_num'. To use 'meta_value' or
    527      *                                         'meta_value_num', `$meta_key` must be also be defined.
    528      *                                         Default 'user_login'.
    529      *     @type string       $order           Designates ascending or descending order of users. Accepts 'ASC',
    530      *                                         'DESC'. Default 'ASC'.
     525     *     @type string|array $orderby         Field to sort the retrieved users by. May be a single value,
     526     *                                     an array of values, or a multi-dimensional array with fields as keys
     527     *                                     and orders ('ASC' or 'DESC') as values. Accepted values are'ID',
     528     *                                     'display_name' (or 'name'), 'user_login' (or 'login'),
     529     *                                     'user_nicename' (or 'nicename'), 'user_email' (or 'email'),
     530     *                                     'user_url' (or 'url'), 'user_registered' (or 'registered'),
     531     *                                     'post_count', 'meta_value', or 'meta_value_num'. To use 'meta_value'
     532     *                                     or 'meta_value_num', `$meta_key` must be also be defined.
     533     *                                     Default 'user_login'.
     534     *     @type string       $order           Designates ascending or descending order of users. Order values
     535     *                                         passed as part of an `$orderby` array take precedence over this
     536     *                                         parameter. Accepts 'ASC', 'DESC'. Default 'ASC'.
    531537     *     @type int          $offset          Number of users to offset in retrieved results. Can be used in
    532538     *                                         conjunction with pagination. Default 0.
     
    612618
    613619        // sorting
    614         if ( isset( $qv['orderby'] ) ) {
    615             if ( in_array( $qv['orderby'], array('nicename', 'email', 'url', 'registered') ) ) {
    616                 $orderby = 'user_' . $qv['orderby'];
    617             } elseif ( in_array( $qv['orderby'], array('user_nicename', 'user_email', 'user_url', 'user_registered') ) ) {
    618                 $orderby = $qv['orderby'];
    619             } elseif ( 'name' == $qv['orderby'] || 'display_name' == $qv['orderby'] ) {
    620                 $orderby = 'display_name';
    621             } elseif ( 'post_count' == $qv['orderby'] ) {
    622                 // todo: avoid the JOIN
    623                 $where = get_posts_by_author_sql('post');
    624                 $this->query_from .= " LEFT OUTER JOIN (
    625                     SELECT post_author, COUNT(*) as post_count
    626                     FROM $wpdb->posts
    627                     $where
    628                     GROUP BY post_author
    629                 ) p ON ({$wpdb->users}.ID = p.post_author)
    630                 ";
    631                 $orderby = 'post_count';
    632             } elseif ( 'ID' == $qv['orderby'] || 'id' == $qv['orderby'] ) {
    633                 $orderby = 'ID';
    634             } elseif ( 'meta_value' == $qv['orderby'] ) {
    635                 $orderby = "$wpdb->usermeta.meta_value";
    636             } elseif ( 'meta_value_num' == $qv['orderby'] ) {
    637                 $orderby = "$wpdb->usermeta.meta_value+0";
    638             } elseif ( 'include' === $qv['orderby'] && ! empty( $include ) ) {
    639                 // Sanitized earlier.
    640                 $include_sql = implode( ',', $include );
    641                 $orderby = "FIELD( $wpdb->users.ID, $include_sql )";
     620        $qv['order'] = isset( $qv['order'] ) ? strtoupper( $qv['order'] ) : '';
     621        $order = $this->parse_order( $qv['order'] );
     622
     623        if ( empty( $qv['orderby'] ) ) {
     624            // Default order is by 'user_login'.
     625            $ordersby = array( 'user_login' => $order );
     626        } else if ( is_array( $qv['orderby'] ) ) {
     627            $ordersby = $qv['orderby'];
     628        } else {
     629            // 'orderby' values may be a comma- or space-separated list.
     630            $ordersby = preg_split( '/[,\s]+/', $qv['orderby'] );
     631        }
     632
     633        $orderby_array = array();
     634        foreach ( $ordersby as $_key => $_value ) {
     635            if ( ! $_value ) {
     636                continue;
     637            }
     638
     639            if ( is_int( $_key ) ) {
     640                // Integer key means this is a flat array of 'orderby' fields.
     641                $_orderby = $_value;
     642                $_order = $order;
    642643            } else {
    643                 $orderby = 'user_login';
     644                // Non-integer key means this the key is the field and the value is ASC/DESC.
     645                $_orderby = $_key;
     646                $_order = $_value;
    644647            }
    645         }
    646 
    647         if ( empty( $orderby ) )
    648             $orderby = 'user_login';
    649 
    650         $qv['order'] = isset( $qv['order'] ) ? strtoupper( $qv['order'] ) : '';
    651         if ( 'ASC' == $qv['order'] )
    652             $order = 'ASC';
    653         else
    654             $order = 'DESC';
    655         $this->query_orderby = "ORDER BY $orderby $order";
     648
     649            $parsed = $this->parse_orderby( $_orderby );
     650
     651            if ( ! $parsed ) {
     652                continue;
     653            }
     654
     655            $orderby_array[] = $parsed . ' ' . $this->parse_order( $_order );
     656        }
     657
     658        // If no valid clauses were found, order by user_login.
     659        if ( empty( $orderby_array ) ) {
     660            $orderby_array[] = "user_login $order";
     661        }
     662
     663        $this->query_orderby = 'ORDER BY ' . implode( ', ', $orderby_array );
    656664
    657665        // limit
     
    923931    public function get_total() {
    924932        return $this->total_users;
     933    }
     934
     935    /**
     936     * Parse and sanitize 'orderby' keys passed to the user query.
     937     *
     938     * @since 4.2.0
     939     * @access protected
     940     *
     941     * @global wpdb $wpdb WordPress database abstraction object.
     942     *
     943     * @param string $orderby Alias for the field to order by.
     944     * @return string|bool Value to used in the ORDER clause, if `$orderby` is valid. False otherwise.
     945     */
     946    protected function parse_orderby( $orderby ) {
     947        global $wpdb;
     948
     949        $_orderby = '';
     950        if ( in_array( $orderby, array( 'login', 'nicename', 'email', 'url', 'registered' ) ) ) {
     951            $_orderby = 'user_' . $orderby;
     952        } elseif ( in_array( $orderby, array( 'user_login', 'user_nicename', 'user_email', 'user_url', 'user_registered' ) ) ) {
     953            $_orderby = $orderby;
     954        } elseif ( 'name' == $orderby || 'display_name' == $orderby ) {
     955            $_orderby = 'display_name';
     956        } elseif ( 'post_count' == $orderby ) {
     957            // todo: avoid the JOIN
     958            $where = get_posts_by_author_sql( 'post' );
     959            $this->query_from .= " LEFT OUTER JOIN (
     960                SELECT post_author, COUNT(*) as post_count
     961                FROM $wpdb->posts
     962                $where
     963                GROUP BY post_author
     964            ) p ON ({$wpdb->users}.ID = p.post_author)
     965            ";
     966            $_orderby = 'post_count';
     967        } elseif ( 'ID' == $orderby || 'id' == $orderby ) {
     968            $_orderby = 'ID';
     969        } elseif ( 'meta_value' == $orderby ) {
     970            $_orderby = "$wpdb->usermeta.meta_value";
     971        } elseif ( 'meta_value_num' == $orderby ) {
     972            $_orderby = "$wpdb->usermeta.meta_value+0";
     973        } elseif ( 'include' === $orderby && ! empty( $this->query_vars['include'] ) ) {
     974            $include = wp_parse_id_list( $this->query_vars['include'] );
     975            $include_sql = implode( ',', $include );
     976            $_orderby = "FIELD( $wpdb->users.ID, $include_sql )";
     977        }
     978
     979        return $_orderby;
     980    }
     981
     982    /**
     983     * Parse an 'order' query variable and cast it to ASC or DESC as necessary.
     984     *
     985     * @since 4.2.0
     986     * @access protected
     987     *
     988     * @param string $order The 'order' query variable.
     989     * @return string The sanitized 'order' query variable.
     990     */
     991    protected function parse_order( $order ) {
     992        if ( ! is_string( $order ) || empty( $order ) ) {
     993            return 'DESC';
     994        }
     995
     996        if ( 'ASC' === strtoupper( $order ) ) {
     997            return 'ASC';
     998        } else {
     999            return 'DESC';
     1000        }
    9251001    }
    9261002
  • trunk/tests/phpunit/tests/user/query.php

    r31662 r31663  
    229229        // assertEquals() respects order but ignores type (get_results() returns numeric strings).
    230230        $this->assertEquals( array( $users[1], $users[0], $users[3] ), $q->get_results() );
     231    }
     232
     233    /**
     234     * @ticket 31265
     235     */
     236    public function test_orderby_space_separated() {
     237        global $wpdb;
     238
     239        $q = new WP_User_Query( array(
     240            'orderby' => 'login nicename',
     241            'order' => 'ASC',
     242        ) );
     243
     244        $this->assertContains( "ORDER BY user_login ASC, user_nicename ASC", $q->query_orderby );
     245    }
     246
     247    /**
     248     * @ticket 31265
     249     */
     250    public function test_orderby_flat_array() {
     251        global $wpdb;
     252
     253        $q = new WP_User_Query( array(
     254            'orderby' => array( 'login', 'nicename' ),
     255        ) );
     256
     257        $this->assertContains( "ORDER BY user_login ASC, user_nicename ASC", $q->query_orderby );
     258    }
     259
     260    /**
     261     * @ticket 31265
     262     */
     263    public function test_orderby_array_contains_invalid_item() {
     264        global $wpdb;
     265
     266        $q = new WP_User_Query( array(
     267            'orderby' => array( 'login', 'foo', 'nicename' ),
     268        ) );
     269
     270        $this->assertContains( "ORDER BY user_login ASC, user_nicename ASC", $q->query_orderby );
     271    }
     272
     273    /**
     274     * @ticket 31265
     275     */
     276    public function test_orderby_array_contains_all_invalid_items() {
     277        global $wpdb;
     278
     279        $q = new WP_User_Query( array(
     280            'orderby' => array( 'foo', 'bar', 'baz' ),
     281        ) );
     282
     283        $this->assertContains( "ORDER BY user_login", $q->query_orderby );
     284    }
     285
     286    /**
     287     * @ticket 31265
     288     */
     289    public function test_orderby_array() {
     290        global $wpdb;
     291
     292        $q = new WP_User_Query( array(
     293            'orderby' => array(
     294                'login' => 'DESC',
     295                'nicename' => 'ASC',
     296                'email' => 'DESC',
     297            ),
     298        ) );
     299
     300        $this->assertContains( "ORDER BY user_login DESC, user_nicename ASC, user_email DESC", $q->query_orderby );
     301    }
     302
     303    /**
     304     * @ticket 31265
     305     */
     306    public function test_orderby_array_should_discard_invalid_columns() {
     307        global $wpdb;
     308
     309        $q = new WP_User_Query( array(
     310            'orderby' => array(
     311                'login' => 'DESC',
     312                'foo' => 'ASC',
     313                'email' => 'ASC',
     314            ),
     315        ) );
     316
     317        $this->assertContains( "ORDER BY user_login DESC, user_email ASC", $q->query_orderby );
    231318    }
    232319
Note: See TracChangeset for help on using the changeset viewer.