在控制器中干净地处理多个过滤器(参数)

时间:2014-02-05 23:14:52

标签: ruby-on-rails ruby ruby-on-rails-3 ruby-on-rails-3.1

我有一个名为Post的类,我需要能够适应以下场景:

  • 如果用户选择某个类别,则仅显示该类别中的帖子
  • 如果用户选择了某种类型,则只显示该类型的帖子
  • 如果用户选择类别和类型,则仅显示该类别中具有该类型的帖子
  • 如果用户不选择任何内容,则显示所有帖子

我想知道我的控制器是否会不可避免地会出现大量的条件......这是我在处理这个方面的错误方法 - 有谁知道我能做到这一点?

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

2 个答案:

答案 0 :(得分:9)

你最好遵循“胖模型,瘦调控制器”的惯例,这意味着你应该把这种逻辑放在模型本身。 Post类应该能够报告哪些帖子符合您的条件,因此您可以定义一种方法来执行此操作:

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

然后在你的控制器中你可以打电话

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

我没有测试过这个,但我认为它应该可以解决这个问题。如果没有,请告诉我!

答案 1 :(得分:0)

您可以像这样重构代码:

控制器:

  def index
    @user = current_user
    @posts = find_posts 
  end

  private

  def category
    Category.find(params[:category])
  end

  def find_posts
    if type = params[:type]
      if params[:category]
        category.posts.of_type(type)
      else
        Post.of_type(type)
      end
    elsif params[:category]
      category.posts
    else
      Post.all
    end
  end

将范围添加到Post模型中,根据需要命名:

scope :of_type, ->(type) { where(post_type: type) }

我建议您使用https://github.com/voxdolo/decent_exposure 上面的代码不是最好的,但你可以用这个gem来改进它。 您甚至可以创建一个新类,负责查找帖子并在您的控制器中使用此类。