Search code examples
ruby-on-railsmodelhas-many-through

Rails: chaining multiple filters together for has many through: relationships


I am in need of some help with chaining some filters involving has many through relationships. Some background:

  1. I have a model called product. A product has many categories. Vice versa, a category can have many products.
  2. I have a model called colours, A product can have many colours. These 2 are also linked via has many through relationships.

My main goal is to somehow be able to filter items based on category and colour. I am receiving input from the user via a form.This would mean doing something in the controller like

@products= Product.includes(:categories).includes(:colours)
        .where(categories: {id: params[:item][:category_ids]})
        .where(colours: {id: params[:item][:colour_ids]})

However, this solution comes with a lot of problems apart from being real janky. Plus, if a user does not pass in any filters, it just filters with nils or "". Is there a better way of chaining multiple has many through relationships like this? I've been trying to find any hints of how to do this online but I am still clueless on what to do here. Any help would be much appreciated. Also, I can edit this post if any additional code is needed.


Solution

  • Your implementation looks quite good). But i would suggest you this. If you just need filtering products then you can call left_joins and pass there your join tables(products_categories, products_colours) instead of target tables. Why? Because it will reduce LEFT JOIN clauses in your sql query and don't load objects into memory, hence you'll improve performance. But it only will work if you don't need to go through your products and take his categories or colours. So query will look like.

    Product
      .left_joins(:products_categories, :products_colours)
      .where(products_categories: { category_id: params[:item][:category_ids].presence || Category.ids } )
      .where(products_colours: { colour_id: params[:item][:colour_ids].presence || Colour.ids }