【Rails】投稿した写真の検索機能(フリーワード/カテゴリー/タグ)を実装したい


概要

投稿した写真を下記の方法で検索できるように実装をしていきます。

  • フリーワード検索
  • カテゴリー検索
  • タグ検索

解決方法

①searchコントローラーを生成して、フリーワード/カテゴリー/タグそれぞれのアクションを記述する。
②ビューファイル内にフリーワード/カテゴリー/タグそれぞれの検索時にコントローラーへ送るパラメーターとして、値(value)と検索方法(how)を指定する記述をする。
③パラメーター内のhowによって実行する検索アクションを条件分岐させる。

※前提として、すでにsearchコントローラーは完了しているとします。
導入方法は下記の通りです。

terminal
$rails g controller search
routes.rb
Rails.application.routes.draw do
  get 'search/search'

  --中略--

end

searchコントローラーを生成して、フリーワード/カテゴリー/タグそれぞれのアクションを記述する

app/controllers/search_controller.rb
class SearchController < ApplicationController
  def search
    @gender = Gender.find([2, 3, 4])
    @price = Price.find([2, 3, 4, 5, 6])
        #ActiveHashを使用してモデル内に直接記述することで、データベースへ保存せずにデータを取り扱う
    @tags = Tag.all.order('created_at DESC')
    @value = params['search']['value']#検索時にパラメーターとして送る値を代入
    @how = params['search']['how']#検索時にパラメーターとして送る検索方法を代入
    @datas = search_for(@how, @value)#検索結果を代入
  end

  private

  def keyword(value)
    Photo.where('title LIKE(?) OR description LIKE(?)', "%#{value}%", "%#{value}%")
        #photosテーブルのtitle, descriptionカラムからフリーワード検索
  end

  def match_gender(value)
    Photo.where(gender_id: value)
        #パラメーターのvalueと同じgender_idを持つデータを検索
  end

  def match_price(value)
    Photo.where(price_id: value)
        #パラメーターのvalueと同じprice_idを持つデータを検索
  end

  def match_tag(value)
    Photo.where(id: PhotoTag.select(:photo_id)#PhotoTagテーブルのphoto_idカラムのデータを取得
    .where(tag_id: value)#重複したphoto_idも含め、パラメーターのvalueと同じtag_idを持つデータを検索
    .group(:photo_id))#photo_idでグループ化して重複したphoto_idはまとめる
  end

ビューファイル内にフリーワード/カテゴリー/タグそれぞれの検索時にコントローラーへ送るパラメーターとして、値(value)と検索方法(how)を指定する記述をする

app/views/shared/_header.html.erb
<%= form_with(url: search_search_path, local: true, method: :get, class: "d-none d-md-inline-block form-inline ms-auto me-0 me-md-3 my-2 my-md-0") do |form| %>
      <div class="input-group">
          <%= form.text_field 'search[value]', placeholder: "キーワードで検索", class: "form-control", 'search[how]': "keyword" %>
          <%= form.submit "検索", class: "btn btn-primary", id: "btnNavbarSearch" %>
      </div>
<% end %>
app/views/shared/_side_nav.html.erb
--中略--
<% @gender.each do |gender| %>
    <%=link_to gender.name, search_search_path('search[value]': gender.id, 'search[how]': "match_gender"), class:"nav-link" %>
<% end %>

--中略--
<% @price.each do |price| %>
    <%=link_to price.name, search_search_path('search[value]': price.id, 'search[how]': "match_price"), class:"nav-link" %>
<% end %>

--中略--
<% @tags.each do |tag| %>
    <%=link_to "##{tag.name}", search_search_path('search[value]': tag.id, 'search[how]': "match_tag") , class:"nav-link" %>
<% end %>

--中略--

パラメーター内のhowによって実行する検索アクションを条件分岐させる

app/controllers/search_controller.rb
class SearchController < ApplicationController

--中略--

  private

--中略--

  def search_for(how, value)
      case how #パラメーターのhowによって検索アクションを条件分岐させる
      when 'match_gender'
        match_gender(value)
      when 'match_price'
        match_price(value)
      when 'match_tag'
        match_tag(value)
      else
        keyword(value)
      end
  end

参考