Rails:基于params的集合

时间:2014-10-01 02:04:31

标签: ruby-on-rails ruby-on-rails-3 model-view-controller routing

我有一个看起来非常像这样的索引方法:

  def index
    if params[:brand]
      @users = User.includes(:brand).where(brand_id: params[:brand]).order("#{sort_column} #{sort_direction}").page(params[:page]).per(10)
    elsif params[:search]
      @user = User.includes(:brand).find_by_client_code(params[:search])
      redirect_to edit_user_path(@user)
    elsif params[:page] == 'all'
      @users = User.includes(:brand).order("#{sort_column} #{sort_direction}").all
    elsif params[:state]
      @users = User.includes(:brand).where(state: params[:state]).page(params[:page]).per(10)
    else
      @users = User.includes(:brand).order("#{sort_column} #{sort_direction}").page(params[:page]).per(10)
    end
  end

我知道,这很混乱但是很有效。现在我正在尝试重构它,我无法弄清楚将它拆分成较小的集合的最佳方法,而不会使我的路线复杂化。

  def index
    [:brand, :page, :search, :state].each do |param|
      if params[:page] == 'all'
        @users = User.includes(:brand).order(column + ' ' + direction)
      elsif params.key?(param)
        param
      else
        @users = User.includes(:brand).order(column + ' ' + direction)
                     .page(params[:page]).per(10)
      end
    end
  end

  def brand
    @users = User.includes(:brand).where('brand_id in (?)', params[:brand])
                 .order(column + ' ' + direction).page(params[:page]).per(10)
  end

  def state
    @users = User.includes(:brand).where(state: params[:state])
                 .page(params[:page]).per(10)
  end

  def search
    @user = User.includes(:brand).find_by_client_code(params[:search])
    redirect_to edit_user_path(@user)
  end

以上不起作用,但你明白了。有谁知道处理这种情况的好方法?欢呼声。

1 个答案:

答案 0 :(得分:1)

我可能会这样做 -

首先,更新此代码,您已将sort_columnsort_direction方法定义为默认值:

def sort_column
  colum_name = params[:colum_name]
  colum_name ||= 'id'
end

def  sort_direction
  direction = params[:direction]
  direction ||= 'ASC'
end

添加一个新方法,让per_page(在您拥有sort_columnsort_direction的同一位置)来自params或默认来自User class:

def per_page
  per = params[:per_page]
  per ||= User.per_page
end
app / models / user.rb 中的

scope :with_brand_id, ->(id) { where(brand_id: id) }
scope :with_state,    ->(state) { where(state: state) }
scope :order_with,    ->(column_name, direction) { order("#{sort_column} #{sort_direction}") }

# never use/avoid magic numbers in your application at multiple places as they gets unmanageable as your application grows
# single place to manage your per page entries for users.
def self.per_page
  10
end

# easy to use key:value based arguments since you're using Ruby 2, cheers!
def self.fetch_with_brand(brand: nil, state: nil, page: nil, sort_column: 'id', sort_direction: 'ASC', per_page: User.per_page)
  user_scope, pagination_scope_applies =  if brand.present?
    [self.with_brand_id(brand), true]
  elsif state.present?
    [self.with_state(state), true]
  else
    [self.scoped, (page != 'all')]
  end

  user_scope.merge(pagination_scope(page, per_page)) if pagination_scope_applies
  user_scope.includes(:brand).order_with(sort_column, sort_direction)
end

# since I am not sure about your implementation of `page` and `per` methods, I'd create a class method, otherwise you can create a `scope` for this, too
def self.pagination_scope(page_number, per_page)
  self.page(page_number).per(per_page)
end

您是否在上述代码中找到了行[self.scoped, (page != 'all')]?此处self.scoped等于self.all(评估时),但我们必须使用scoped代替all,因为在Rails 3中它提供了数组,而在Rails 4中它将是一个 ActiveRecord :: Relation 对象,因此如果你在Rails 4上,你可以使用self.all。注意:scoped是在Rails 4中弃用,而不是all

另外,我想在这里指出一个问题。在您的代码中,您优先考虑params[:page] == 'all'条件,然后优先考虑params[:search]。在我上面提到的代码中,优先考虑search然后优先考虑page,但是你明白了,对吗?

现在,让我们在控制器中添加用户特定的params方法:

def user_params
  params.slice(:brand, :page, :state).merge!({sort_column: sort_column, sort_direction: sort_direction, per_page: per_page })
end

但是,在Rails 4中,使用强参数更容易,例如:params.require(:user).permit(:search,..)等。

现在,您的控制器的索引方法可能如下所示:

def index
  if params[:search].present?
    @user = User.find_by_client_code(params[:search])
    redirect_to edit_user_path(@user)
  else
    @users = User.fetch_with_brand(user_params)
  end
end

如果您倾向于将用户重定向到更多地方的编辑页面,您可以进一步重构:

before_filter :redirect_to_edit, only: [:index, :some_other_method_name]

def index
  @users = User.fetch_with_brand(user_params)
end

def redirect_to_edit
  if params[:search].present?
    @user = User.find_by_client_code(params[:search])
    redirect_to edit_user_path(@user)
  end
end

您现在已经拥有了瘦控制器