random_user_0891
random_user_0891

Reputation: 2051

pass Rails conditional statement to where clause

I have a dropdown in a form and based on what the user selects I need to perform different queries. What's the best way to accomplish this? Could I have 2 functions where I get the privacy_level and then process what query to use in order to pass that value to the 2nd function?

  def get_article_restrictions(privacy_level)
     case privacy_level
     when 0
       "#{@authenticated_user.id} = articles.user_id"
     when 1
       "query 1"
     when 2
       "query 2"
     end
  end

in the 2nd function I would use whatever query was returned from the 1st function.

def display_articles
  privacy_level = get_article_restrictions(params[:privacy_level])
  @articles = Article.includes(:user).where(privacy_level)
end

Upvotes: 0

Views: 666

Answers (2)

3limin4t0r
3limin4t0r

Reputation: 21110

mrzasa suggested in his answer to move the method to a model. Here is an example of how this could look.

class Article < ApplicationRecord
  # ...

  def self.privacy_level(level, user)
    case level
    when 0
      where(user_id: user.id)
    when 1
      where(attribute: 'value')
    when 2
      where(attribute: 'value')
    else
      # Return the current scope without adding constraints.
      where(nil)
    end
  end

  # ...
end

Then in the controller simply call the scope.

def display_articles
  @articles = Article.includes(:user)
                     .privacy_level(params[:privacy_level], @authenticated_user)
end

Keep in mind that most request parameters arive as strings, meaning only the else case is going to match. You can resolve this by changing the cases to when 0, '0' instead of when 0.

You might also want to move the default case into the else statement. This way when no level is given (params[:privacy_level] equals nil) you still constrain the fetched records.

Upvotes: 0

mrzasa
mrzasa

Reputation: 23307

It depends on the query you need to perform. If it's just a simple where, you can pass a hash with where params:

  def get_article_restrictions(privacy_level)
     case privacy_level
     when 0
       { "articles.user_id": @authenticated_user.id }
     when 1
       { param1: value1, param2: value2 }
     when 2
       { param3: value3, param4: value4 }
     end
  end

def display_articles
  privacy_level = get_article_restrictions(params[:privacy_level])
  @articles = Article.includes(:user).where(privacy_level)
end

I would suggest doing it the other way around: pass the scope to the method:

def fetch_articles(scope, privacy_level)
     case privacy_level
     when 0
       scope.where("articles.user_id": @authenticated_user.id)
     when 1
       scope.where(param1: value1, param2: value2)
     when 2
       scope.where(param3: value3, param4: value4)
     end
end

def display_articles
  @articles = fetch_articles(Article.includes(:user), params[:privacy_level])
end

I'd also suggest to move fetch_articles to the modes, as calling active record methods (like where) causes too much coupling and makes testing harder.

Upvotes: 2

Related Questions