Railsアプリでの検索フォームの作り方

f:id:endoakak:20200727195612j:plain

投稿を検索する機能の作り方です。

こういうやつですね。

f:id:endoakak:20200827193902p:plain:w200

ルーティングを設定

まずはルーティングを設定します。

resourcesの7種類には含まれないアクションを作るので、collectionを使います。config/routes.rbに以下のように記述します。

Rails.application.routes.draw do
  略
  resources :posts do
    collection do
      get "search"
    end
  end
end

これで、postsコントローラーのsearchアクションへのルーティングが設定できました。

f:id:endoakak:20200827190134p:plain

rails routesで調べてみると、search_postsという名前がついています。

ビューに検索フォームを作成

パスが作成できたので、ビューファイルにフォームを作ります。

<%= form_with url: search_posts_path, local: true, method: :get, class: "search-form" do |f| %>
  <%= f.text_field :keyword, placeholder: "投稿を検索する", class: "search-input" %>
  <%= f.submit "検索", class: "search-btn" %>
<% end %>

検索するワードには、:keywordという名前をつけておきました。

form_withはデフォルトではAjax通信を行うので、local: trueの記述でAjaxを無効にする必要があります。そして、HTTPメソッドはgetに変更しておきます。

コントローラーにsearchアクションを作成

入力されたキーワードで検索した投稿を受け取るため、app/controllers/posts_controller.rbでsearchアクションを定義します。

@posts = Post.search(params[:keyword])

しかしsearchというメソッドは存在しないので、自分で定義する必要があります。

モデルでsearchメソッドを定義

入力されたキーワードを含む投稿をデータベースから検索するメソッドを、モデルで定義します。データベースに関するメソッドはモデルで定義する、という意識が重要です。

class Post < ApplicationRecord
  def self.search(keyword)
    if keyword != ""
      Post.where('text LIKE(?)', "%#{keyword}%")
    else
      Post.all
    end
  end
end

クラスメソッドなので、self.method()というようにメソッド名の前にself.をつけて定義します。

入力されたキーワードが空であるかを判断し、空だった場合は全ての投稿を返すようにしています。


実際に検索をするのに必要なのが、LIKE句というものです。

モデル.where("カラム名 LIKE(?)", "検索したい文字列")

という感じで使います。


検索したい文字列は、"%"と"_"を使って表現します。

"%"は任意の文字列、"_"は任意の一文字を意味しています。

例えば"_#{keyword}%"とすると、keywordの前に一文字、後ろに0文字以上の文字が並ぶ文字列を探してきます。


よって上の例で言えば、postsテーブルの中で、textというカラムの値にkeywordを含んでいるレコードを全て取得してくる、ということになります。

検索結果を表示するビューを用意

最後に、検索して得られた結果を表示するためのビューファイルを用意したら完了です。

今回は、postsコントローラーのsearchアクションに対応するビューが必要なので、app/views/posts/search.html.erbというファイルを作成します。

まとめ

  • 検索機能をつけるにはモデルでsearchメソッドを定義
  • 文字列の検索にはLIKE句