3

我正在尝试处理一个有点复杂的查询。我已经阅读了一些关于如何处理这个问题的方法,但它们在这里并不真正适用,因为这不像一个复杂的搜索表单(比如在 vBulletin 搜索帖子表单上),而是一组过滤两者的路由按“类别”(未发布、流行、最新)和“时间”(所有时间、上个月、上周、今天)

我意识到下面的代码非常糟糕。我的目标只是让它工作,然后重构。更不用说,它甚至没有真正起作用,因为它没有考虑到类别和时间,只是一个或另一个,但我想我会在这个线程中处理这个问题。

此外,为了使这个 SO 代码粘贴更清晰,我.page(params[:page]).per(30)从每一行中排除了 ,但是它需要继续所有这些。

那么,有谁知道我该怎么做呢?我已经考虑了一段时间,有点难过

def index
  case params[:category]
  when "latest"
    @books = Book.all.page(params[:page]).per(30)   
  when "downloads"
    @books = Book.order('downloads DESC')
  when "top100"
    @books = Book.order('downloads DESC').limit(100)
  when "unreleased"
    @books = Book.unreleased
  else
    @books = Book.all.page(params[:page]).per(30)   
  end

  case params[:time]
  when "today"
    @books = Book.days_old(1)
  when "week"
    @books = Book.days_old(7)
  when "month"
    @books = Book.days_old(30)
  when "all-time"
    @books = Book.all
  else
    @books = Book.all.page(params[:page]).per(30)   
  end
end  

路线:

# Books
get 'book/:id', to: 'books#show', as: 'book'

resources :books, only: [:index] do
  get ':category/:time(/:page)', action: 'index', on: :collection
end
4

2 回答 2

8
  1. 将所有查询作为范围移动到模型

    class Book < ActiveRecord::Base
      scope :downloads,  -> { order('downloads DESC') }
      scope :top100,     -> { order('downloads DESC').limit(100) }
      scope :unreleased, -> { unreleased }
      scope :today,      -> { days_old(1) }
      scope :week,       -> { days_old(7) }
      scope :month,      -> { days_old(30) }
      scope :latest,     -> { }
      scope :all_time,   -> { }
    end
    
  2. 创建辅助方法来过滤参数并避免不匹配的数据

    class BooksController < ApplicationController
      private
    
      def category_params
        %w(downloads top100 unreleased).include?(params[:category]) ? params[:category].to_sym : nil
      end
    
      def time_params
        %w(today week month latest all_time).include?(params[:time]) ? params[:time].to_sym : nil
      end
    end
    
  3. case通过应用与参数同名的范围来摆脱语句

    def index
      query = Book.all
      query = query.send(category_params) if category_params
      query = query.send(time_params) if time_params
      @books = query.page(params[:page]).per(30)
    end
    

在四行中,我们仍在Sandi Metz 指导方针的范围内!:)

于 2013-09-29T04:38:38.367 回答
2

例如,在 Rails 中,您可以“链接”查询

   Book.where(:released => true).where(:popular => true)

是相同的

   Book.where(:released => true, popular => true)

您可以使用它来帮助您进行重构。这是我的看法:

   def index

      # Start with all books, we are going to add other filters later
      query = Book.scoped

      # Lets handle the time filter first   
      query = query.where(['created_at > ?', start_date] if start_date

      case params[:category]
      when "latest"
        query = query.order('created_at DESC')
      when "downloads"
        query = query.order('downloads DESC')
      when "top100"
        query = query.order('downloads DESC').limit(100)
      when "unreleased"
        query = query.where(:released => false)
      end

      # Finally, apply the paging
      @books = query.page(params[:page]).per(30)

end

    private

    def start_date
      case params[:time]
      when "today"
        1.day.ago 
      when "week"
        7.days.ago
      when "month"
        1.month.ago
      when "all-time"
        nil
      else
        nil
      end
    end
于 2013-09-29T04:14:52.383 回答