建立搜索用gem 'ransack'
不好嗎? 完整又便利。
真的.....但是有些小東西,自己刻一個出來,還是蠻好玩的。
簡單複習一下。
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
等等搜索功能就是會利用到帶參數
scope
與scope
之間是可以連在一起使用的。
為你自己學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
將scope
與render
還有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
整潔的搜索功能就完成啦。