使用范围来过滤轨道中的结果5

时间:2017-09-04 23:38:03

标签: ruby-on-rails ruby-on-rails-5

我遇到了rails中的范围问题。目前我在索引控制器中有一个case语句,它导致应用程序过滤1个类别 OR 另一个类别。 但我需要能够一次过滤多件事。基本上链接范围,以便当用户点击链接A时,应用范围A,然后当他们点击链接B时,范围B被添加到范围A的顶部以进行更多过滤。最后我想在视图中添加一个复选框,以便在检查范围时应用范围,当取消选中时,范围将被删除。

我现在使用了一堆条件逻辑(if \ else)语句因为我不需要过滤说“体验”我需要过滤4种不同的体验选项(0 - 2年, 2-5岁,5-10岁等...)。对于所有范围,这都是相同的。

总而言之,我需要找到一种方法来链接范围,以便过滤器不断应用(过滤器A +过滤器B +过滤器C等等),而不是像现在这样过滤它们的方式A或过滤器B,但无法让它们共同协作以进一步缩小结果范围。

这是我的范围

scope :by_experience, -> (ex) { where(experience: ex) }

scope :by_num_days_past, -> (days_past) { where('created_at >= ?', days_past.days.ago) }

这是我的观点

            <!-- toggle for experience -->  
            <a class="" role="" data-toggle="collapse" 
href="#experience-collapse" aria-expanded="false" aria-
controls="experience-collapse">
                Experience<br />
            </a>
            <div class="collapse" id="experience-collapse">
              <div class="well">
                <%= link_to "0-2 years", filtered_jobs_path(:experience, '0-2 years') %><br />
                <%= link_to "2-5 years", filtered_jobs_path(:experience, '2-5 years') %><br />
                <%= link_to "5-10 years", filtered_jobs_path(:experience, '5-10 years') %><br />
                <%= link_to "10+ years", filtered_jobs_path(:experience, '10+ years') %>        
              </div>
            </div>  
            <br />            

            <!-- toggle for num_days_past -->  
            <a class="" role="" data-toggle="collapse" href="#num-days-
past-collapse" aria-expanded="false" aria-controls="num-days-past-
collapse">
                Days Listed<br />
            </a>
            <div class="collapse" id="num-days-past-collapse">
              <div class="well">
                <%= link_to "7 days", filtered_jobs_path(:num_days_past, '7') %><br />
                <%= link_to "30 days", filtered_jobs_path(:num_days_past, '30') %><br />
                <%= link_to "60 days", filtered_jobs_path(:num_days_past, '60') %><br />
              </div>
            </div>  
            <br />   

这是我的控制器的索引操作,我认为需要进行更改才能同时应用多个范围。

def index

      case params[:scope]  
      when "num_days_past"
        if (params[:format] == "7")
          @jobs = Job.by_num_days_past("7").paginate(page: params[:page], per_page: 5)     
        elsif (params[:format] == "30")
          @jobs = Job.by_num_days_past("30").paginate(page: params[:page], per_page: 5)     
        elsif (params[:format] == "60")
          @jobs = Job.by_num_days_past("60").paginate(page: params[:page], per_page: 5)  
         else   
             @jobs = Job.order('created_at DESC').paginate(page: params[:page], per_page: 5)        
         end      
end

4 个答案:

答案 0 :(得分:0)

在实例变量上调用多个范围会使它们被链接。请尝试以下方法:

def index

    @jobs = Job.by_num_days_past("7") if (params[:format] == "7")     
    @jobs = @jobs&.by_num_days_past("30") || Job.by_num_days_past("30") if (params[:format] == "30")        
    @jobs = @jobs&.by_num_days_past("60") || Job.by_num_days_past("60") if (params[:format] == "60")         
    @jobs = Job.order('created_at DESC') if @jobs.blank?
    @jobs.paginate(page: params[:page], per_page: 5)

end

答案 1 :(得分:0)

你可以延迟分页直到你完成应用范围。像这样:

def index
  case params[:scope]  
  when "num_days_past"
    if (params[:format] == "7")
      @jobs = Job.by_num_days_past("7")
    elsif (params[:format] == "30")
      @jobs = Job.by_num_days_past("30")
    elsif (params[:format] == "60")
      @jobs = Job.by_num_days_past("60")
    else
      @jobs = Job.all
    end 

  # Now you can apply scope 2
  # For example
  @jobs = @jobs.by_experience(params[:exp]) if params[:scope_by_experience]

  @jobs = @jobs.order('created_at DESC').paginate(page: params[:page], per_page: 5)        
end

#paginate实现可能正在立即执行查询,结果将不是可以应用其他范围的Active Record对象。

答案 2 :(得分:0)

  

问题:

目前无法正常工作的原因是您没有保存当前的过滤器/范围。如果单击第二个过滤器,则无法确定先前呈现的范围。

要解决此问题,您需要将过滤器存储在客户端上,并在下一个请求中将其作为参数发送。这通常在URL中完成,这样您就可以使用过滤器共享网站。如果其他人进入链接,则过滤器会自动跳转到正确的位置。但它也可以通过cookie来完成。

例如,看看:

我知道它们可能是您的外语(荷兰语),但在打开和关闭过滤器时请仔细查看URL栏。保存过滤器并在下一个请求时传递。

  

一个可行的解决方案:

目前,您为过滤器创建了一个专用的路径,我的猜测是:

resources :jobs do
  get '/:scope/:format', on: :collection, as: :filtered, action: :index
end

你应该做的是使用jobs_path这样的参数(查看):

jobs_path(number_of_days_past: 7, experience: '0-2 years')

这将生成URL:

/jobs?number_of_days_past=7&experience=0-2+years

如果要将新过滤器与先前过滤器结合使用,则必须先提取先前的过滤器。这是在控制器中完成的,如下所示:

def index
  @filters = params.permit(:number_of_days_past, :experience).to_h
end

创建一个帮助方法,将@filters与您要应用或删除的过滤器(app/helpers/jobs_helper.rb)合并。下面的示例可以打开和关闭过滤器并支持多个过滤器,但给定过滤器的多个值。如果您希望每个过滤器支持多个值,请以此为例,并根据自己的喜好进行编辑。

def combine_filter(apply_filters)
  apply_filters.each_with_object(@filters.deep_dup) do |(key, value), filters|
    filters[key] == value ? filters.delete(key) : filters[key] = value
  end
end

# OR, if the above did go to fast.

def combine_filter(apply_filters)
  # duplicate the @filters hash, this way multiple calls don't influence each other
  filters = @filters.deep_dup
  # loop through apply_filters (Hash) and check every filter
  apply_filters.each do |key, value|
    if filters[key] == value
      # filter content was the same, toggle it off
      filters.delete(key)
    else
      # filter content was not the same, replace value for this filter
      filters[key] = value 
    end
  end
  # return the filters variable
  filters
end

这将为您提供如下视图

<div class="well">
    <%= link_to "0-2 years", jobs_path(combine_filter(experience: '0-2 years')) %><br />
    <%= link_to "2-5 years", jobs_path(combine_filter(experience: '2-5 years')) %><br />
    <%= link_to "5-10 years", jobs_path(combine_filter(experience: '5-10 years')) %><br />
    <%= link_to "10+ years", jobs_path(combine_filter(experience: '10+ years')) %>        
</div>

此时,在控制器中收到请求时,您在变量@filters中拥有所有过滤器,但仍需要处理它们。请查看以下在线资源以获取示例。

  

在线帮助和资源:

以下是关于如何实现简单版本(处理传入过滤器并调用正确范围)的博客文章:

但你也可以尝试为这份工作找到合适的宝石,这是我用快速Google search找到的宝石:

答案 3 :(得分:0)

所以我最终使用了几个答案的组合。感谢Oscar解释关于暂停分页直到最后,Ahmand感谢有关链接的信息,Johan感谢您解释jobs_path params。我会投票,但它说我还没有足够的重复点。在这里发布我的答案,万一有人将来偶然发现这一点。

在控制器中,我从Job.all开始,然后在其上构建范围,最后应用分页。我觉得我应该将所有这些移动到模型中的方法,然后从控制器中调用它。但它的工作方式正是如此,我稍后会测试将它移到模型中。

def index
  @jobs = Job.all

  # scopes
  if params[:experience].present? 
    @jobs = @jobs.by_experience(params[:experience])
  end


  if params[:num_days_past].present? 
    @jobs = @jobs.by_num_days_past(params[:num_days_past].to_i)
  end      


  @jobs = @jobs.paginate(page: params[:page], per_page: 5)
end

在我看来,我已将经验参数添加到链接中,以便在参数存在时,网址将显示经验和过滤天数。

      <div class="well">
            <%= link_to "7 days", filtered_jobs_path(num_days_past: '7', experience: params[:experience]) %><br />
            <%= link_to "30 days", filtered_jobs_path(num_days_past: '30', experience: params[:experience]) %><br />
            <%= link_to "60 days", filtered_jobs_path(num_days_past: '60', experience: params[:experience]) %><br />
      </div>

这是我的范围

的模型
 # scopes to filter results from database.
scope :by_created_at, -> { order(created_at: :desc) }
scope :by_experience, -> (ex) { where(experience: ex) if ex.present? }
scope :by_num_days_past, -> (days_past) { where('created_at >= ?', days_past.days.ago) if days_past.present? }
scope :by_location, -> (location) { where('city LIKE ? OR state LIKE ?', "%#{location}%", "%#{location}%") }

这是我的路线,我删除了以前的/ filer /:范围。

  #get 'jobs/filter/:scope' => 'jobs#index', as: :filtered_jobs
  get 'jobs' => 'jobs#index', as: :filtered_jobs