Is there a way to do includes that isn't this insane?


#1

I’m not sure if I’m missing something, but when I include something I was expecting it to be inside the same data model so I could loop through it in the view.

Because the includes are a different object, they seem to be a bit disconnected when trying to work with the API response.

For example, if I have a URL that is /product/slug I can pull the products with their images by slug using the .Filter() method attached to the product model. Eg:

function getProducts(slug) {
    return Moltin.Products.With(['main_image']).Filter({eq: {slug}}).All();
}

However, if I have a URL that is /category/slug I can’t filter on the category model. I can get a category by ID, but /category/123456523567237hdhdd isn’t very user-friendly. Plus I then wouldn’t be able to include the products main_image.

So, for a page where I want to show all the products in a category with their images, it seems the only way I can do this is to pull all the products and include the categories and the images. I then have to loop through the products and remove any that don’t have a category and only include those that match the slug. I then have to do another loop to create a new object where it finds the main_image for each product in the includes object.

My code currently looks like this, which works, but you will probably agree is pretty awful lol:

function getCategory(slug) {
  return Moltin.Products.With(['main_image', 'categories']).All()
  .then(({data, included}) => {
    const catIdToMatch = included.categories.find(category =>
      category.slug === slug
    ).id;

    const products = data.filter(product => {
      if (product.relationships.categories === undefined) {
        return false;
      }
      return product.relationships.categories.data.some(category =>
        category.id === catIdToMatch
      );
    });

    const productsWithimages = products.map(product =>
      Object.assign({}, product, {image: included.main_images.find(image => image.id === product.relationships.main_image.data.id)})
    );
    return productsWithimages;
  });
}

I’m basically re-modelling the entire response in order to get a data structure I can actually loop through in the view, whilst still only making one call to the API. Eg:

{
    products: [
        {
            name: ...,
            slug: ...,
            main_image: ....
        },
        {
            name: ...,
            slug: ...,
            main_image: ....
        }
    ]
}

#2

Hi there!

This use case is something we’ve picked up on and we realise it isn’t very intuitive right now. We’re planning on extending the search functionality so in this case you’d do something along the lines of (you’d also be able to filter by category ID if you liked):

function getProductsByCategory(categorySlug) {
    return Moltin.Products.With(['main_image']).Filter({
      eq: {
        'category.slug': categorySlug
      }
    }).All();
}

This would give you a paginated list of products in that category with their main_image. You’ll also be able to sort or add additional filters as usual.

I’d be interested to get your feedback on whether this would solve your use case?

Thanks,
James


#3

Any updates on when this will be implemented?
I would think that this is a very common use case. Filtering products with a category is something that every E-Commerce site does and the whole point of Moltin is to make it easy to build an E-Commerce site.