LoginSignup
2
3

More than 3 years have passed since last update.

【Ruby on Rails】タグ付機能/タグ絞り込み機能の実装

Posted at

はじめに

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を使うのですが、インストールの途中でエラーが発生してしまうので、そのエラーがでないように実装していきましょう。順番に記載します。

Gemfile
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公式リファレンスの通りターミナルでコマンドを実行します。
スクリーンショット 2020-12-23 10.49.07.png

ターミナル
% rails acts_as_taggable_on_engine:tag_names:collate_bin

次に先程生成されたマイグレーションファイルを編集します。(ファイル名に気をつけてください)

db/migrate/・・・・_add_missing_unique_indices.acts_as_taggable_on_engine.rb
# 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行目をコメントアウト

db/migrate/・・・・_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)

②12行目から以下の記述を追加

db/migrate/・・・・_add_missing_unique_indices.acts_as_taggable_on_engine.rb
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に入れておきます。

db/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にデータが保存されると思います。
スクリーンショット 2020-12-23 11.17.07.png

ここまでで事前準備は整いました。

2. ModelとController

タグを付与したいモデルに以下の記述を追加します。

app/models/event.rb
class Event < ApplicationRecord
    acts_as_taggable  #追加
end
app/controllers/events_controller.rb
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

インスタンスにタグ付けを行う

views/events.new.html.haml
 = 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 "確認画面へ"
views/events.new.html.haml
.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(',')

カンマで区切ることによって、入力されたタグが配列の形でデータが保存されます。

一覧画面にタグを表示させる

views/events.html.haml
  %p.tag-word
    ジャンルで検索
   - @tags1.each do |genre|
     = link_to "#{genre.name}(#{genre.taggings_count})", events_path(tag_name: genre.name), class: "tag-link"

コントローラーの部分で@tag1はこのように定義されています

app/controllers/events_controller.rb
def index
  #一覧画面にタグを表示
    @tags1 = ActsAsTaggableOn::Tag.where("id < ?", 10)

意味としては「seed.rbで事前に作成したタグの中からidが10番未満のタグを@tags1に代入」となります。
viewでは@tags1eachメソッドで一覧表示しています。
残りの@tags2@tags3@tags4も同じ手順で一覧画面に表示しています。

(#{genre.taggings_count})

.taggings_countメソッドはタグに紐付いているインスタンスの数をviewに表示してくれます。

events_path(tag_name: genre.name)

タグをクリックすると、そのタグが紐付いているインスタンスを一覧表示します。

app/controllers/events_controller.rb
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使用

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3