はじめに
ransackというgemを使えば簡単に検索機能を実装できますが、今回は勉強のために、gemを使わないシンプルな検索機能の作り方を紹介します。
前提として、私の環境は下記の通りです。
Ruby 2.6.3
Rails 5.2.3
目的
検索をしたい任意の文字列と、タスクのタイトルが部分一致するデータの集合を取得する。
今回扱うタスクモデル・コントローラー・ビューをscaffoldで一括生成します。
rails g scaffold task title:string
タスクモデルはこのようになったと思います。
ActiveRecord::Schema.define(version: 2019_06_28_130239) do
create_table "tasks", force: :cascade do |t|
t.string "title"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
実装方法
それではタスク一覧tasks#index
に検索機能を実装しています。
まずはviewから作りましょう。form_with
ヘルパーを使います。
url: tasks_path
と指定することで、ユーザーが入力した文字列をtasks_controller
のindexアクション
に送ります。
データを取得したいのでmethod: :get
を指定しましょう。
また、form_withはデフォルトでajaxを使うようにになっているので、local: :true
と指定して、ajaxを解除しましょう。
form.text_field :search
とすることで、indexアクション側でparams[:search]
を使って、入力した文字列を受け取れるようにします。
<%= form_with url: tasks_path, method: :get, local: true do |form| %>
<%= form.text_field :search %>
<%= form.submit 'Search', name: nil %>
<% end %>
このような検索欄(テキストフィールド)が表示されていると思います。
次にcontrollerを書いていきます。
@tasks.where('title LIKE ?', "%#{params[:search]}%")
で、ユーザーが入力した文字列に部分一致するデータを検索し、取得しています。
"%#{params[:search]}%"
このように%%
で囲むことによって、部分一致の検索を行っています。
def index
@tasks = Task.all
@tasks = @tasks.where('title LIKE ?', "%#{params[:search]}%") if params[:search].present?
end
イメージは次の通りです。
(1) hogeと検索欄に入力し、Searchをクリック
ちなみに、where('title LIKE ?', "%#{params[:search]}%")
をwhere("title LIKE %#{params[:search]}%")
のように直接変数を入れることはSQLインジェクションの危険があるので、避けましょう。
以下はRAILS GUIDESからの引用です。
条件文字列の中に変数を直接置くと、その変数はデータベースに そのまま 渡されてしまいます。これは、悪意のある人物がエスケープされていない危険な変数を渡すことができるということです。このようなコードがあると、悪意のある人物がデータベースを意のままにすることができ、データベース全体が危険にさらされます。くれぐれも、条件文字列の中に引数を直接置くことはしないでください。
以上です。
参考
Active Record クエリインターフェイス - Rails ガイド
Rails セキュリティガイド - Rails ガイド