はじめに
Railsでgem acts-as-taggable-on
を用いて、タグ機能を実装しました。
仕様としては、よくあるパターンでインスタンス作成画面でユーザーが任意のタグを入力して保存するというものです。
↓
実装イメージ
イメージのように
①new画面でインスタンスを作成
②confirm画面で入力内容を確認
③createアクション実行で一覧画面にリダイレクト
④タグをクリックすると、そのタグに紐づくインスタンスが一覧表示される
それでは実装に入ります!
前提(開発環境)
- macOS Catalina
- Ruby 2.6.5
- Ruby on Rails 6.0.0
- Visual Studio Code
- データベース: Mysql
- テンプレートエンジン: haml
目次
1.事前準備
2.ModelとController
3.View
1. 事前準備
今回はacts-as-taggable-onというGemを使うのですが、インストールの途中でエラーが発生してしまうので、そのエラーがでないように実装していきましょう。順番に記載します。
gem 'acts-as-taggable-on', '~> 6.0' #追加
% bundle install
% rails acts_as_taggable_on_engine:install:migrations
rails acts_as_taggable_on_engine:install:migrations
を実行すると必要なマイグレーションファイルが作成されます。通常はここでrails db:migrate
を実行してデータベースに反映させるのですが、Mysqlを使用している場合、ここで必要な処理を行わないとエラーが起きてしまいます。
もしも、migrateさせてしまった場合はrails db:rollback
でファイルを戻しましょう。
acts-as-taggable-on公式リファレンスの通りターミナルでコマンドを実行します。
% rails acts_as_taggable_on_engine:tag_names:collate_bin
次に先程生成されたマイグレーションファイルを編集します。(ファイル名に気をつけてください)
# This migration comes from acts_as_taggable_on_engine (originally 2)
if ActiveRecord.gem_version >= Gem::Version.new('5.0')
class AddMissingUniqueIndices < ActiveRecord::Migration[4.2]; end
else
class AddMissingUniqueIndices < ActiveRecord::Migration; end
end
AddMissingUniqueIndices.class_eval do
def self.up
add_index ActsAsTaggableOn.tags_table, :name, unique: true
# remove_index ActsAsTaggableOn.taggings_table, :tag_id if index_exists? (ActsAsTaggableOn.taggings_table, :tag_id) #コメントアウトする
if index_exists?(ActsAsTaggableOn.taggings_table, :tag_id) #追加
remove_foreign_key :taggings, :tags #追加
remove_index ActsAsTaggableOn.taggings_table, :tag_id #追加
end
remove_index ActsAsTaggableOn.taggings_table, name: 'taggings_taggable_context_idx'
add_index ActsAsTaggableOn.taggings_table,
[:tag_id, :taggable_id, :taggable_type, :context, :tagger_id, :tagger_type],
unique: true, name: 'taggings_idx'
end
def self.down
remove_index ActsAsTaggableOn.tags_table, :name
remove_index ActsAsTaggableOn.taggings_table, name: 'taggings_idx'
add_index ActsAsTaggableOn.taggings_table, :tag_id unless index_exists?(ActsAsTaggableOn.taggings_table, :tag_id)
add_index ActsAsTaggableOn.taggings_table, [:taggable_id, :taggable_type, :context], name: 'taggings_taggable_context_idx'
end
end
①11行目をコメントアウト
# remove_index ActsAsTaggableOn.taggings_table, :tag_id if index_exists?(ActsAsTaggableOn.taggings_table, :tag_id)
②12行目から以下の記述を追加
if index_exists?(ActsAsTaggableOn.taggings_table, :tag_id) #追加
remove_foreign_key :taggings, :tags #追加
remove_index ActsAsTaggableOn.taggings_table, :tag_id #追加
end
これで処理はバッチリです!
% rails db:migrate
次に一覧画面に表示しておくタグをseed.rbに入れておきます。
# タグ付紐付け(%w()の中に記述)
array = %w(ダンス 歌 撮影モデル サロン・カットモデル 演技 マジック アイドリング エキストラ 楽器演奏 リモート可能 東京 神奈川 埼玉 千葉 群馬 茨城 兵庫 北海道 大阪 京都 愛知 福岡 5000円~ 10000円~ 15000円~ 20000円~ 25000円~ 30000円~ 積極募集 Youtube広告モデル)
array.each{ |tag|
tag_list = ActsAsTaggableOn::Tag.new
tag_list.name = tag
tag_list.save
}
% rails db:seed
rails db:seed
がうまくいくとMysqlにデータが保存されると思います。
ここまでで事前準備は整いました。
2. ModelとController
タグを付与したいモデルに以下の記述を追加します。
class Event < ApplicationRecord
acts_as_taggable #追加
end
class EventsController < ApplicationController
def index
#一覧画面にタグを表示
@tags1 = ActsAsTaggableOn::Tag.where("id < ?", 10)
@tags2 = ActsAsTaggableOn::Tag.where(id: 11..22)
@tags3 = ActsAsTaggableOn::Tag.where(id: 23...29)
@tags4 = ActsAsTaggableOn::Tag.where(id: 29...31)
# タグ検索時にそのタグずけしているものを表示
if params[:tag_name]
@events = Event.tagged_with("#{params[:tag_name]}").includes(:recruiter)
else
@events = Event.all.includes(:recruiter)
end
end
def new
@event = Event.new
end
def confirm
@event = Event.new(event_params)
if @event.invalid?
render :new
end
end
def create
@event = Event.new(event_params)
@event.recruiter_id = current_user.id
render :new and return if params[:back] || !@event.save
if @event.save
redirect_to root_path
else
render :new
end
end
def show
@event = Event.find_by(id: params[:id])
end
private
def event_params
params.require(:event).permit(:event_name, :datetime, :prefecture, :place, :detail, :tag_list)
#ストロングパラメーターに :tag_listを追加
end
def set_user
@user = current_user
end
end
解説は後にまとめて行います。先にViewパートに移ります。
3.View
インスタンスにタグ付けを行う
= form_with(model: @event, url: confirm_events_path(@event), local: true, id: "new-event") do |f|
.
.
.create-item-2
= f.label :tag_list, "タグ付け"
= f.text_field :tag_list, value: @event.tag_list.join(','), placeholder: "ダンス,東京,・・・"
.
.
.create-btn-field
= f.submit "確認画面へ"
.create-item-2
= f.label :tag_list, "タグ付け"
= f.text_field :tag_list, value: @event.tag_list.join(','), placeholder: "ダンス,東京,・・・"
上記の部分をform_with
にネストさせることで**「@event
に対してタグを付与させますよ」**という意味になります。
url: confirm_events_path(@event)
となっていますが、確認画面を挟まない場合は= form_with(model: @event, local: true, id: "new-event") do |f|
で大丈夫です。
コントローラーの部分もconfirmアクションは必要ありません。
確認画面の実装はこちらを御覧ください
@event.tag_list.join(',')
カンマで区切ることによって、入力されたタグが配列の形でデータが保存されます。
一覧画面にタグを表示させる
%p.tag-word
ジャンルで検索
- @tags1.each do |genre|
= link_to "#{genre.name}(#{genre.taggings_count})", events_path(tag_name: genre.name), class: "tag-link"
コントローラーの部分で@tag1
はこのように定義されています
def index
#一覧画面にタグを表示
@tags1 = ActsAsTaggableOn::Tag.where("id < ?", 10)
意味としては**「seed.rbで事前に作成したタグの中からidが10番未満のタグを@tags1
に代入」**となります。
viewでは@tags1
をeach
メソッドで一覧表示しています。
残りの@tags2``@tags3``@tags4
も同じ手順で一覧画面に表示しています。
(#{genre.taggings_count})
.taggings_count
メソッドはタグに紐付いているインスタンスの数をviewに表示してくれます。
events_path(tag_name: genre.name)
タグをクリックすると、そのタグが紐付いているインスタンスを一覧表示します。
def index
.
.
if params[:tag_name]
@events = Event.tagged_with("#{params[:tag_name]}").includes(:recruiter)
else
@events = Event.all.includes(:recruiter)
end
end
Event.tagged_with("#{params[:tag_name]}")
とすることでタグが紐付いているインスタンスの情報を全て取得してくれます。
実装は以上です!
おわりに
最後まで読んでいただきありがとうございました!
お疲れ様でした。。
参考文献
Rails | acts-as-taggable-on を使ったタグ機能の実装 | 備忘録
Railsにタグ機能をつける。acts-as-taggable-on使用