Make WordPress Core


Ignore:
Timestamp:
02/27/2017 07:53:43 PM (10 years ago)
Author:
ocean90
Message:

REST API: Fix behavior of sticky posts filter when no posts are sticky.

Previously, when getting posts from the API with sticky=true, if there were no sticky posts set, the query would return all posts as if the sticky argument was not set. In this situation, the query should return an empty array instead.

A sticky=true query that should return an empty array (in the previous situation, or with include and no intersecting post IDs) was also broken in that it would query the post with ID 1.

Finally, this commit significantly improves test coverage for the sticky filter argument, including direct testing of the WHERE clauses generated by WP_Query.

Merge of [40037] and [40122] to the 4.7 branch.

Props ryelle, jnylen0.
See #39079.
Fixes #39947.

Location:
branches/4.7
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • branches/4.7

  • branches/4.7/tests/phpunit/tests/rest-api/rest-posts-controller.php

    r40115 r40136  
    2121
    2222        protected $forbidden_cat;
     23        protected $posts_clauses;
    2324
    2425        public static function wpSetUpBeforeClass( $factory ) {
    … …  
    6667                parent::setUp();
    6768                register_post_type( 'youseeme', array( 'supports' => array(), 'show_in_rest' => true ) );
     69                add_filter( 'rest_pre_dispatch', array( $this, 'wpSetUpBeforeRequest' ), 10, 3 );
     70                add_filter( 'posts_clauses', array( $this, 'save_posts_clauses' ), 10, 2 );
     71        }
     72
     73        public function wpSetUpBeforeRequest( $result, $server, $request ) {
     74                $this->posts_clauses = array();
     75                return $result;
     76        }
     77
     78        public function save_posts_clauses( $orderby, $query ) {
     79                array_push( $this->posts_clauses, $orderby );
     80                return $orderby;
     81        }
     82
     83        public function assertPostsClause( $clause, $pattern ) {
     84                global $wpdb;
     85                $expected_clause = str_replace( '{posts}', $wpdb->posts, $pattern );
     86                $this->assertCount( 1, $this->posts_clauses );
     87                $this->assertEquals( $expected_clause, $this->posts_clauses[0][ $clause ] );
     88        }
     89
     90        public function assertPostsOrderedBy( $pattern ) {
     91                $this->assertPostsClause( 'orderby', $pattern );
     92        }
     93
     94        public function assertPostsWhere( $pattern ) {
     95                $this->assertPostsClause( 'where', $pattern );
    6896        }
    6997
    … …  
    224252                $this->assertEquals( 2, count( $data ) );
    225253                $this->assertEquals( $id3, $data[0]['id'] );
     254                $this->assertPostsOrderedBy( '{posts}.post_date DESC' );
    226255                // Orderby=>include
    227256                $request->set_param( 'orderby', 'include' );
    … …  
    230259                $this->assertEquals( 2, count( $data ) );
    231260                $this->assertEquals( $id1, $data[0]['id'] );
     261                $this->assertPostsOrderedBy( "FIELD( {posts}.ID, $id1,$id3 )" );
    232262                // Invalid include should error
    233263                $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
    … …  
    438468                $data = $response->get_data();
    439469                $this->assertEquals( 'Apple Sauce', $data[0]['title']['rendered'] );
     470                $this->assertPostsOrderedBy( '{posts}.post_title DESC' );
    440471                // order=>asc
    441472                $request->set_param( 'order', 'asc' );
    … …  
    443474                $data = $response->get_data();
    444475                $this->assertEquals( 'Apple Cobbler', $data[0]['title']['rendered'] );
     476                $this->assertPostsOrderedBy( '{posts}.post_title ASC' );
    445477                // order=>asc,id should fail
    446478                $request->set_param( 'order', 'asc,id' );
    … …  
    480512                $this->assertEquals( $id2, $data[1]['id'] );
    481513                $this->assertEquals( $id1, $data[2]['id'] );
     514                $this->assertPostsOrderedBy( '{posts}.ID DESC' );
    482515        }
    483516
    … …  
    496529                $this->assertEquals( 'xyz', $data[0]['slug'] );
    497530                $this->assertEquals( 'abc', $data[1]['slug'] );
     531                $this->assertPostsOrderedBy( '{posts}.post_name DESC' );
    498532        }
    499533
    500534        public function test_get_items_with_orderby_relevance() {
    501                 $this->factory->post->create( array( 'post_title' => 'Title is more relevant', 'post_content' => 'Content is', 'post_status' => 'publish' ) );
    502                 $this->factory->post->create( array( 'post_title' => 'Title is', 'post_content' => 'Content is less relevant', 'post_status' => 'publish' ) );
    503 
     535                $id1 = $this->factory->post->create( array( 'post_title' => 'Title is more relevant', 'post_content' => 'Content is', 'post_status' => 'publish' ) );
     536                $id2 = $this->factory->post->create( array( 'post_title' => 'Title is', 'post_content' => 'Content is less relevant', 'post_status' => 'publish' ) );
     537                $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     538                $request->set_param( 'orderby', 'relevance' );
     539                $request->set_param( 'search', 'relevant' );
     540                $response = $this->server->dispatch( $request );
     541                $this->assertEquals( 200, $response->get_status() );
     542                $data = $response->get_data();
     543                $this->assertCount( 2, $data );
     544                $this->assertEquals( $id1, $data[0]['id'] );
     545                $this->assertEquals( $id2, $data[1]['id'] );
     546                $this->assertPostsOrderedBy( '{posts}.post_title LIKE \'%relevant%\' DESC, {posts}.post_date DESC' );
     547        }
     548
     549        public function test_get_items_with_orderby_relevance_two_terms() {
     550                $id1 = $this->factory->post->create( array( 'post_title' => 'Title is more relevant', 'post_content' => 'Content is', 'post_status' => 'publish' ) );
     551                $id2 = $this->factory->post->create( array( 'post_title' => 'Title is', 'post_content' => 'Content is less relevant', 'post_status' => 'publish' ) );
     552                $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     553                $request->set_param( 'orderby', 'relevance' );
     554                $request->set_param( 'search', 'relevant content' );
     555                $response = $this->server->dispatch( $request );
     556                $this->assertEquals( 200, $response->get_status() );
     557                $data = $response->get_data();
     558                $this->assertCount( 2, $data );
     559                $this->assertEquals( $id1, $data[0]['id'] );
     560                $this->assertEquals( $id2, $data[1]['id'] );
     561                $this->assertPostsOrderedBy( '(CASE WHEN {posts}.post_title LIKE \'%relevant content%\' THEN 1 WHEN {posts}.post_title LIKE \'%relevant%\' AND {posts}.post_title LIKE \'%content%\' THEN 2 WHEN {posts}.post_title LIKE \'%relevant%\' OR {posts}.post_title LIKE \'%content%\' THEN 3 WHEN {posts}.post_excerpt LIKE \'%relevant content%\' THEN 4 WHEN {posts}.post_content LIKE \'%relevant content%\' THEN 5 ELSE 6 END), {posts}.post_date DESC' );
     562        }
     563
     564        public function test_get_items_with_orderby_relevance_missing_search() {
    504565                $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
    505566                $request->set_param( 'orderby', 'relevance' );
    … …  
    638699        }
    639700
    640         public function test_get_items_sticky_query() {
     701        public function test_get_items_sticky() {
    641702                $id1 = self::$post_id;
    642703                $id2 = $this->factory->post->create( array( 'post_status' => 'publish' ) );
    … …  
    659720        }
    660721
    661         public function test_get_items_sticky_with_post__in_query() {
     722        public function test_get_items_sticky_with_include() {
    662723                $id1 = self::$post_id;
    663724                $id2 = $this->factory->post->create( array( 'post_status' => 'publish' ) );
    … …  
    673734                $this->assertCount( 0, $response->get_data() );
    674735
     736                // FIXME Since this request returns zero posts, the query is executed twice.
     737                $this->assertCount( 2, $this->posts_clauses );
     738                $this->posts_clauses = array_slice( $this->posts_clauses, 0, 1 );
     739
     740                $this->assertPostsWhere( " AND {posts}.ID IN (0) AND {posts}.post_type = 'post' AND (({posts}.post_status = 'publish'))" );
     741
    675742                update_option( 'sticky_posts', array( $id1, $id2 ) );
    676743
    … …  
    686753                $post = $posts[0];
    687754                $this->assertEquals( $id1, $post['id'] );
    688         }
    689 
    690         public function test_get_items_not_sticky_query() {
     755
     756                $this->assertPostsWhere( " AND {posts}.ID IN ($id1) AND {posts}.post_type = 'post' AND (({posts}.post_status = 'publish'))" );
     757        }
     758
     759        public function test_get_items_sticky_no_sticky_posts() {
     760                $id1 = self::$post_id;
     761
     762                update_option( 'sticky_posts', array() );
     763
     764                $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     765                $request->set_param( 'sticky', true );
     766
     767                $response = $this->server->dispatch( $request );
     768                $this->assertCount( 0, $response->get_data() );
     769
     770                // FIXME Since this request returns zero posts, the query is executed twice.
     771                $this->assertCount( 2, $this->posts_clauses );
     772                $this->posts_clauses = array_slice( $this->posts_clauses, 0, 1 );
     773
     774                $this->assertPostsWhere( " AND {posts}.ID IN (0) AND {posts}.post_type = 'post' AND (({posts}.post_status = 'publish'))" );
     775        }
     776
     777        public function test_get_items_sticky_with_include_no_sticky_posts() {
     778                $id1 = self::$post_id;
     779
     780                update_option( 'sticky_posts', array() );
     781
     782                $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     783                $request->set_param( 'sticky', true );
     784                $request->set_param( 'include', array( $id1 ) );
     785
     786                $response = $this->server->dispatch( $request );
     787                $this->assertCount( 0, $response->get_data() );
     788
     789                // FIXME Since this request returns zero posts, the query is executed twice.
     790                $this->assertCount( 2, $this->posts_clauses );
     791                $this->posts_clauses = array_slice( $this->posts_clauses, 0, 1 );
     792
     793                $this->assertPostsWhere( " AND {posts}.ID IN (0) AND {posts}.post_type = 'post' AND (({posts}.post_status = 'publish'))" );
     794        }
     795
     796        public function test_get_items_not_sticky() {
    691797                $id1 = self::$post_id;
    692798                $id2 = $this->factory->post->create( array( 'post_status' => 'publish' ) );
    … …  
    703809                $post = $posts[0];
    704810                $this->assertEquals( $id1, $post['id'] );
    705         }
    706 
    707         public function test_get_items_sticky_with_post__not_in_query() {
     811
     812                $this->assertPostsWhere( " AND {posts}.ID NOT IN ($id2) AND {posts}.post_type = 'post' AND (({posts}.post_status = 'publish'))" );
     813        }
     814
     815        public function test_get_items_not_sticky_with_exclude() {
    708816                $id1 = self::$post_id;
    709817                $id2 = $this->factory->post->create( array( 'post_status' => 'publish' ) );
    … …  
    722830                $post = $posts[0];
    723831                $this->assertEquals( $id1, $post['id'] );
     832
     833                $this->assertPostsWhere( " AND {posts}.ID NOT IN ($id3,$id2) AND {posts}.post_type = 'post' AND (({posts}.post_status = 'publish'))" );
     834        }
     835
     836        public function test_get_items_not_sticky_with_exclude_no_sticky_posts() {
     837                $id1 = self::$post_id;
     838                $id2 = $this->factory->post->create( array( 'post_status' => 'publish' ) );
     839                $id3 = $this->factory->post->create( array( 'post_status' => 'publish' ) );
     840
     841                update_option( 'sticky_posts', array() );
     842
     843                $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     844                $request->set_param( 'sticky', false );
     845                $request->set_param( 'exclude', array( $id3 ) );
     846
     847                $response = $this->server->dispatch( $request );
     848                $this->assertCount( 2, $response->get_data() );
     849
     850                $posts = $response->get_data();
     851                $ids = wp_list_pluck( $posts, 'id' );
     852                sort( $ids );
     853                $this->assertEquals( array( $id1, $id2 ), $ids );
     854
     855                $this->assertPostsWhere( " AND {posts}.ID NOT IN ($id3) AND {posts}.post_type = 'post' AND (({posts}.post_status = 'publish'))" );
    724856        }
    725857
    … …  
    29183050                        $this->remove_added_uploads();
    29193051                }
     3052                remove_filter( 'rest_pre_dispatch', array( $this, 'wpSetUpBeforeRequest' ), 10, 3 );
     3053                remove_filter( 'posts_clauses', array( $this, 'save_posts_clauses' ), 10, 2 );
    29203054                parent::tearDown();
    29213055        }
Note: See TracChangeset for help on using the changeset viewer.