開発環境
Mac OS Catalina 10.15.7
ruby 2.6系
rails 6.0系
前提
前提として、現在私が作っている、記事を投稿できるアプリを例に説明します。
私はモデル名はpostモデルとしています。(参考にする際は、自分用に置き換えてください。)
ルーティング設定
まずはルーティングを設定します。
今回はsearchアクションという、基本アクション(index,new,create,show,edit,update,destroy)以外のアクションを定義する必要があるため、collectionを使い設定します。
Rails.application.routes.draw do
devise_for :users
root to: "posts#index"
resources :posts do
resources :comments, only: [:create, :destroy]
collection do
get 'search'
end
end
resources :users, only: :show
end
collectionを使うと、ルーティングのURLと実行されるコントローラーを任意にカスタムできます。
また、ルーティングにidを含めたいときはmemberが使えます。
member do
get 'search'
end
単純にcollectionのところをmemberにするだけです。
今回の私の例ではidは必要ないので、collectionを使っています。
検索フォーム作成
ルーティングができたので、ビューに配置する検索フォームを作っていきます。
<%= form_with(url: search_posts_path, local: true, method: :get, class: "search-form") do |form| %>
<%= form.text_field :keyword, placeholder: "投稿を検索する", class: "search-input" %>
<%= form.submit "検索", class: "search-btn" %>
<% end %>
いろいろ書き方があるとは思いますが、自分はこんな感じで習いました。
ポイントは先ほどのルーティング設定で生成したパス(search_posts_path)が入っていること(パスの名前はコンソールで確認しましょう)、それとmethod: :getを忘れないことぐらいですかね。
加えてform_withのtext_fieldのキー名はkeywordとしました。
モデルに検索の処理を記述
それでは実際に検索する際の処理をモデルに書いていきます。
このとき、コントローラーに書いてしまいたくなる気持ちはわかりますが、テーブルとやり取りをするようなメソッドを定義する際は極力モデルに書くようにしましょう。
def self.search以下(最下部のendは除く)が今回記述した内容です。
class Post < ApplicationRecord
extend ActiveHash::Associations::ActiveRecordExtensions
belongs_to_active_hash :category
belongs_to :user
has_one_attached :image
has_many :comments, dependent: :destroy
with_options presence: true do
validates :image
validates :title
validates :category_id, numericality: { other_than: 1 , message: "は--以外から選んでください"}
end
def self.search(search)
if search != ""
Post.where(['title LIKE(?) OR explanation LIKE(?) OR animal_name LIKE(?)', "%#{search}%", "%#{search}%", "%#{search}%"])
else
Post.includes(:user).order('created_at DESC')
end
end
end
基本的な書き方としては
オブジェクト名.where('検索をかけたいカラム名 LIKE(?)', "%#{search}%")
複数のカラムで検索をかけたいときは
オブジェクト名.where(['検索をかけたいカラム名 LIKE(?) OR 検索をかけたいカラム名 LIKE(?)', "%#{search}%", "%#{search}%"])
みたいな感じで実行できます。
自分の場合は3つのカラムで検索をかけたいので上記のような記述になります。
また、if文で検索欄が空だった場合には、postの一覧が表示するように記述しました。
コントローラー記述
次にコントローラーを記述します。
def search
@posts = Post.search(params[:keyword])
end
先ほどモデルに定義したsearchメソッドを使っています。
引数のparamsの中の[:keyword]は、検索フォームを作った際に設定したもので、text_fieldのキー名です。
ビュー作成
最後にsearchアクションが参照するビューを作成します。
別に作らなくても、renderでindexページに飛ばしたりもできると思います。
<%= render "shared/header"%>
<%= form_with(url: search_posts_path, local: true, method: :get, class: "search-form") do |form| %>
<%= form.text_field :keyword, placeholder: "投稿を検索する", class: "search-input" %>
<%= form.submit "検索", class: "search-btn" %>
<% end %>
<% if @posts == [] %>
<h2 class = "seach-result">検索結果はありません</h2>
<% else %>
<div class = "post-contents">
<ul class = "post-list">
<% @posts.each do |post| %>
<li class = "list">
<%= link_to post_path(post.id) do %>
<%= image_tag post.image, class: "post-img" %>
<h3 class='post-title'><%= post.title %></h3>
<p class = "root-show">〜クリックして詳細を見る〜</p>
<% end %>
</li>
<% end %>
</ul>
</div>
<% end %>
<%= link_to new_post_path, class: 'post-btn' do %>
<span class='post-btn-text'>投稿する</span>
<% end %>
<%= render "shared/footer" %>
以上がrailsにおける検索機能の実装方法です。
参考になれば幸いです。