概要
ActiveHashのデータでプルダウン検索する機能を実装する方法について、まとめます。
機能の実装には、ransackというgemを用いました。
ransackは複雑な検索を可能にするgemです。
今回の機能はシンプルな検索なので、ransackを用いなくてもできるかも知れません。
その点については今後の課題とします。
参照
- ActiveHashのGitHubページ
https://github.com/zilkey/active_hash - 【Rails】active_hashを使って疑似モデルを作ろう
https://pikawaka.com/rails/active_hash
- ransackのGitHubページ
https://github.com/activerecord-hackery/ransack - Ransackで簡単に検索フォームを作る73のレシピ
http://nekorails.hatenablog.com/entry/2017/05/31/173925
完成イメージ
今回は、写真投稿アプリを題材にカテゴリーを選択後、そのカテゴリーの写真一覧を表示する方法を説明します。
開発環境
- macOS Catalina 10.15.7
- ruby 2.6.5
- Rails 6.0.3.4
実装の流れ
- gem ransackをインストール
- ルーティングを設定
- コントローラーにアクションを設定
- 検索ボックスを作成
- 検索結果ビューを作成
今回のコード
コードは、必要な部分以外(CSSなど)は省略して記載します。
ActiveHashを用いた疑似モデルの作成方法についても省略します。疑似モデルの作成が完了しているという前提で話を進めます。作成方法については参照ページなどをご覧ください。
1. gem ransackをインストール
# ファイルの一番下に追記
gem 'ransack'
% bundle install
2. ルーティングを設定
get '/photo/category', to: "photos#category"
photosコントローラーにcategoryアクションを設定します。
categoryアクションが実行されると、カテゴリー検索結果のページが表示されるようにします。
3. コントローラーにアクションを設定
before_action :search_category_photo, only: [:index, :category, :hashtag, :search]
(省略)
private
def search_category_photo
@q = Photo.ransack(params[:q])
end
privateメソッドにsearch_category_photo
メソッドを作成します。これを実行すると、@q = Photo.ransack(params[:q])
という検索オブジェクトが作成されます。
今回、検索結果ボックスをヘッダーに格納しました。それにより、カテゴリー検索結果画面(:category)だけでなく、トップ画面(:index)、ハッシュタグ検索結果画面(:hashtag)、キャプション検索結果画面(:search)にも、検索結果ボックスが表示されています。そのため、before_action
で各画面のロード時にsearch_category_photo
メソッドが実行されるようにします。これがないとエラーがでました。この解決に苦労しました…。
def category
@photos = @q.result
category_id = params[:q][:category_id_eq]
@category = Category.find_by(id: category_id)
end
categoryメソッド内に@photos = @q.result
を記入し、検索結果を取得します。
params[:q]には検索パラメーターが格納されています。
例えば、ActiveHash疑似モデルのid:5のカテゴリーをプルダウンで選択した場合、params[:q]には、以下のものが格納されていました。
[1] pry(#<PhotosController>)> params[:q]
=> <ActionController::Parameters {"category_id_eq"=>"5"} permitted: false>
検索結果ビューで選択したカテゴリー名を表示するため、category_id = params[:q]:category_id_eq]
として、params[:q]内のcategory_id(この例では"5")を取得しました。そのcategory_idを用いて@category = Category.find_by(id: category_id)
として、選択したカテゴリー名を取得しました。
4. 検索ボックスを作成
以下は、今回作成したカテゴリーのActiveHash疑似モデルです。
class Category < ActiveHash::Base
self.data = [
{ id: 0, name: '--' },
{ id: 1, name: '犬' },
{ id: 2, name: '猫' },
{ id: 3, name: '鳥' },
{ id: 4, name: '魚' },
{ id: 5, name: '爬虫類' },
{ id: 6, name: '両生類' },
{ id: 7, name: '昆虫' },
{ id: 8, name: 'その他' }
]
include ActiveHash::Associations
has_many :photos
end
<%= search_form_for @q, url: photo_category_path do |f| %>
<%= f.collection_select :category_id_eq, Category.where.not(id: 0), :id, :name, include_blank: "カテゴリー検索" %>
<%= f.submit '検索' %>
<% end %>
ransackのヘルパーメソッドsearch_form_for
メソッドで、検索フォームを作成します。引数に検索オブジェクト@q
を渡しています(参照:『3. コントローラーにアクションを設定』)。urlのパス名はcategoryアクションのパス名です(rails routes
で確認しました)。
ヘルパーメソッドcollection_select
メソッドでプルダウンのカテゴリー選択ボックスを作成します。
注).collection_selectメソッドについて理解が不十分の可能性があります。以下、間違いがありましたらご指摘いただきたいです。
<%= f.collection_select ①:category_id_eq, ②Category.where.not(id: 0), ③:id, ④:name, ⑤include_blank: "カテゴリー検索" %>
①カラム名+ransackの検索メソッド
photosテーブルの:category_idで検索するため、カラム名は:category_id
とし、選択したカテゴリーと同じidの写真を取得するため、検索メソッドを-eq
としました。-eq
は条件に合った検索を行うメソッドです(イコールのイメージ)。
②プルダウンリストで使う配列データ
ActiveHashのCategoryモデルのデータを使用するため、モデル名Category
を引数としました。.where.not(id: 0)
でid: 0は表示せず、かわりに⑤でinclude_blank: "カテゴリー検索"
とし、未選択時は"カテゴリー検索"という文字が検索ボックス内に表示されるようにしました。
③検索に使うカラム名
Categoryモデルのidカラムの値を検索に使うため、:id
としました。
④プルダウンメニューに表示されるカラム名
プルダウンメニューには、Categoryモデルの:id
ではなく:name
の値を表示したいため、:name
としました。
5. 検索結果ビューを作成
カテゴリー検索結果ページの内容をcategory.html.erb
に記載します。
# 実装に必要な部分のみ記載しています
<h2>カテゴリー <%= @category.name %></h2>
<ul>
<% @photos.each do |photo| %>
<li>
<%= image_tag photo.image if photo.image.attached? %>
</li>
<% end %>
</ul>
</div>
@category
と@photos
は、上記、photosコントローラーのcategoryメソッドで設定したインスタンス変数です(参照:『3. コントローラーにアクションを設定』)。
@category.name
で、プルダウンリストから選択したカテゴリー名を取得し検索結果ビューに表示させました。
@photos
にはプルダウンリストから選択したカテゴリーの写真が格納されているので、.each
メソッドで1枚ずつ取り出しビューに表示させました。
おわりに
.collection_selectメソッド
の引数の入力方法が難しく、何度も試行錯誤して正解にたどり着きました。今回の記事が参考になりましたら幸いです。
初学者のため、間違いや改善点などありましたらご指摘お願いいたします。