Skip to content
This repository has been archived by the owner on Sep 24, 2018. It is now read-only.

Since filter has been removed, how to get posts by category slug with same schema as v2/posts? #2949

Closed
kosso opened this issue Dec 7, 2016 · 14 comments

Comments

@kosso
Copy link

kosso commented Dec 7, 2016

Prior to 4.7 we could get posts from a category using:

/wp-json/wp/v2/posts?filter[taxonomy]=category&filter[term]=some-category-slug

This no longer works. So how should we go about doing this now?

Thanks.

@kadamwhite
Copy link
Contributor

kadamwhite commented Dec 7, 2016

The most robust way to handle this would be to first request

/wp/v2/categories?slug=some-category slug

then use the ID of the category you get back in

/wp/v2/posts?categories=some-category-id

Alternatively you can install the new rest-filter plugin, to reinstate filter; or create a plugin to add a query parameter to collections to handle this specific behavior. But the above will work on all sites, whether or not you have the ability to add a plugin to them.

It's true that two requests is less efficient than one, and the additional HTTP request does introduce another point of failure on e.g. an unreliable mobile connection; but relying on the slug is conceptually brittle as slugs are mutable (IDs are the only guaranteed relation), and the slug-to-ID mapping can be cached locally within the client app, removing the need to make the initial query unless the subsequent one fails or you know a slug to have changed.

Hope this helps!

@kosso
Copy link
Author

kosso commented Dec 7, 2016

OK. Thanks! I've written our own plugin to deal with some custom types, so I'll add something to do the query.

You're quite right about the use of slugs though, but our site is built around them staying pretty solid ;)

@kadamwhite
Copy link
Contributor

kadamwhite commented Dec 7, 2016

Depending on your environment a client method could also be created to abstract the requests into one developer-facing call; for example, with the wpapi npm package I could write,

function getPostsByCategorySlug(slug) {
  return site.categories().slug( slug )
    .then(function( matchingCats ) {
      // matchingCats will be an array of the one post that matches
      // the provided slug
      if ( ! matchingCats.length ) {
        throw new Error( `No category found for slug "${slug}"` );
      }
      var catID = matchingCats[ 0 ].id;
      return site.posts().categories(catID);
    });
}

getPostsByCategorySlug( 'some-category-slug' )
  .then(function( posts ) {
    // Do something with your posts
  })
  .catch(function( err ) {
    // Could not get categories
    console.error(err);
  });

@kosso
Copy link
Author

kosso commented Dec 7, 2016

Thanks for that.

But while writing a little plugin for this with PHP, I'm a little confused :

/wp-json/wp/v2/posts?_embed provides the posts JSON in a particular schema with the _embedded and _links data all there.

But in my plugin, after registering the rest route, when I do a get_posts($args) in my callback, I do get JSON back, but it's a representation of the standard WP_Post schema, without the _links or _embedded info.

How do I return the same structure as /wp-json/wp/v2/posts?_embed ?
I thought that maybe setting the WP_REST_Posts_Controller would do it.

Thanks.

Here's what I'm doing in the plugin:

add_action( 'rest_api_init', 
    function () {
            register_rest_route( 'our-namespace/v1', '/posts/(?P<slug>[a-zA-Z0-9-]+)', array(
                'methods' => WP_REST_Server::READABLE,
                'rest_controller_class' => 'WP_REST_Posts_Controller',
                'callback' => 'get_posts_by_category_slug',
                'show_in_rest' => true
            )
        );
    }
);

function get_posts_by_category_slug( $request ) {
    $cat = get_category_by_slug( $request['slug'] );
    $posts = get_posts( 
        array( 
            'post_type' => 'post',
            'posts_per_page' => 10, 
    	    'category' => $cat->cat_ID
    	)
     );
     if ( empty( $posts ) ) {
         return [];
     }
    return rest_ensure_response( $posts );
}

@kosso
Copy link
Author

kosso commented Dec 7, 2016

Update.: RESOLVED (but no XP-* headers?)

Seems I needed to the use the WP_REST_Posts_Controller methods to prepare the data :

add_action( 'rest_api_init', 
    function () {
            register_rest_route( 'our-namespace/v1', '/posts/(?P<slug>[a-zA-Z0-9-]+)', array(
                'methods' => WP_REST_Server::READABLE,
                'callback' => 'get_posts_by_category_slug',
                'show_in_rest' => true
            )
        );
    }
);

function get_posts_by_category_slug( $request ) {
    $cat = get_category_by_slug( $request['slug'] );
    $posts = get_posts( 
        array( 
            'post_type' => 'post',
            'posts_per_page' => 10, 
    	    'category' => $cat->cat_ID
    	)
     );
     if ( empty( $posts ) ) {
         return [];
     }

    $controller = new WP_REST_Posts_Controller('post');

    foreach ( $posts as $post ) {
        $response = $controller->prepare_item_for_response( $post, $request );
        $data[] = $controller->prepare_response_for_collection( $response );
    }
    return rest_ensure_response( $data );

}

@kosso kosso changed the title Since filter has been removed, how to get posts by category slug? Since filter has been removed, how to get posts by category slug with same schema as v2/posts? Dec 7, 2016
@kadamwhite kadamwhite reopened this Dec 7, 2016
@kadamwhite
Copy link
Contributor

Re-opening as the question has changed! @joehoyle any ideas about how to pass those headers through?

@joehoyle
Copy link
Member

joehoyle commented Dec 7, 2016

These headers need to be generated by your custom controller, however I'm not sure why you are writing a whole new controller just to support slug, just filter rest_post_query or use the Filter plugin that @kadamwhite linked above, I think that would be a much easier approach.

The pagination headers part of the Posts Controller isn't all that reusable as you may have discovered, so the above is I think the best way to solve this.

@kosso
Copy link
Author

kosso commented Dec 7, 2016

Thanks. I just updated the previous comment to show that I now have the correct post schema properties in the response. But no pagination headers yet.

@kosso
Copy link
Author

kosso commented Dec 7, 2016

@joehoyle Could I have an example of how to do this? If I'm going about it the wrong way.

All I'm trying to do is get a list of posts by category slug in exactly the same structure as wp/v2/posts

@kosso
Copy link
Author

kosso commented Dec 7, 2016

@kadamwhite I'm getting a collection, not a single post. ;)
"Posts by category slug"

I know I can do it with the ID, but I want to create a custom endpoint which supports category slug since I can't use filter any more.

@kosso
Copy link
Author

kosso commented Dec 8, 2016

@joehoyle Where is the documentation for all the rest_* hooks?

You mentioned rest_post_query, but I can't find the documentation or any example.

Thank you.

@rmccue
Copy link
Member

rmccue commented Jan 4, 2017

Hi! We're no longer handling support here on GitHub, so future questions should be posted on WordPress.org.

To find filters, you can check the developer.wordpress.org reference. rest_post_query is a dynamic hook, so it's documented as rest_{$this->post_type}_query.

@tah75
Copy link

tah75 commented Aug 19, 2018

I want to filter slug that they have some string, wp/v2/categories/?slug=%home%
some thing like this that worked
do have any idea?

@budhaayer
Copy link

Hi there, I have a category "cinema".
I want to access that category by this "/?rest_route=/wp/v2/categories/slug=cinema"
but it is not worked.
Could you help me?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants