商品越來越多,使用者不可能一頁頁慢慢找
我們在後台也是一樣,要快速找到一件商品並且修改刪除
就需要搜尋功能啦!
我們今天會先介紹比較簡單的搜尋功能 Ransack
提供搜尋工具的套件,裡面包含了多樣的功能
個人覺得 Ransack 算是比較入門的搜尋工具
使用上其實蠻簡單的
提供的方法也都很便利
而且像是 pagy 這些套件也都有支援
不過就沒辦法使用模糊搜尋
但我們之後會再介紹另一款搜尋工具
可以拿來做模糊搜尋
gem 'ransack'
預設的搜尋關鍵字為 q ,不過 q 應該沒人看的懂
所以我們先把它改成 query
# config/initializers/ransack.rb
Ransack.configure do |config|
config.search_key = :query
end
我們先用 name 來搜尋就好
# app/models/drink.rb
...
def self.ransackable_attributes(auth_object = nil)
["name"]
end
end
到 rails console 試試看
name_start
後面的 _start
是 Ransack 提供的 match 方法,後續我們在做詳細說明
一開始搜出來會是一個 Ransack 的實體,需要加上 result 才會跑出搜尋結果
# rails console
> aa = Drink.ransack(name_start: '阿')
Ransack::Search<class: Drink, base: Grouping <conditions: [Condition <attributes: ["name"], predicate: start, values: ["阿"]>], combinator: and>>
> aa.result
[#<Drink:0x00000001085e3270
id: 16,
name: "阿薩姆奶茶",
description: "就是回憶中的阿薩姆奶茶",
price: 40,
capacity: 600,
ingredient: [],
created_at: Sat, 23 Sep 2023 12:51:09.575584000 UTC +00:00,
updated_at: Sat, 23 Sep 2023 12:51:09.575584000 UTC +00:00,
deleted_at: nil>]
這樣就輕鬆完成搜尋了
不過我們如果要在商品頁面(含所有商品)做搜尋的話,會需要依照 polymorphic 的方式去搜尋(還記得昨天我們是用 Product 去撈所有資料嗎?忘記可回上一章複習)
一樣先到 Product 設定可以讓 ransack 搜尋的欄位
# app/models/product.rb
class Product < ApplicationRecord
...
def self.ransackable_attributes(auth_object = nil)
["productable_type"]
end
end
不過因為 Product 跟 Polymorphic 是有關聯,我們需要多加一個關聯方法
# app/models/product.rb
class Product < ApplicationRecord
...
def self.ransackable_associations(auth_object = nil)
["productable"]
end
end
而我們會去搜尋的是 dessert 以及 drink 這兩個 table
所以需要在他們的 model 檔案中加上 product 關聯設定,才有辦法進行搜尋
# app/models/drink.rb
class Drink < ApplicationRecord
...
def self.ransackable_associations(auth_object = nil)
["product"]
end
end
我們到 rails console 試試看,搜尋的時候需要明確指定 of_Drink_type ,讓 Ransack 知道他需要去 joins 哪個 table
# rails console
> Product.ransack(productable_of_Drink_type_name_eq: '阿薩姆奶茶').result
Product Load (1.2ms) SELECT "products".* FROM "products" LEFT OUTER JOIN "drinks" ON "drinks"."deleted_at" IS NULL AND "drinks"."id" = "products"."productable_id" AND "products"."productable_type" = 'Drink' WHERE "products"."deleted_at" IS NULL AND "drinks"."name" = '阿薩姆奶茶'
=>
[#<Product:0x000000010a605328
id: 12,
productable_type: "Drink",
productable_id: 16,
created_at: Sat, 23 Sep 2023 19:31:44.004810000 UTC +00:00,
updated_at: Sat, 23 Sep 2023 19:31:44.004810000 UTC +00:00,
deleted_at: nil>]
如此一來我們就完成了 Polymorphic 的搜尋
我們目前只知道怎麼在 Rails console 中做搜尋
如果要放在 View 呢?
需要使用 search_form_for 的方法(是 Ransack 提供給我們的)
一開始設定 Ransack 所要抓的 search keyword 為 query ,所以我們這邊就帶 @query
search_field 這邊要放的比較複雜,因為同時可以搜尋 drink 與 dessert
而我們又是需要用 polymorphic 的關聯去搜尋,
所以需要多加上 of_Drink_type 跟 of_Dessert_type 去搜尋
# app/views/products/index.html.erb
...
<div class="mb-4">
<%= search_form_for @query do |q| %>
<%= q.search_field :productable_of_Drink_type_name_or_productable_of_Dessert_type_name_cont, class: 'rounded-md mr-4' %>
<%= q.submit '搜尋', class: 'border-2 p-2 rounded-md bg-slate-500 text-white cursor-pointer' %>
<% end %>
</div>
...
搜尋框會帶回一包 params ,我們要請 Ransack 依照這包 params 去幫我們找到符合條件的資料,並且丟給 @query
我們要將結果渲染到頁面上,所以 pagy 的參數需要改成 @query.result
app/controllers/products_controller.rb
class ProductsController < ApplicationController
def index
@query = Product.ransack(params[:query])
@pagy, @products = pagy(@query.result)
end
end
上述就大概完成了搜尋功能囉
Ransack 提供多種的搜尋結果比對方式,詳情可參照官網
假設我們今天要搜尋 Drink 中的 name 欄位
可以看搜尋的模式是要哪種
如果是要一模一樣
就可使用 name_matches_all
或者是 name_eq
如果要類似的話就可以使用 name_cont
這個方法還蠻常用到的
那麼如果是要搜尋開頭呢? 使用 name_start
搜尋數字的話可以使用 *_lt
*_lteq
*_gt
*_gteq
等等
還有其他多種設定,可以參考官網手冊