How to list posts of categories and its sub categories?

I’m trying to find a way to include a list of posts of a category and its sub categories in my app. For example, this is the default behavior if you call /category/<parent_category> in a wp blog.
Using Frontity, I get a 404 not found if there are no posts in a specific category, even if it contains sub categories with posts (Frontity calls wp-json/wp/v2/categories?slug=… and then wp-json/wp/v2/posts?categories=<cat_id> where cat_id is the id of my parent category; the endpoint only returns the specific category’s posts).
Is there a way to include those posts?
Thank you!

That’s not a Frontity problem, but the default behaviour of the REST API.

The easiest way to solve it is to add the parent category to all the posts of the child categories. Is that possible in your case?

It would be possible, but I’m honestly more interested in a solution where I can implement the workflow:

  1. Get id of parent category
  2. Get sub categories
  3. Fetch posts

… so that I don’t have to assign them to the parent category. Would I have to adapt wp-source and the handlers in that case?
Seems that I have to fetch categories?parent=<parent_cat> and then fetch per category.

Yeah, I think so.

You can fetch posts from multiple categories at once using comma separated ids: ?categories=1,2,3.

It’d be something like this:

const myCategoriesHandler = {
  pattern: "/category/(.*)?/:slug",
  func: async ({ route, params, state, libraries }) => {
    // Get the page of the current route.
    const { page } = libraries.source.parse(route);

    // Get the id of the parent category.
    const parentCatResponse = await libraries.source.api.get({
      endpoint: "categories",
      params: { slug: params.slug }
    });
    const [parentCat] = await libraries.source.populate({
      state,
      response: parentCatResponse
    });

    // Get the ids of all the child categories.
    const childCatsResponse = await libraries.source.api.get({
      endpoint: "categories",
      params: { parent: parentCat.id }
    });
    const childCats = await libraries.source.populate({
      state,
      response: childCatsResponse
    });
    const ids = childCats.map(cat => cat.id);
    ids.push(parentCat.id);

    // Get the posts from those categories.
    const postsResponse = await libraries.source.api.get({
      endpoint: "posts",
      params: { categories: ids.join(","), page, _embed: true }
    });
    const items = await libraries.source.populate({
      state,
      response: postsResponse
    });
    const total = libraries.source.getTotal(postsResponse);
    const totalPages = libraries.source.getTotalPages(postsResponse);

    // Populate state.source.data with the proper info about this URL.
    Object.assign(state.source.data[route], {
      id: parentCat.id,
      taxonomy: "category",
      items,
      total,
      totalPages,
      isArchive: true,
      isTaxonomy: true,
      isCategory: true
    });
  }
};

Take into account that you need to do 3 serial requests in order to get this info so you better cache your REST API with a CDN or at least a plugin :slight_smile:

3 Likes

That’s cool! Thanks for the code - I’ll try it out!
Jep sure, didn’t know about WP Rest cache plugin, sound’s promising :slight_smile:

1 Like