D-6. Model scope & 建立搜索功能

建立搜索用gem 'ransack'不好吗? 完整又便利。
真的.....但是有些小东西,自己刻一个出来,还是蛮好玩的。


简单复习一下。

scope

Model里的scope也是新手常会被问的内容,毕竟对程序码维护及重构还是有所帮助,所以稍微复习一下。
当商业逻辑是简单搜索条件时,多用scope
当商业逻辑有复杂运算时,使用类别方法

第一个Model建立时,可能其相对应的controller会直接打上。

def index
  @roles = Role.all
end

随着开发,可能index需要不同的排列方式,可能希望只有包含特定资料。

def index
  @roles = Role.order(id: :desc)
end

def some_action
  @roles = Role.where('address LIKE ?', "%屏东%")
end

需求越来越多时,查询语句也就越来越长。

def some_action
  @roles = Role.where('address LIKE ? OR phone_number LIKE ?', "%屏东%", "%878%").order(id: :desc)
end

scope这个方法可以让你的controller变乾净(DRY)。

可以直接在Model

class Role
  scope :scope_name_you_like, -> {where('address LIKE ?', "%屏东%")}
end

controller就可以改写。

def some_action
  @roles = Role.scope_name_you_like
end

还记得lambda吗? 就是它。
D-26.Block、Proc、lambda https://ithelp.ithome.com.tw/articles/10260028
所以如果有需求,也可以带参数。例如

class Role
  scope :whos_pay__high, ->(100000) { where["Pay > ?", 100000] }
end

等等搜索功能就是会利用到带参数


scopescope之间是可以连在一起使用的。

为你自己学Ruby on Rails
class Product < ApplicationRecord
scope :available, -> { price_over(0).where(is_available: true) }
scope :price_over, ->(p) { where(["price > ?", p]) }
end


建立scope不要为了单一搜索。例如

  scope :first_pingtung_role, -> { where('address LIKE ?', "%屏东%").first }

有需求单独show可以这样处理。

class Role
  scope :pingtung_role, -> { where('address LIKE ?', "%屏东%") }
end

# controler
def some_show
  @role = Role.pingtung_role.first
end

保有原本的scope :pingtung_role,也可以重复利用。


直接以scope建立简单搜索功能。

简单的搜索功能至少要有一个简单的搜索框。

view

<%= form_tag roles_path, method: :get do %>
  <%= text_field_tag :search, params[:search], placeholder: "请输入" %>
  <%= submit_tag "送出", name: nil %>
<% end %>

modle

class Role
  # 这一个是原本给index用的。
  scope :by_last_name, -> { order(:last_name) }
  # 这一个是搜索功能的。
  scope :any_name_you_want, -> (search) { where('address LIKE ?', "%#{search}%") if search.present?}
end

记得两个%%喔,没有会变成,address一定要 == #{search}。

controller

def index
  begin params[:search]
    @roles = Role.any_name_you_want(params[:search])
  rescue
    @roles = Role.by_last_name
  end
end

这样就完成了一个关於地址的简单搜索,也可以稍稍加工,让这个搜索对另一个栏位也有反应。

class Role
  scope :any_name_you_want, -> (search) { where('address LIKE ? OR first_name LIKE ?', "%#{search}%", "%#{search}%") if search.present?}
end

scoperender还有callback一起运用,可以再让code整洁一点。

可能view除了index,还会需要再多一些页面是跟关联性有关,或是有某些特定资料的。
例如:

def index
  begin params[:search]
    @roles = Role.any_name_you_want(params[:search])
  rescue
    @roles = Role.by_last_name
  end
end

def phone_number
  @roles = Role.order(:phone_number)
end

def job
  @roles = Role.order(:job)
end

但是这三页可能长得都一样,那就不如直接将原本的index.html.erb改为_index.html.erb,将这三页都render上去。
例如:

index.html.erb

<% render 'index'%>

phone_numbe.html.erb

<% render 'index'%>

job.html.erb

<% render 'index'%>

这时我们再将controller修改一下。

before_action :roles_list, only: [:index, :phone_number, :job]
def index
end

def phone_numbe
end

def job
end

private
def roles_list
  @roles = case action_name
           when "phone_number"
             Role.order(:phone_number)
           when "job"
             Role.order(:job)
           else
             Role.by_last_name
           end.any_name_you_want(params[:search])
end

这三页除了初始会看到的资料不同,但搜索功能一样可以共用。
这样利用scope客制化搜索方法,简单又兼顾code整洁的搜索功能就完成啦。


<<:  10 有局数就可以打分数

>>:  Day 9:AWS是什麽?30天从动漫/影视作品看AWS服务应用 -《PSYCHO-PASS心灵判官》part 3

Sudoku Solution Validator

今日kata 原始题目如下:(4kyu) Write a function validSolutio...

【D22】制作讯号灯之反思:观察讯号灯与9/22大盘关系

前言 今天加权指数开低,维持一个大跌,来观察讯号灯和大盘、个股的关系,来验证我们的讯号灯能不能参考。...

企划实现(19)

在写app时常常会因为所有app在外观看起来一样,所以往往会要找很久才能找到自己想要执行的app,所...

[Day20] JavaScript - Event Bubbling (事件冒泡) & Event Capturing (事件捕获)

Event Flow 事件流 DOM的Event flow概念,指的是「网页元素接收事件的顺序」。 ...

Powershell 入门之动态参数

前面我们讲了一些参数的属性,我们可以通过 validateSet 来限制参数值的范围。但有些情况下,...