Railsの検索とフィルタの設計!初心者でもわかるParamsとクエリオブジェクトの使い方
生徒
「Railsで一覧表示の検索や絞り込みをしたいんですが、どこに書けばいいのかわかりません…」
先生
「それなら、コントローラをスッキリ保ちながら検索を実装できる方法がありますよ。」
生徒
「パラメータをどう処理するのかもよく分からなくて…」
先生
「それでは、Paramsオブジェクトとクエリオブジェクトを使って、検索とフィルタ処理を分かりやすく設計する方法を学びましょう!」
1. Railsで検索・フィルタをする場面とは?
Webアプリケーションでは、データの一覧画面で検索やフィルタ(絞り込み)が必要になる場面がよくあります。たとえば、ブログ記事をタイトルで検索したり、カテゴリーで絞り込んだりするケースです。
Railsでは、こういった機能をコントローラに直接書くこともできますが、それではコードが長くなり、メンテナンスがしづらくなってしまいます。そこで、検索専用の処理を切り出す「クエリオブジェクト」や、パラメータを整理して使いやすくする「Paramsオブジェクト」が登場します。
2. パラメータとは?Paramsの基本
パラメータとは、ユーザーが検索フォームなどから送信する「条件」のことです。Railsでは、この情報はparamsというハッシュの形でコントローラに渡されます。
たとえば、タイトルを検索するフォームで「Ruby」と入力した場合、以下のようなリクエストになります。
<form action="/posts" method="get">
<input type="text" name="title" value="Ruby">
<button type="submit">検索</button>
</form>
このフォームを送信すると、URLには?title=Rubyのようにパラメータが付きます。Railsではこれを次のように受け取ります。
params[:title] #=> "Ruby"
3. フィルタ処理をコントローラに書くとどうなる?
検索や絞り込みの処理をすべてコントローラに書くと、次のようにゴチャゴチャしてしまいます。
def index
posts = Post.all
posts = posts.where("title LIKE ?", "%#{params[:title]}%") if params[:title].present?
posts = posts.where(category: params[:category]) if params[:category].present?
render :index, locals: { posts: posts }
end
このように、条件が増えるとコントローラの中身がどんどん複雑になっていきます。これでは、コードの再利用やテストがしづらくなります。
4. クエリオブジェクトで検索処理を整理しよう
そこで登場するのがクエリオブジェクトです。検索専用のクラスを作ることで、ロジックをスッキリまとめることができます。
次のようなPostSearchクラスを作成します。
class PostSearch
def initialize(params)
@params = params
end
def results
scope = Post.all
scope = scope.where("title LIKE ?", "%#{@params[:title]}%") if @params[:title].present?
scope = scope.where(category: @params[:category]) if @params[:category].present?
scope
end
end
コントローラ側は次のようにシンプルになります。
def index
search = PostSearch.new(params)
render :index, locals: { posts: search.results }
end
これにより、ロジックと表示の分離ができ、保守性の高いコードになります。
5. Paramsオブジェクトでパラメータの取り扱いを安全に
さらに一歩進めて、Paramsオブジェクトを使うことで、パラメータの安全性や意図の明確化ができます。
次のように、Strong Parametersの仕組みを使って安全なパラメータだけを許可します。
def search_params
params.permit(:title, :category)
end
これをクエリオブジェクトに渡せば、不正なパラメータが混入するのを防げます。
search = PostSearch.new(search_params)
6. 検索フォームとの連携
クエリオブジェクトとParamsオブジェクトを使えば、検索フォームともスムーズに連携できます。
例えば、indexページに以下のようなフォームを用意します。
<form action="/posts" method="get" class="mb-3">
<input type="text" name="title" placeholder="タイトルで検索" class="form-control mb-2">
<select name="category" class="form-select mb-2">
<option value="">すべてのカテゴリ</option>
<option value="tech">技術</option>
<option value="life">生活</option>
</select>
<button type="submit" class="btn btn-primary">検索</button>
</form>
検索条件を入力して送信すると、コントローラがクエリオブジェクトで結果を取得し、ビューに表示する仕組みです。
7. 使い方の工夫と応用
このような設計にすると、次のようなメリットがあります。
- テストしやすくなる(クエリオブジェクトを単体でテスト可能)
- 条件の追加や変更がしやすい
- 複数の画面で共通の検索処理を使い回せる
Railsでよくある検索機能やフィルタ機能を実装する際は、このように「責務を分ける設計」を意識すると、アプリ全体が読みやすく、拡張しやすくなります。