Make WordPress Core

Changeset 29027


Ignore:
Timestamp:
07/08/2014 05:15:53 PM (10 years ago)
Author:
wonderboymusic
Message:

Allow an array() to be passed as the value for orderby to WP_Query. Allows for an independent order value for each key.

Example: 'orderby' => array( 'title' => 'DESC', 'menu_order' => 'ASC' ).

Adds docs and unit tests.

Props wonderboymusic, johnbillion, DrewAPicture, dd32, andy.
See #17065.

Location:
trunk
Files:
2 edited

Legend:

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

    r28987 r29027  
    22012201
    22022202    /**
     2203     * If the passed orderby value is allowed, convert the alias to a
     2204     * properly-prefixed orderby value.
     2205     *
     2206     * @since 4.0.0
     2207     * @access protected
     2208     *
     2209     * @global wpdb $wpdb WordPress database access abstraction object.
     2210     *
     2211     * @param string $orderby Alias for the field to order by.
     2212     * @return string|bool Table-prefixed value to used in the ORDER clause. False otherwise.
     2213     */
     2214    protected function parse_orderby( $orderby ) {
     2215        global $wpdb;
     2216
     2217        // Used to filter values.
     2218        $allowed_keys = array(
     2219            'post_name', 'post_author', 'post_date', 'post_title', 'post_modified',
     2220            'post_parent', 'post_type', 'name', 'author', 'date', 'title', 'modified',
     2221            'parent', 'type', 'ID', 'menu_order', 'comment_count', 'rand',
     2222        );
     2223
     2224        $meta_key = $this->get( 'meta_key' );
     2225        if ( ! empty( $meta_key ) ) {
     2226            $allowed_keys[] = $meta_key;
     2227            $allowed_keys[] = 'meta_value';
     2228            $allowed_keys[] = 'meta_value_num';
     2229        }
     2230
     2231        if ( ! in_array( $orderby, $allowed_keys ) ) {
     2232            return false;
     2233        }
     2234
     2235        switch ( $orderby ) {
     2236            case 'post_name':
     2237            case 'post_author':
     2238            case 'post_date':
     2239            case 'post_title':
     2240            case 'post_modified':
     2241            case 'post_parent':
     2242            case 'post_type':
     2243            case 'ID':
     2244            case 'menu_order':
     2245            case 'comment_count':
     2246                $orderby = "$wpdb->posts.{$orderby}";
     2247                break;
     2248            case 'rand':
     2249                $orderby = 'RAND()';
     2250                break;
     2251            case $meta_key:
     2252            case 'meta_value':
     2253                $type = $this->get( 'meta_type' );
     2254                if ( ! empty( $type ) ) {
     2255                    $meta_type = $this->meta_query->get_cast_for_type( $type );
     2256                    $orderby = "CAST($wpdb->postmeta.meta_value AS {$meta_type})";
     2257                } else {
     2258                    $orderby = "$wpdb->postmeta.meta_value";
     2259                }
     2260                break;
     2261            case 'meta_value_num':
     2262                $orderby = "$wpdb->postmeta.meta_value+0";
     2263                break;
     2264            default:
     2265                $orderby = "$wpdb->posts.post_" . $orderby;
     2266                break;
     2267        }
     2268
     2269        return $orderby;
     2270    }
     2271
     2272    /**
     2273     * Parse an 'order' query variable and cast it to ASC or DESC as necessary.
     2274     *
     2275     * @since 4.0.0
     2276     * @access protected
     2277     *
     2278     * @param string $order The 'order' query variable.
     2279     * @return string The sanitized 'order' query variable.
     2280     */
     2281    protected function parse_order( $order ) {
     2282        if ( ! is_string( $order ) || empty( $order ) ) {
     2283            return 'DESC';
     2284        }
     2285
     2286        if ( 'ASC' === strtoupper( $order ) ) {
     2287            return 'ASC';
     2288        } else {
     2289            return 'DESC';
     2290        }
     2291    }
     2292
     2293    /**
    22032294     * Sets the 404 property and saves whether query is feed.
    22042295     *
     
    27032794        $where .= $search . $whichauthor . $whichmimetype;
    27042795
    2705         if ( empty($q['order']) || ((strtoupper($q['order']) != 'ASC') && (strtoupper($q['order']) != 'DESC')) )
     2796        if ( ! isset( $q['order'] ) ) {
    27062797            $q['order'] = 'DESC';
    2707 
    2708         // Order by
    2709         if ( empty($q['orderby']) ) {
    2710             $orderby = "$wpdb->posts.post_date " . $q['order'];
     2798        } else {
     2799            $q['order'] = $this->parse_order( $q['order'] );
     2800        }
     2801
     2802        // Order by.
     2803        if ( empty( $q['orderby'] ) ) {
     2804            /*
     2805             * Boolean false or empty array blanks out ORDER BY,
     2806             * while leaving the value unset or otherwise empty sets the default.
     2807             */
     2808            if ( isset( $q['orderby'] ) && ( is_array( $q['orderby'] ) || false === $q['orderby'] ) ) {
     2809                $orderby = '';
     2810            } else {
     2811                $orderby = "$wpdb->posts.post_date " . $q['order'];
     2812            }
    27112813        } elseif ( 'none' == $q['orderby'] ) {
    27122814            $orderby = '';
     
    27162818            $orderby = "FIELD( {$wpdb->posts}.post_parent, $post_parent__in )";
    27172819        } else {
    2718             // Used to filter values
    2719             $allowed_keys = array( 'name', 'author', 'date', 'title', 'modified', 'menu_order', 'parent', 'ID', 'rand', 'comment_count', 'type' );
    2720             if ( !empty($q['meta_key']) ) {
    2721                 $allowed_keys[] = $q['meta_key'];
    2722                 $allowed_keys[] = 'meta_value';
    2723                 $allowed_keys[] = 'meta_value_num';
    2724             }
    2725             $q['orderby'] = urldecode($q['orderby']);
    2726             $q['orderby'] = addslashes_gpc($q['orderby']);
    2727 
    27282820            $orderby_array = array();
    2729             foreach ( explode( ' ', $q['orderby'] ) as $i => $orderby ) {
    2730                 // Only allow certain values for safety
    2731                 if ( ! in_array($orderby, $allowed_keys) )
    2732                     continue;
    2733 
    2734                 switch ( $orderby ) {
    2735                     case 'menu_order':
    2736                         $orderby = "$wpdb->posts.menu_order";
    2737                         break;
    2738                     case 'ID':
    2739                         $orderby = "$wpdb->posts.ID";
    2740                         break;
    2741                     case 'rand':
    2742                         $orderby = 'RAND()';
    2743                         break;
    2744                     case $q['meta_key']:
    2745                     case 'meta_value':
    2746                         if ( isset( $q['meta_type'] ) ) {
    2747                             $meta_type = $this->meta_query->get_cast_for_type( $q['meta_type'] );
    2748                             $orderby = "CAST($wpdb->postmeta.meta_value AS {$meta_type})";
    2749                         } else {
    2750                             $orderby = "$wpdb->postmeta.meta_value";
    2751                         }
    2752                         break;
    2753                     case 'meta_value_num':
    2754                         $orderby = "$wpdb->postmeta.meta_value+0";
    2755                         break;
    2756                     case 'comment_count':
    2757                         $orderby = "$wpdb->posts.comment_count";
    2758                         break;
    2759                     default:
    2760                         $orderby = "$wpdb->posts.post_" . $orderby;
     2821            if ( is_array( $q['orderby'] ) ) {
     2822                foreach ( $q['orderby'] as $_orderby => $order ) {
     2823                    $orderby = addslashes_gpc( urldecode( $_orderby ) );
     2824                    $parsed  = $this->parse_orderby( $orderby );
     2825
     2826                    if ( ! $parsed ) {
     2827                        continue;
     2828                    }
     2829
     2830                    $orderby_array[] = $parsed . ' ' . $this->parse_order( $order );
    27612831                }
    2762 
    2763                 $orderby_array[] = $orderby;
    2764             }
    2765             $orderby = implode( ' ' . $q['order'] . ', ', $orderby_array );
    2766 
    2767             if ( empty( $orderby ) )
    2768                 $orderby = "$wpdb->posts.post_date ".$q['order'];
    2769             else
    2770                 $orderby .= " {$q['order']}";
     2832                $orderby = implode( ', ', $orderby_array );
     2833
     2834            } else {
     2835                $q['orderby'] = urldecode( $q['orderby'] );
     2836                $q['orderby'] = addslashes_gpc( $q['orderby'] );
     2837
     2838                foreach ( explode( ' ', $q['orderby'] ) as $i => $orderby ) {
     2839                    $parsed = $this->parse_orderby( $orderby );
     2840                    // Only allow certain values for safety.
     2841                    if ( ! $parsed ) {
     2842                        continue;
     2843                    }
     2844
     2845                    $orderby_array[] = $parsed;
     2846                }
     2847                $orderby = implode( ' ' . $q['order'] . ', ', $orderby_array );
     2848
     2849                if ( empty( $orderby ) ) {
     2850                    $orderby = "$wpdb->posts.post_date ".$q['order'];
     2851                } else {
     2852                    $orderby .= " {$q['order']}";
     2853                }
     2854            }
    27712855        }
    27722856
  • trunk/tests/phpunit/tests/post/query.php

    r28622 r29027  
    762762        $this->assertNotContains( "post_status <> 'auto-draft'", $q3->request );
    763763    }
     764
     765    /**
     766     *
     767     * @ticket 17065
     768     */
     769    function test_orderby_array() {
     770        global $wpdb;
     771
     772        $q1 = new WP_Query( array(
     773            'orderby' => array(
     774                'type' => 'DESC',
     775                'name' => 'ASC'
     776            )
     777        ) );
     778        $this->assertContains(
     779            "ORDER BY $wpdb->posts.post_type DESC, $wpdb->posts.post_name ASC",
     780            $q1->request
     781        );
     782
     783        $q2 = new WP_Query( array( 'orderby' => array() ) );
     784        $this->assertNotContains( 'ORDER BY', $q2->request );
     785        $this->assertNotContains( 'ORDER', $q2->request );
     786
     787        $q3 = new WP_Query( array( 'post_type' => 'post' ) );
     788        $this->assertContains(
     789            "ORDER BY $wpdb->posts.post_date DESC",
     790            $q3->request
     791        );
     792
     793        $q4 = new WP_Query( array( 'post_type' => 'post' ) );
     794        $this->assertContains(
     795            "ORDER BY $wpdb->posts.post_date DESC",
     796            $q4->request
     797        );
     798    }
     799
     800    /**
     801     *
     802     * @ticket 17065
     803     */
     804    function test_order() {
     805        global $wpdb;
     806
     807        $q1 = new WP_Query( array(
     808            'orderby' => array(
     809                'post_type' => 'foo'
     810            )
     811        ) );
     812        $this->assertContains(
     813            "ORDER BY $wpdb->posts.post_type DESC",
     814            $q1->request
     815        );
     816
     817        $q2 = new WP_Query( array(
     818            'orderby' => 'title',
     819            'order'   => 'foo'
     820        ) );
     821        $this->assertContains(
     822            "ORDER BY $wpdb->posts.post_title DESC",
     823            $q2->request
     824        );
     825
     826        $q3 = new WP_Query( array(
     827            'order' => 'asc'
     828        ) );
     829        $this->assertContains(
     830            "ORDER BY $wpdb->posts.post_date ASC",
     831            $q3->request
     832        );
     833    }
    764834}
Note: See TracChangeset for help on using the changeset viewer.