🏷️

【Rails】gem:acts-as-taggable-on と Bootstrap Tags Input を使ったタグ機能① 実装編

2023/09/27に公開

gemを使ってタグ機能を実装します。
今回の記事ではタグの入力と表示までを実装します。

イメージ

  • タグ実装(posts.new)

    簡単に1個ずつのタグが入力可能!
    重複したタグは入力できないようになる!
    消したいタグだけ消すことが可能!

  • タグ表示(posts.show)

    タグ付けされている件数が表示可能!

  • タグ検索(tags.show)

環境

  • ruby 3.1.2
  • rails 6.1.7.4
  • DB 開発環境:SQLite / 本番環境:MySQL

ER図


gemを入れてpostテーブルと繋げます。

流れ

  1. gem導入
  2. モデルとコントローラに追記
  3. コントローラ作成
  4. ビュー作成
  5. Bootstrap Tags Inputの導入

gem導入

  1. Gemifileに追記し、bundle install。
Gemifile
gem 'acts-as-taggable-on'
ターミナル
bundle install
  1. 以下を入力
ターミナル
rails acts_as_taggable_on_engine:install:migrations

入力するとファイルがいっぱい作成されます。

  1. マイグレーションファイルの修正
    2つのマイグレーションファイルをコメントアウトして修正します。修正しないとSQLiteであれば使用できますが、MySQLへの本番環境のデプロイ時にエラーが発生します。(詳しくは以下の記事を参照ください)

https://qiita.com/mattan5271/items/726b2f2f1b6655516b9d

以下のようになっていればOK。

  • _add_missing_unique_indices.acts_as_taggable_on_engine
20230812064908_add_missing_unique_indices.acts_as_taggable_on_engine
# frozen_string_literal: true
# This migration comes from acts_as_taggable_on_engine (originally 2)

class AddMissingUniqueIndices < ActiveRecord::Migration[6.0]
  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)
    #remove_index ActsAsTaggableOn.taggings_table, name: 'taggings_taggable_context_idx'
    #add_index ActsAsTaggableOn.taggings_table,
    #          %i[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, %i[taggable_id taggable_type context],
    #          name: 'taggings_taggable_context_idx'
  end
end
  • _add_missing_taggable_index.acts_as_taggable_on_engine
20230812064910_add_missing_taggable_index.acts_as_taggable_on_engine
# frozen_string_literal: true
# This migration comes from acts_as_taggable_on_engine (originally 4)

class AddMissingTaggableIndex < ActiveRecord::Migration[6.0]
  def self.up
  # add_index ActsAsTaggableOn.taggings_table, %i[taggable_id taggable_type context],
  #            name: 'taggings_taggable_context_idx'
  end

  def self.down
  #  remove_index ActsAsTaggableOn.taggings_table, name: 'taggings_taggable_context_idx'
  end
end
  1. マイグレーションをする
ターミナル
rails db:migrate

モデルとコントローラに追記

モデル追記

使用したいモデルに以下を追記します。

post.rb
class Post < ApplicationRecord
  # gem:acts_as_taggableの使用
  acts_as_taggable_on :tags
:

上記により、Postモデルにタグ付けできるようになりました。

コントローラ追記

タグ表示のためのアクションと、コントローラのストロングパラメータにtag_listを追記します。
俺の場合はshowとindexでタグを表示させます。

posts.controller.rb
class Public::PostsController < ApplicationController
  :
  
  def index
    case params[:sort]
    when 'favorites'
      @posts = Post.sort_by_favorites.page(params[:page]).per(12)
    when 'popular'
      @posts = Post.sort_by_impressions_count.page(params[:page]).per(12)
    else
      @posts = Post.published.order(created_at: :desc).page(params[:page]).per(12)
    end

    @posts.each do |post|
      impressionist(post, nil, unique: [:session_hash, :user_id])
    end
  end
  
  def new
    @post = Post.new
  end
  
  def show
    @post = Post.find(params[:id])
+   @tags = @post.tag_counts_on(:tags)
  end

  private

  def post_params
+   params.require(:post).permit(:title, :body, :link, :tag_list, :status)
  end

解説:
上記で分かる通りindexアクションにはタグを追加していません。明示的に追加しなくとも、タグはindexや他のアクションでも問題なく表示されました。どうやら1つのアクションでタグ情報を取得すれば(今回はshowで取得)、他のアクションで追加しなくもビューで表示できるようです(便利!)

今回は、そのタグの使用回数をカウントしたタグを表示たいため、tags_counts_onを使っています。

メソッドは以下を参照ください。

メインメソッド 説明
tag_list 特定のモデルのインスタンスに関連付けられたタグのリストを取得します。
tag_list.add("tag_name") 特定のモデルのインスタンスに新しいタグを追加します。
tag_list.remove("tag_name") 特定のモデルのインスタンスからタグを削除します。
tagged_with("tag_name") 特定のタグ名に関連付けられたモデルのインスタンスを取得します。
tag_counts モデルに関連付けられたタグとその使用回数をカウントし、リストとして取得します。
most_used 最も使用されているタグを取得します。

ビュー作成

タグ付けのフォーム

posts/new.html.erb
:
<%= form_with model: @post do |f| %>
:
<div class="form-group form-group_tags">
  <%= f.label :tag_list, "Tag", class: "form-label" %>
  <%= f.text_field :tag_list, value: @post.tag_list.join(","), class: "form-control", data: { role: "tagsinput" } %>
</div>
:

解説:

  1. tag_list:
    テキストフィールドの名前としてtag_listを指定します。
  2. value: @post.tag_list.join(","):
    join(",")とすることで、このタグリストをカンマで区切った文字列に変換し、初期値として表示します。これにより、投稿の編集時に既存のタグが正しく表示されます。joinしないと2つのタグを結合してしまいます。
  3. data: { role: "tagsinput" }: tagsinput]:
    Bootstrap Tags Inputの導入。詳細は以下で説明します。

タグ表示画面

posts/show.html.erb
<div class="tags">
  <% if @tags.present? %>
    <div class="d-flex flex-wrap">
      <% @tags.each do |tag| %>
	<span class="badge badge-info mr-2 mb-2">
	  <%= link_to "#{tag.name}(#{tag.taggings_count})", tag_path(tag.name), class: "text-white" %>
	</span>
      <% end %>
    </div>
  <% else %>
    <p>登録されているタグはありません</p>
  <% end %>
</div>

このようにすると以下のように表示することができます。

Bootstrap Tgas Input導入

ライブラリのインストール

インストール方法はいくつかありますが、今回はyarnを使用しインストールします。

ターミナル
yarn add bootstrap-tagsinput

ライブラリのインポートとタグ入力フィールドの有効化

以下ファイルでインポートし、JavaScriptのコードも追加します。
これにより、タグ入力フィールドが有効になります。

app/javascript/packs/application.js
import 'bootstrap-tagsinput';

$(document).ready(function() {
  $("input[data-role='tagsinput']").tagsinput();
});

htmlでタグ入力フィールドの作成

上述のタグ付フォームで記載済みです。

  <%= f.text_field :tag_list, value: @post.tag_list.join(","), class: "form-control", data: { role: "tagsinput" } %>
  • data: { role: "tagsinput" }: tagsinput]:
    role属性に指定された値:tagsinputを使用して、JavaScriptの操作ができるようにします。

参考記事

https://qiita.com/manbolila/items/3f178b7698d877a29b12
https://qiita.com/d510/items/e1caa8b73d118822a0a2
https://qiita.com/mattan5271/items/726b2f2f1b6655516b9d
https://hirocorpblog.com/post-160/


以上で完成。

Discussion