完成イメージ
1. Reactionモデルの作成
(すでに作成済みの場合はスキップ)
bash
rails generate model Reaction user:references tweet:references kind:string
rails db:migrate
2. モデルの関連付け・バリデーション
app/models/reaction.rb
class Reaction < ApplicationRecord
belongs_to :user
belongs_to :tweet
# 1ユーザー1ツイートにつき各種類1つずつリアクションできる
validates :user_id, uniqueness: { scope: [:tweet_id, :kind] }
validates :kind, presence: true
end
3. DBインデックスの修正
bash
rails generate migration ChangeReactionIndex
db/migrate/xxxxxx_change_reaction_index.rb
class ChangeReactionIndex < ActiveRecord::Migration[8.0]
def change
remove_index :reactions, [:user_id, :tweet_id]
add_index :reactions, [:user_id, :tweet_id, :kind], unique: true
end
end
マイグレーションを実行
bash
rails db:migrate
4. モデルの関連付け(User/Tweet)
app/models/user.rb
class User < ApplicationRecord
has_many :reactions, dependent: :destroy
end
app/models/tweet.rb
class Tweet < ApplicationRecord
has_many :reactions, dependent: :destroy
end
5. ルーティングの追加
config/routes.rb
Rails.application.routes.draw do
resources :tweets do
resources :reactions, only: [:create, :destroy]
end
# ...他のルーティング...
end
6. コントローラーの作成・編集
app/controllers/reactions_controller.rb
class ReactionsController < ApplicationController
before_action :authenticate_user!
before_action :set_tweet
def create
reaction = current_user.reactions.find_or_initialize_by(tweet: @tweet, kind: params[:kind])
if reaction.persisted?
# すでに同じリアクションがあれば削除(トグル動作)
reaction.destroy
notice = 'リアクションを解除しました'
else
reaction.save
notice = 'リアクションしました'
end
redirect_to tweets_path, notice: notice
end
def destroy
reaction = current_user.reactions.find_by(tweet: @tweet, kind: params[:id])
reaction&.destroy
redirect_to tweets_path, notice: 'リアクションを解除しました'
end
private
def set_tweet
@tweet = Tweet.find(params[:tweet_id])
end
end
7. ヘルパーの作成
app/helpers/reactions_helper.rb
module ReactionsHelper
def reaction_kinds
{
"like" => "👍",
"love" => "❤️",
"laugh" => "😂"
}
end
end
8. ビューの編集
app/views/tweets/index.html.erb
<div class="reactions mt-2">
<% reaction_kinds.each do |kind, emoji| %>
<% user_reaction = tweet.reactions.find_by(user: current_user, kind: kind) if current_user %>
<% count = tweet.reactions.where(kind: kind).count %>
<%= form_with url: tweet_reactions_path(tweet), method: :post, local: true, class: 'd-inline' do |f| %>
<%= hidden_field_tag :kind, kind %>
<% if current_user && user_reaction %>
<%= f.submit "#{emoji} #{count}(解除)", class: "btn btn-warning btn-sm" %>
<% else %>
<%= f.submit "#{emoji} #{count}", class: "btn btn-outline-secondary btn-sm" %>
<% end %>
<% end %>
<% end %>
</div>
9. ヘルパーをコントローラーで使えるようにする
app/controllers/tweets_controller.rb
class TweetsController < ApplicationController
helper ReactionsHelper
# ...既存のコード...
end
10. サーバーを再起動
bash
rails s
11. 動作確認
- ログインした状態でツイート一覧ページにアクセス
- 各ツイート下に「👍」「❤️」「😂」のボタンが表示される
- 各ボタンを押すと、その種類ごとにリアクションできる(同時に複数種類OK)
- もう一度同じボタンを押すと解除できる