0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Rails】DM機能を実装する

Posted at

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 %>
0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?