1. 概要
- DeviseのUser同士でメッセージ(DM)を送受信
- 画面は 受信箱/送信済み/新規作成 の3つ
追加・編集するファイル
追加(新規)
app/models/direct_message.rb
db/migrate/*_create_direct_messages.rb
- app/controllers/direct_messages_controller.rb`
app/views/direct_messages/index.html.erb
app/views/direct_messages/new.html.erb
編集(追記)
app/models/user.rb
config/routes.rb
2. 生成コマンド
コマンドプロンプト
rails g controller direct_messages index new
rails g model DirectMessage sender:references recipient:references body:text
マイグレーションファイルを編集
db/migrate/*_create_direct_messages.rb
class CreateDirectMessages < ActiveRecord::Migration[7.0]
def change
create_table :direct_messages do |t|
t.references :sender, null: false, foreign_key: { to_table: :users } # ← 修正
t.references :recipient, null: false, foreign_key: { to_table: :users } # ← 修正
t.text :body, null: false
t.timestamps
end
add_index :direct_messages, [:sender_id, :created_at]
add_index :direct_messages, [:recipient_id, :created_at]
end
end
コマンドプロンプト
rails db:migrate
3. models
app/models/direct_message.rb
class DirectMessage < ApplicationRecord
belongs_to :sender, class_name: "User"
belongs_to :recipient, class_name: "User"
validates :body, presence: true, length: { maximum: 10_000 }
validate :not_self
scope :inbox_for, ->(user_id) { where(recipient_id: user_id).order(created_at: :desc) }
scope :outbox_for, ->(user_id) { where(sender_id: user_id).order(created_at: :desc) }
private
def not_self
errors.add(:base, "自分には送信できません") if sender_id.present? && sender_id == recipient_id
end
end
app/models/user.rb
class User < ApplicationRecord
# Devise モジュールは既存
# ======== ここから ========
has_many :sent_messages, class_name: "DirectMessage",
foreign_key: :sender_id,
dependent: :destroy
has_many :received_messages, class_name: "DirectMessage",
foreign_key: :recipient_id,
dependent: :destroy
def display_name
respond_to?(:name) && name.present? ? name : email
end
# ======== ここまで ========
end
4. routes
config/routes.rb
Rails.application.routes.draw do
devise_for :users
resources :posts
resources :direct_messages, only: [:index, :new, :create] # 追加
root "posts#index"
end
5. Controllers
app/controllers/direct_messages_controller.rb
class DirectMessagesController < ApplicationController
before_action :authenticate_user!
def index
@inbox = DirectMessage.inbox_for(current_user.id)
@outbox = DirectMessage.outbox_for(current_user.id)
end
def new
@message = DirectMessage.new(recipient_id: params[:recipient_id])
@users = User.where.not(id: current_user.id).order(created_at: :desc).limit(200)
end
def create
@message = DirectMessage.new(message_params.merge(sender_id: current_user.id))
if @message.save
redirect_to direct_messages_path, notice: "送信しました"
else
@users = User.where.not(id: current_user.id).order(created_at: :desc).limit(200)
render :new, status: :unprocessable_entity
end
end
private
def message_params
params.require(:direct_message).permit(:recipient_id, :body)
end
end
6. Views
DM一覧(受信箱/送信済み)
app/views/direct_messages/index.html.erb
<h1>DM</h1>
<h2>受信箱</h2>
<% if @inbox.blank? %>
<p>受信メッセージはありません。</p>
<% else %>
<ul>
<% @inbox.each do |m| %>
<li>
<strong><%= m.sender.display_name %> ➜ あなた</strong><br>
<%= simple_format(h(m.body)) %>
<small><%= m.created_at.strftime("%Y-%m-%d %H:%M") %></small>
</li>
<% end %>
</ul>
<% end %>
<h2>送信済み</h2>
<% if @outbox.blank? %>
<p>送信メッセージはありません。</p>
<% else %>
<ul>
<% @outbox.each do |m| %>
<li>
<strong>あなた ➜ <%= m.recipient.display_name %></strong><br>
<%= simple_format(h(m.body)) %>
<small><%= m.created_at.strftime("%Y-%m-%d %H:%M") %></small>
</li>
<% end %>
</ul>
<% end %>
<p><%= link_to "新規メッセージ", new_direct_message_path %></p>
DM一覧に行くためのリンク
app/views/layouts/application.html.erb
<li><%= link_to "DM", direct_messages_path %></li>
メッセージ新規作成
app/views/direct_messages/new.html.erb
<h1>新規メッセージ</h1>
<%= form_with model: @message, url: direct_messages_path, local: true do |f| %>
<% if @message.recipient_id.present? %>
<% recipient = User.find(@message.recipient_id) %>
<p>宛先:<strong><%= recipient.display_name %></strong></p>
<%= f.hidden_field :recipient_id %> <!-- ★ 追加:選択済みを隠しで送る -->
<% else %>
<div class="field">
<%= f.label :recipient_id, "宛先ユーザー" %><br>
<%= f.collection_select :recipient_id, @users, :id, :display_name, include_blank: "選択してください" %>
</div>
<% end %>
<div class="field">
<%= f.label :body, "本文" %><br>
<%= f.text_area :body, cols: 50, rows: 5 %>
</div>
<%= f.submit "送信" %>
<% end %>
ユーザー詳細から「このユーザーにDM」
app/views/users/show.html.erb
<% if user_signed_in? && current_user.id != @user.id %>
<p><%= link_to "このユーザーにDM", new_direct_message_path(recipient_id: @user.id) %></p>
<% end %>
投稿詳細から「作者にDM」
app/views/posts/show.html.erb
<% if user_signed_in? && @post.user && current_user.id != @post.user_id %>
<p><%= link_to "作者にDM", new_direct_message_path(recipient_id: @post.user_id) %></p>
<% end %>