WordPress.org

Make WordPress Core

Changeset 37692


Ignore:
Timestamp:
06/14/2016 01:59:25 AM (3 years ago)
Author:
boonebgorges
Message:

Query: Allow plugins to supply post results instead of having WP_Query fetch them from the database.

Returning a non-null value from the new posts_pre_query filter will cause
WP_Query to skip its database query, so that posts data can be provided from
elsewhere. This is useful in cases where post data may be mirrored in a
separate location, such as an external search application.

Developers should note that the WP_Query properties generally used to
calculate pagination - specifically, found_posts and max_num_pages, which
are determined by default in set_found_posts() - must be provided explicitly
when using the posts_pre_query filter; since WP_Query will not be
contacting the database, it will have no access to SELECT FOUND_ROWS().
The WP_Query instance is passed to posts_pre_query by reference, so that
these properties can be set manually if needed.

Props jpdavoutian, tlovett1.
Fixes #36687.

Location:
trunk
Files:
2 edited

Legend:

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

    r37589 r37692  
    35573557        }
    35583558
     3559        /**
     3560         * Filters the posts array before the query takes place.
     3561         *
     3562         * Return a non-null value to bypass WordPress's default post queries.
     3563         *
     3564         * Filtering functions that require pagination information are encouraged to set
     3565         * the `found_posts` and `max_num_pages` properties of the WP_Query object,
     3566         * passed to the filter by reference. If WP_Query does not perform a database
     3567         * query, it will not have enough information to generate these values itself.
     3568         *
     3569         * @since 4.6.0
     3570         *
     3571         * @param array|null $posts Return an array of post data to short-circuit WP's query,
     3572         *                          or null to allow WP to run its normal queries.
     3573         * @param WP_Query   $this  The WP_Query instance, passed by reference.
     3574         */
     3575        $this->posts = apply_filters_ref_array( 'posts_pre_query', array( null, &$this ) );
     3576
    35593577        if ( 'ids' == $q['fields'] ) {
    3560             $this->posts = $wpdb->get_col( $this->request );
     3578            if ( null === $this->posts ) {
     3579                $this->posts = $wpdb->get_col( $this->request );
     3580            }
     3581
    35613582            $this->posts = array_map( 'intval', $this->posts );
    35623583            $this->post_count = count( $this->posts );
     
    35673588
    35683589        if ( 'id=>parent' == $q['fields'] ) {
    3569             $this->posts = $wpdb->get_results( $this->request );
     3590            if ( null === $this->posts ) {
     3591                $this->posts = $wpdb->get_results( $this->request );
     3592            }
     3593
    35703594            $this->post_count = count( $this->posts );
    35713595            $this->set_found_posts( $q, $limits );
     
    35823606        }
    35833607
    3584         $split_the_query = ( $old_request == $this->request && "$wpdb->posts.*" == $fields && !empty( $limits ) && $q['posts_per_page'] < 500 );
    3585 
    3586         /**
    3587          * Filters whether to split the query.
    3588          *
    3589          * Splitting the query will cause it to fetch just the IDs of the found posts
    3590          * (and then individually fetch each post by ID), rather than fetching every
    3591          * complete row at once. One massive result vs. many small results.
    3592          *
    3593          * @since 3.4.0
    3594          *
    3595          * @param bool     $split_the_query Whether or not to split the query.
    3596          * @param WP_Query $this            The WP_Query instance.
    3597          */
    3598         $split_the_query = apply_filters( 'split_the_query', $split_the_query, $this );
    3599 
    3600         if ( $split_the_query ) {
    3601             // First get the IDs and then fill in the objects
    3602 
    3603             $this->request = "SELECT $found_rows $distinct $wpdb->posts.ID FROM $wpdb->posts $join WHERE 1=1 $where $groupby $orderby $limits";
     3608        if ( null === $this->posts ) {
     3609            $split_the_query = ( $old_request == $this->request && "$wpdb->posts.*" == $fields && !empty( $limits ) && $q['posts_per_page'] < 500 );
    36043610
    36053611            /**
    3606              * Filters the Post IDs SQL request before sending.
     3612             * Filters whether to split the query.
     3613             *
     3614             * Splitting the query will cause it to fetch just the IDs of the found posts
     3615             * (and then individually fetch each post by ID), rather than fetching every
     3616             * complete row at once. One massive result vs. many small results.
    36073617             *
    36083618             * @since 3.4.0
    36093619             *
    3610              * @param string   $request The post ID request.
    3611              * @param WP_Query $this    The WP_Query instance.
     3620             * @param bool     $split_the_query Whether or not to split the query.
     3621             * @param WP_Query $this            The WP_Query instance.
    36123622             */
    3613             $this->request = apply_filters( 'posts_request_ids', $this->request, $this );
    3614 
    3615             $ids = $wpdb->get_col( $this->request );
    3616 
    3617             if ( $ids ) {
    3618                 $this->posts = $ids;
    3619                 $this->set_found_posts( $q, $limits );
    3620                 _prime_post_caches( $ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
     3623            $split_the_query = apply_filters( 'split_the_query', $split_the_query, $this );
     3624
     3625            if ( $split_the_query ) {
     3626                // First get the IDs and then fill in the objects
     3627
     3628                $this->request = "SELECT $found_rows $distinct $wpdb->posts.ID FROM $wpdb->posts $join WHERE 1=1 $where $groupby $orderby $limits";
     3629
     3630                /**
     3631                 * Filters the Post IDs SQL request before sending.
     3632                 *
     3633                 * @since 3.4.0
     3634                 *
     3635                 * @param string   $request The post ID request.
     3636                 * @param WP_Query $this    The WP_Query instance.
     3637                 */
     3638                $this->request = apply_filters( 'posts_request_ids', $this->request, $this );
     3639
     3640                $ids = $wpdb->get_col( $this->request );
     3641
     3642                if ( $ids ) {
     3643                    $this->posts = $ids;
     3644                    _prime_post_caches( $ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
     3645                } else {
     3646                    $this->posts = array();
     3647                }
    36213648            } else {
    3622                 $this->posts = array();
    3623             }
    3624         } else {
    3625             $this->posts = $wpdb->get_results( $this->request );
     3649                $this->posts = $wpdb->get_results( $this->request );
     3650            }
     3651        }
     3652
     3653        // Convert to WP_Post objects and set the found-post totals.
     3654        if ( $this->posts ) {
     3655            $this->posts = array_map( 'get_post', $this->posts );
    36263656            $this->set_found_posts( $q, $limits );
    36273657        }
    3628 
    3629         // Convert to WP_Post objects
    3630         if ( $this->posts )
    3631             $this->posts = array_map( 'get_post', $this->posts );
    36323658
    36333659        if ( ! $q['suppress_filters'] ) {
  • trunk/tests/phpunit/tests/post/query.php

    r37225 r37692  
    389389        $this->assertEqualSets( $requested, $actual_posts );
    390390    }
     391
     392    /**
     393     * @ticket 36687
     394     */
     395    public function test_posts_pre_query_filter_should_bypass_database_query() {
     396        global $wpdb;
     397
     398        add_filter( 'posts_pre_query', array( __CLASS__, 'filter_posts_pre_query' ) );
     399
     400        $num_queries = $wpdb->num_queries;
     401        $q = new WP_Query( array(
     402            'fields' => 'ids',
     403            'no_found_rows' => true,
     404        ) );
     405
     406        remove_filter( 'posts_pre_query', array( __CLASS__, 'filter_posts_pre_query' ) );
     407
     408        $this->assertSame( $num_queries, $wpdb->num_queries );
     409        $this->assertSame( array( 12345 ), $q->posts );
     410    }
     411
     412    public static function filter_posts_pre_query( $posts ) {
     413        return array( 12345 );
     414    }
    391415}
Note: See TracChangeset for help on using the changeset viewer.