Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling multiple filters (params) cleanly in controller

I have a class called Post, and I need to be able to accommodate the following scenarios:

  • If a user selects a category, only show posts from that category
  • If a user selects a type, only show posts with that type
  • If a user selects a category and type, only show posts from that category with that type
  • If a user selects nothing, show all posts

I'm wondering if it's inevitable that my controller is just going to look gross with a ton of conditionals... here's my flawed approach at tackling this – does anyone know how I can accomplish this?

class PostsController < ApplicationController

  def index
    @user = current_user

    # If a user has not specified a type or category, 
    # show them everything
    @posts = Post.all

    # If a user has selected a category, but no type, only 
    # show posts from that category.
    if params[:category] && !params[:type]
      category = Category.find(params[:category])
      @posts = @category.posts
    end

    # If a user has selected a category and a type, only show
    # posts from that category with that type
    if params[:category] && params[:type]
      category = Category.find(params[:category])
      type = params[:type]
      @posts = category.posts.where(post_type: type)
    end

    # If a user has selected a type but not a category, show all 
    # of the posts with that type 
    if params[:type] && !params[:category]
      type = params[:type]
      @posts = Post.where(post_type: post_type)
    end
  end

end
like image 341
cmw Avatar asked Feb 05 '14 23:02

cmw


1 Answers

You'll be better off following the convention of "fat models, skinny controllers", meaning you should put this kind of logic in the model itself. The Post class should be able to report which posts correspond to your conditions, so you can define a method to do that:

class Post < ActiveRecord::Base
  ...
  def self.by_category_and_type(category = nil, type = nil)
    return where(category: category, type: type) if category && type
    return where(category: category) if category
    return where(type: type) if type
    all
  end
  ...
end

Then in your controller you can just call

@posts = Post.by_category_and_type(params[:category], params[:type])

I haven't tested this, but I think it should do the trick. Let me know if it doesn't!

like image 155
Tyler Avatar answered Nov 15 '22 10:11

Tyler