68
9

【Ruby on Rails】paramsについて理解しながら検索機能を作ろう

Last updated at Posted at 2023-12-05

【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

モデル

モデル名はUserで、nameカラムを持っています。
ER.drawio.png

コントローラ

users_controller.rb
class UsersController < ApplicationController
  def index
    @users = User.all
  end
  
  def show
    @user = User.find(params[:id])
  end
end

...

ルーティング

resourcesを使って次のようにします。

routes.rb
Rails.application.routes.draw do
    resources :users
end

この記述で生成されるルーティングを確認しておきましょう。

terminal
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

一覧画面

users/index.html.erb
<h1> 一覧画面 </h1>

<div>
    <h2>ユーザー一覧</h2>
    <% @users.each do |user| %>
        <div>
            <%= user.id %>人目: <%= link_to user.name, user_path(user.id) %>
        </div>
    <% end %>
</div>

...

詳細画面

users/show.html.erb
<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に以下の記述をした後に、記述した内容を読み込むコマンドを打ちます。

db/seeds.rb
User.create([
  { name: '田中一郎' },
  { name: '田中次郎' },
  { name: '山中一郎' },
  { name: '山中次郎' }
])
terminal
rails db:seed

画面確認

ここまでで問題がなければ、一覧画面に幾つか投稿がされていると思います。

index-show.gif

それぞれの名前をクリックすると、詳細画面(users/ユーザーの番号)に飛ぶようになっています。
また、例えばURLに直接users/1users/2などを打ち込んでも画面遷移ができると思います。

そもそもUserが数名登録されていないといけません。
例えば5人目のユーザーがいない状態で/users/5にアクセスすると、エラーになります。

レコードがない場合、こんなエラーがでます。

SS.png

これは、@user = User.find(params[:id])の箇所でエラーが出ていて、具体的にはCouldn't find User with 'id'=5と怒られています。
つまり、「Userに対して、idが5のものは見つけれらなかった」という内容です。

3. 詳細画面の表示

さて、ここから画面遷移の流れを追ってみましょう。
今回、一覧画面(/users)から、詳細画面(/users/:id)に遷移しています。

例えば、一覧画面からuserの名前をクリックしたり、URLに直接users/1users/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アクションの意)に以下の記述がありました。

users_controller.rb
...
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"}なんて記述は見当たらないと思います。

users_controller.rb
class UsersController < ApplicationController
  def index
-   @users = User.all
+   @user = User.find(params[:id])
  end

...

上記のように書き換えたうえで一覧画面のusersにアクセスすると下のようなエラー画面が出ます。

スクリーンショット 2023-11-06 0.14.28.png

先ほどの、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をそのまま活用することができます。

では、実際にクエリパラメータの送受信をしてみましょう。
まずは受け取り側です。

users_controller.rb
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を使った検索機能完了になります。

index.html.erb
<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で送ります。という内容です。
まずは適当に名前を入れてみて検索してみたりしてみましょう。また、先ほどのパスパラメータの確認同様に、ターミナルを見てみましょう。

search-test.gif

注目するところは、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とは別にもう一工夫する必要があります。

  • 名前が「田中」から始まるユーザー
  • 名前が「一郎」で終わるユーザー
  • 名前に「中」が含まれているユーザー

検索機能の幅を効かせるためにもぜひ取り組んでみてください!

68
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
68
9