#acts-as-taggable-onを使用したタグ付け機能
・ここでは、gem 'acts-as-taggable-on'を使ったタグ機能の実装について書かせていただきますの、どうぞよろしくお願いします。
・僕自身が上記のgemを使ってエラーが頻繁に出たので、それらを解決したい方に是非読んでいただきたいと思います。
※また、こちらの記事の修正点がある場合はコメントして頂けると幸いです。
##開発環境
Rails 6.0.3.2
ruby 2.6.5
acts-as-taggable-on バージョン 6.0.0
データベース mySQL
##実装内容
###1, gemを追加してインストール
gem 'acts-as-taggable-on', '~> 6.0' #追加
$ bundle install
###2, マイグレーションファイルをインストール
$ rails acts_as_taggable_on_engine:install:migrations
成功するとマイグレーションファイルが生成される。
あとで記載する
###3, マイグレーションを実行
$ rails db:migrate
Mysql2::Error: Cannot drop index 'index_taggings_on_tag_id': needed in a foreign key constraint: DROP INDEX `index_taggings_on_tag_id` ON `taggings`
~
ActiveRecord::StatementInvalid: Mysql2::Error: Cannot drop index 'index_taggings_on_tag_id': needed in a foreign key constraint: DROP INDEX `index_taggings_on_tag_id` ON `taggings`
~
Mysql2::Error: Cannot drop index 'index_taggings_on_tag_id': needed in a foreign key constraint
しかし、ここでエラーが発生。
※筆者は、ここで原因が分からず数日悩みました。
調べて見るとDBを「mySQL」にしている場合は初期導入で色々問題があるようなのでmigrateを実行する前にrails acts_as_taggable_on_engine:tag_names:collate_bin
を実行する必要があります。
※tagsテーブルのnameカラムは'binary encoded string'(パソコンで扱う2真数の文字列)として読み込まれるので、'utf8_bin'で読まなければいけないらしいです。
$ rails acts_as_taggable_on_engine:tag_names:collate_bin #実行
また、gemのバグで、
acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb
のマイグレーションファイルの15行目で外部キーを設定しているにも関わらず、
t.references :tag, foreign_key: { to_table: ActsAsTaggableOn.tags_table }
add_missing_unique_indices.acts_as_taggable_on_engine.rb
の11行目で外部キーを削除せずにインデックスを削除しようとしているためエラーが出ます。
remove_index ActsAsTaggableOn.taggings_table, :tag_id if index_exists?(ActsAsTaggableOn.taggings_table, :tag_id)
ここで、GitHubのissuesから不具合報告の内容を確認
※上記の1文は削除し、下記4文を追加してください
(add_missing_unique_indices.acts_as_taggable_on_engine.rb)
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 #追加
・これでマイグレーションファイルが導入できます!
バグ解消前にrails db:migrateを実行した場合は、ArgumentErrorが出ます。
一度目のmigrate実行時に最初のファイルだけ読み込んでるからですね!
その場合はrails db: rollbackでmigrateを実行する前の状態に戻しましょう。
※こういった、gemのバグは今後も考えて検索しないといけなくなりますね。。。
###4, モデルとコントローラーに記述を追加
・タグ付けしたい記事等のモデルとコントローラーを作成後、ファイルに下記のような内容で記述しましょう。
※今回は、postとしてモデルとコントローラーを作成しています。
class Post < ApplicationRecord
acts_as_taggable #追加(acts-as-taggable-onの別名)
~
end
class PostsController < ApplicationController
def index
@post = Post.includes(:user,:tags).all.order('created_at DESC')
end
def new
@post = Post.new
end
def create
@post = Post.new(post_params)
if @post.save
flash[:notice] = "投稿が保存できました"
redirect_to root_path
else
@post = Post.new
flash.now[:alert] = "投稿できません、もう一度入力してください"
render :new
end
#略
private
def set_post
@post = Post.find(params[:id])
end
def post_params
#tag_listをpermitに追加
params.require(:post).permit(:title, :text, :tag_list).merge(user_id: current_user.id)
end
end
###5, ビューファイル編集(保存ページ)
# 略
= form_with(model: @post, local: true) do |f|
# 略
.form-contents__tag
= f.label :tag_list
= f.text_field :tag_list, value: @post.tag_list.join(","), autocomplete: "tag", class: "tag-field", placeholder:'タグを入力してください'
これでDBに値が保存されます!
tagsテーブルの[name]カラムに入力したタグと同じ名前があればレコードは増えずに、[taggings_count]のカウントが増えていきます。
###6, ビューファイル編集(一覧表示)
続いてDBに保存されたタグを表示させます。
def index
@post = Post.includes(:user,:tags).all.order('created_at DESC')
if params[:tag_name]
@posts = Post.tagged_with("#{params[:tag_name]}")
end
end
indexアクションにて一覧表示させます。
N+1問題を考慮してincludes(:tags)を記述しましょう。
- @post.each do |post|
%ul.posts__left__group__post-tag
##最後に
長くなりましたが、acts-as-taggable-onを使用したタグ付け機能はここまでで実装ができます。
実装時には、3,マイグレーション実行の際には、ぜひお気をつけてください。
acts-as-taggable-onは他にもタグの使用頻度で検索を掛けれたりと便利なメソッドがあるので、一度リファレンスを見ていただけると良いかなと思います!
GitHub acts-as-taggable-on
以下、導入編同様に参考にさせていただいた記事になります。
Rails | acts-as-taggable-on を使ったタグ機能の実装 | 備忘録