Blankman
Blankman

Reputation: 267140

How can I pass a hash for my where clause?

I am listing products and I want to be able to pass a hash as my where clause so I can do something like:

filter = {}
filter[:category_id] = @category.id
filter[:is_active] = true

@products = Products.where(filter)

Is it possible to do this somehow?

I also need to add something like this in my where clause:

WHERE price > 100

How could I add that to a filter?

The reason I want to do this is because in the UI I will have a set of optional filters, so then I will use if clauses in my controller to set each filter.

Upvotes: 3

Views: 2263

Answers (2)

inopinatus
inopinatus

Reputation: 3779

You could have a bit of fun with scopes: write a scope that's actually a mini predicate builder, sanitizing and pattern-matching strings, and delegating to the standard predicate builder for other scalar types. E.g.

# app/models/concerns/searchable.rb
module Searchable
  extend ActiveSupport::Concern

  included do
    scope :search, ->(params) {
      params.inject(self) do |rel, (key, value)|
        next rel if value.blank?
        case value
        when String
          rel.where arel_table[key].matches '%%%s%%' % sanitize_sql_like(value)
        when Range, Numeric, TrueClass, FalseClass
          rel.where key => value
        else
          raise ArgumentError, "unacceptable search type"
        end
      end
    }
  end
end

# app/models/product.rb
class Product < ApplicationRecord
  include Searchable

then you can

filter = { name: 'cheese', description: 'aged', age: 42.. }

Product.search(filter) #=> SELECT "products".* FROM products WHERE "products"."name" ILIKE '%cheese%' AND "products"."description" ILIKE '%aged%' AND "products"."age" >= 42

Upvotes: 1

Jordan Running
Jordan Running

Reputation: 106077

You can pass a hash to where exactly like you did:

filter = {
  category_id: @category_id,
  is_active: true
}

@products = Product.where(filter)

Using a hash only works for equality (e.g. category_id = 123), so you can't put something like price > 100 in there. To add that criteria, just add another where to the chain:

@product = Product.where(filter).where('price > 100')

Or...

@product = Product.where(filter)

if params[:min_price]
  @product = @product.where('price > ?', min_price)
end

Upvotes: 4

Related Questions