#はじめに
アソシエーションを使ってタグ機能を実装しました。
備忘録的な感じです。良かったら参考にしてみて下さいレベルです。ご了承ください。
用意するもの
簡単なCRUD機能がついたアプリ前提に書いています。
環境
Windows10
Ruby 2.6.6
Rails 6.0
#実装
1.モデルを作る
rails g model tag name:string
rails g model tweet_tag_relation tweet:references tag:references
カラムが空で保存されるのを防ぐために、null: false を先ほど作成したマイグレーションファイルに追加する。
class CreateTags < ActiveRecord::Migration[6.0]
def change
create_table :tags do |t|
t.string :name, null: false #null; falseをnameが空で保存されないように追加
t.timestamps
end
end
end
rails db:migrate
2.アソシエーションとバリデーションを書く
has_many :tweet_tag_relations, dependent: :destroy
has_many :tags, through: :tweet_tag_relations, dependent: :destroy
上記を追加
class Tag < ApplicationRecord
has_many :tweet_tag_relations, dependent: :destroy
has_many :tweets, through: :tweet_tag_relations, dependent: :destroy
end
上記の通り記述
class CafeTagRelation < ApplicationRecord
belongs_to :tweet
belongs_to :tag
end
上記の通り記述
3.コントローラー
createアクションの中身を少し変更
def create
@tweet = current_user.tweets.new(tweet_params)
tag_list = params[:tweet][:name].split(/[[:blank:]]+/).select(&:present?)
@tweet.user_id = current_user.id
if @tweet.save
@tweet.save_tag(tag_list)
redirect_to :action => "index"
else
redirect_to :action => "new"
end
end
一応、半角スペースをあけることで、複数タグつけれるように書いています。
4.モデルに追加
def save_tag(tag_list)
current_tags = self.tags.pluck(:name) unless self.tags.nil?
old_tags = current_tags - tag_list
new_tags = tag_list - current_tags
# Destroy old taggings:
old_tags.each do |old_name|
self.tags.delete Tag.find_by(name: old_name)
end
# Create new taggings:
new_tags.each do |new_name|
tweet_tag = Tag.find_or_create_by(name: new_name)
self.tags << tweet_tag
end
end
5.Viewに追加
<% @tweet.tags.each do |tag| %>
<span><%= tag.name %></span>
<% end %>
上記を追加する。
<div class="field">
<%= c.label :タグ %>
<%= c.text_field :name, :size => 140 %>
</div>
自分のアプリに応じて、書き方は変えてください。
ここまでで、一旦タグ投稿して、詳細ページにて表示できたと思います。
6.つけたタグから、タグ付いた投稿一覧ページに飛べるようにする
resources :tags do
get 'tweets', to: 'tweets#search'
end
def search
@tag = Tag.find(params[:tag_id])
@tweets = @tag.tweets.all
end
views/tweetsの中にsearch.html.erbを作り、
<div class= tweetsearch>
<%= @tag.name %>
<% @tweets.each do |t| %>
<div class="tweet">
<%= t.body %>
</div>
<% end %>
</div>
<%= link_to "Tweet一覧に戻る", tweets_path %>
以上で実装はできたかなと思います!
#もしかしたらエラー
もし、エラーでTweetのカラムにnameないやんけ!!ってエラー文が出たら、Tweetテーブルにnameのカラムを足しておいてください。タグのnameと関係はないのですが、私の場合はこれでなんか解決できました。(根本解決じゃないと思いますので参考までに)
#参考記事
railsでタグ機能を実装する