【Ruby on Rails】paramsについて理解しながら検索機能を作ろう
目次
1.はじめに
2.前提・テンプレ作成
3.詳細画面の表示
4.検索機能の実装
5.まとめ
1. はじめに
本記事は、DMM WEBCAMP Advent Calendar 2023 6日目記事です。
DWCメンター・卒業生が記事を投稿しておりますので、是非他の記事もご覧ください!
執筆背景
Ruby on Railsを学習中に、よくPost.find(params[:id])
みたいな記述を見かけると思います。
このparams
という記述は、ページ遷移やデータを作成する時に用いられ、さらにはタグによる絞り込み機能や検索機能にも登場します。
paramsがいまいち理解できない、活用方法が思い浮かばないという方に向けて、本記事を執筆いたしました。
目的
- paramsを理解する
- paramsを用いた検索機能を実装する
2. 前提・テンプレ作成
開発環境
- ruby 3.1.2
- rails 6.1.4
モデル
コントローラ
class UsersController < ApplicationController
def index
@users = User.all
end
def show
@user = User.find(params[:id])
end
end
...
ルーティング
resourcesを使って次のようにします。
Rails.application.routes.draw do
resources :users
end
この記述で生成されるルーティングを確認しておきましょう。
rails routes
こちらが出力された結果です。
Prefix Verb URI Pattern Controller#Action
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
...
この画面は結局どう見ればいいのか?という疑問を持つ方へ
- Verb
どんな動作(リクエスト)をさせるかのかを決める。
コンテンツを取得する(GET)やデータを送信する(POST)など。
- URI Pattern
URLに似たようなもの。
東京都/港区/芝公園/4丁目/2-8みたいにインターネット上の住所を示す。
- Controller#Action
どのコントローラのどのアクションが動くかを示す。
- Prefix
URLなどにあだ名をつける。
東京都/港区/芝公園/4丁目/2-8と書かれても、で、結局どこの住所なの?長くて覚えられないよ。となってしまうので、「東京タワー」の住所
と、あだ名をつけておく。
使用時には「あだ名」+ _path と書けるため、今回で言えばusers_path
などになる。
view
一覧画面
<h1> 一覧画面 </h1>
<div>
<h2>ユーザー一覧</h2>
<% @users.each do |user| %>
<div>
<%= user.id %>人目: <%= link_to user.name, user_path(user.id) %>
</div>
<% end %>
</div>
...
詳細画面
<h1> 詳細画面 </h1>
<div>
<h2>ユーザー詳細</h2>
<div>
<%= @user.id %>人目: <%= @user.name %>
</div>
<div>
<%= link_to '一覧画面へ', users_path %>
</div>
</div>
...
Userのデータは別途作成をお願いします。
面倒な方はデータを挿入することができるseedsを活用することをお勧めします。
seeds.rbを利用したデータの挿入
db/seeds.rbに以下の記述をした後に、記述した内容を読み込むコマンドを打ちます。
User.create([
{ name: '田中一郎' },
{ name: '田中次郎' },
{ name: '山中一郎' },
{ name: '山中次郎' }
])
rails db:seed
画面確認
ここまでで問題がなければ、一覧画面に幾つか投稿がされていると思います。
それぞれの名前をクリックすると、詳細画面(users/ユーザーの番号)に飛ぶようになっています。
また、例えばURLに直接users/1
やusers/2
などを打ち込んでも画面遷移ができると思います。
3. 詳細画面の表示
さて、ここから画面遷移の流れを追ってみましょう。
今回、一覧画面(/users)から、詳細画面(/users/:id)に遷移しています。
例えば、一覧画面からuserの名前をクリックしたり、URLに直接users/1
やusers/2
などを打ち込むと、ターミナルにこのような結果が出ていると思います。
Started GET "/users/1" for ::1 at 2023-11-08 22:08:48 +0900
Processing by UsersController#show as HTML
Parameters: {"id"=>"1"}
...
ここに書かれている処理をなるべく日本語で追ってみます。
- Started GET /users/1
- HTTPメソッドがGETで、URI(URL)が/users/1 という場所にアクセスした
- Processing by users#show
- users_controllerのshowアクションという場所を動かします
- Parameters: {"id"=>"1"}
- パラメータとして、合言葉(キー)を「id」として、値(バリュー)が「1」というのが送られた
今回は詳細画面(show.html.erb)に遷移していたので、users_controllerのshowアクションがきちんと動いていることも確認できました。
一度ルーティングの結果を思い出しながら、URLとログに残っているコントローラとアクションが対応しているかどうかを確認しておくといいでしょう。下のようになっているはずです。
- /users の時は、users#index
- /users/1やusers/2の時は、users#show
その時のターミナルに表示されているログとして、parametersも確認しましょう。
-
Started GET /users/1
の時には、Parameters: {"id"=>"1"}
-
Started GET /users/2
の時には、Parameters: {"id"=>"2"}
つまりルーティングで表示されていた/users/:id
のうち、:id
の部分が1や2の数字に置き換わっていることや、その数字がParametersで送られてきていることが確認できます。
さて、users_controller#show
(users_controllerのshowアクションの意)に以下の記述がありました。
...
def show
@user = User.find(params[:id])
end
...
この記述を括弧の中身から見ていきます。
params(キー)
パラメータの中から、合言葉(キー)で値(バリュー)を取り出す。
find( ID )
IDを指定してレコードを取得
今回はUserの中から、主キー(出席番号みたいに順番に割り振られている数字)を元に探す。
今回受け取っているparamsが、ターミナルを見て{id => 1}となっていれば、params[:id]
という記述で得られる値は「1」になります。
それがfindメソッドに与えられているので、user.find(1)となり、1番目のユーザーを探すことになります。
見つけたユーザーが@user
に渡され、さらにviewに渡され、詳細画面にユーザーが表示されます。
裏を返せば、いつどんな時でもparams[キー]が使えるわけではなく、あくまでパラメータが送られてくる時に限られます。
パラメータが送られてきていない時にparams[:id]を使うとこんなエラーになります。
とりあえず、indexアクションの時にはパラメータが送られてこない(後述)ので、indexアクションでparams[:id]を試してみましょう。
実際に一覧画面に遷移した時のターミナルを追ってみても、Parameters: {"id"=>"1"}なんて記述は見当たらないと思います。
class UsersController < ApplicationController
def index
- @users = User.all
+ @user = User.find(params[:id])
end
...
上記のように書き換えたうえで一覧画面のusers
にアクセスすると下のようなエラー画面が出ます。
先ほどの、id=5のUserが見つからないエラーと似ていますが、微妙に違います。
@users
= User.find(params[:id])の箇所で
Couldn't find User without an ID と書かれているので、「ID無しでUserは見つけられないよ」というエラーが出ています。
前述した通り、find( )では、カッコの中に1や2などのIDを欲しています。
しかし、今回はそもそもパラメータが送られてきていないので、params[:id]
が空っぽになってしまいます。
そのため、User.find(空っぽ)となってしまい結局誰を探せばいいのか分からないため、「ID無しでUserは見つけられないよ」というエラーが出ています。
ここまで詳細画面表示におけるparamsについて流れを追ってみました。詳細画面表示時のparams[:id]
が何をしているのかイメージできたら幸いです。
4. 検索機能の実装
さて、paramsについて理解が深まったところで、検索機能に取り組んでいきます。
検索機能といっても難しいことはせずに、あくまでparamsの応用として実装に挑戦しましょう。
その前に、パラメータを2種類挙げます。
- パスパラメータ
ルーティングに、
:id
のようなものが入っているURLにアクセスした時に送られてくるパラメータのこと。
今回であれば/users/:id
という設定で、users/1
にアクセスした時に、パスパラメータとして{id => 1}
が送られてきます。
また、一覧画面のURLは/users
であり、URLに:id
が含まれていないため、このパスパラメータというのが送られてきません。
- クエリパラメータ
URLに?qみたいな文字が入っている場合に送られてくるパラメータのこと。
youtubeなどで検索すると、https://www.youtube.com/results?search_query=プログラミング
みたいなURLになります。
今回作る検索機能では、パスパラメータでも実装できますがクエリパラメータで実装していきます。
なぜかというと、パスパラメータで作成する場合にはusers/:serch_word
のような、ルーティングを新たに作り、viewやcontroller、actionも別途作らなければならないためです。
対してクエリパラメータの場合は、既存の/users
というURLをそのまま活用することができます。
では、実際にクエリパラメータの送受信をしてみましょう。
まずは受け取り側です。
class usersController < ApplicationController
def index
- @users = User.all
+ q = params[:search_word]
+ if q.present?
+ @users = User.where(name: q)
+ else
+ @users = User.all
+ end
end
...
present?
存在しているかを判断するメソッドです。
そもそもパラメータが送られてきていない時のために用意する必要があります。
where(カラム名: 条件)
データの中から条件にマッチしたものを全て抽出するメソッドです。
今回はUserデータの中から、nameカラムと検索ワードが一致しているものを探しています。
コントローラーの記述の説明をします。
params[キー]で実際に送られてきた値をqという変数に一度置いてあげて、
- もしqに中身があれば、Userデータの中から、nameカラムの中身とqが一致しているユーザだけを抽出して、それを
@users
としてviewに渡す。 - qが空っぽであれば、そのままユーザ全員を
@users
としてviewに渡す。
これで、paramsを使った検索機能完了になります。
<h1> 一覧画面 </h1>
+ <div>
+ <h2>検索</h2>
+ <%= form_with url: users_path, method: :get do |f| %>
+ <%= f.text_field :search_word %>
+ <%= f.submit '検索' %>
+ <% end %>
+ </div>
<div>
<h2>ユーザー一覧</h2>
<% @users.each do |user| %>
<div>
<%= user.id %>人目: <%= link_to user.name, user_path(user.id) %>
</div>
<% end %>
</div>
今加筆したのは、:search_word
というキーワードを、users_pathという場所に, HTTPメソッドをgetで送ります。という内容です。
まずは適当に名前を入れてみて検索してみたりしてみましょう。また、先ほどのパスパラメータの確認同様に、ターミナルを見てみましょう。
注目するところは、URLとターミナルのパラメータの部分です。
URLを見てみると、/usersの後ろにsearch_word=田中一郎&commit=検索
というものがくっついています。
これで、あくまでメインのURLは/usersだけど、検索ワードがURLにくっついている、という状態になります。
さらに、ターミナルから、パラメータが送られていることも確認しましょう。
ターミナルの中から、下記のようなものを見つけられましたか?
Parameters: {"search_word"=>"鈴木一郎", "commit"=>"検索"}
...省略...
Parameters: {"search_word"=>"", "commit"=>"検索"}
...省略...
Parameters: {"search_word"=>"田中一郎", "commit"=>"検索"}
先ほどのパスパラメータの時と同様に、パラメータが送られてきていることが確認できました。
パラメータが送られてきているのであれば、あとはparamsで受け取ってあげましょう。
今一度コントローラの記述を見返して、パラメータを受け取っている流れを確認しましょう。
5. まとめ
本記事ではRuby on Railsにおけるparamsに焦点を当て、パラメータの受け渡しの流れを追うことで、検索機能の実装を行いました。
しかし現状の検索機能として、名前と検索文字が完全一致しているデータしか検索できません。
例えば、以下のような一部分だけに着目した検索はparamsとは別にもう一工夫する必要があります。
- 名前が「田中」から始まるユーザー
- 名前が「一郎」で終わるユーザー
- 名前に「中」が含まれているユーザー
検索機能の幅を効かせるためにもぜひ取り組んでみてください!