💬

【Rails】相互フォロー同士のDM機能(非同期通信)

に公開
2023/07/02

相互フォロー同士のDM機能の実装

前提

・フォロー・フォロワー機能が実装されている。
・レイアウトは Bootstrap を用いる。

モデルの作成

今回は users テーブルと、 messages テーブルを用いて実装をしていきます。

まずは Message モデルを作成します。

terminal
rails g model Message send_user_id:integer receive_user_id:integer chat:string

migrationを行います。

terminal
rails db:migrate

モデルの記述

リレーションを設定します。
今回は send_user_id と receive_user_id の親 model が両方とも User になっているのでそれぞれの指定を行います。また、今回はメッセージを140字まで送信可能とするためlength: { maximum: 140 }と記述します。

message.rb
class Message < ApplicationRecord
  belongs_to :send_user, class_name: 'User'
  belongs_to :receive_user, class_name: 'User'
  validates :chat, presence: true, length: { maximum: 140 }
end

messages コントローラーの作成、記述

terminal
rails g controller messages

コントローラーにおいて、相互フォローの確認の処理も行っています。

messages_controller
class MessagesController < ApplicationController
  before_action :check, only: [:message]
  def message
    @user = User.find(params[:id])
    @message = Message.new
    @messages = Message.where(send_user_id: current_user.id,
                              receive_user_id: params[:id]).or(@receive_messages = Message.where(send_user_id: params[:id],
                                                                                                 receive_user_id: current_user.id)).order(:created_at)
  end

  def create
    @message = Message.new(message_params)
    @message.send_user_id = current_user.id
    if @message.save!
      @messages = Message.where(send_user_id: current_user.id,
                                receive_user_id: params[:message][:receive_user_id]).or(@receive_messages = Message.where(
                                  send_user_id: params[:message][:receive_user_id], receive_user_id: current_user.id
                                )).order(:created_at)
    else
      @message = Message.new
      @messages = Message.where(send_user_id: current_user.id,
                                receive_user_id: params[:message][:receive_user_id]).or(@receive_messages = Message.where(
                                  send_user_id: params[:message][:receive_user_id], receive_user_id: current_user.id
                                )).order(:created_at)
      render :message
    end
  end

  private

  def message_params
    params.require(:message).permit(:receive_user_id, :chat)
  end

  def check
    # #自分自身とのDMを防ぐ
    if params[:id] == current_user.id
      redirect_back fallback_location: root_path
    else
      ## 相互にフォローしているかの確認。
      check1 = Relationship.exists?(followed_id: params[:id], follower_id: current_user.id)
      check2 = Relationship.exists?(follower_id: params[:id], followed_id: current_user.id)
      redirect_back fallback_location: root_path if !check1 || !check2
    end
  end
end

route の記述

routes.rb に下記の文を追加します。

routes.rb
  get 'messages/:id' => 'messages#message', as: 'message'
  post 'messages' => 'messages#create', as: 'messages'

view ファイルの作成

views/messages フォルダ配下に message.html.erb を作成します。

message.html.erb
<div class="d-flex justify-content-center mb-2">
    <h3><%= @user.name %></h3>
</div>  
<div class="container bg-dark">
    <div id="messages">
      <%= render 'message', messages: @messages %>
    </div>
  <div class="row">
    <%= form_with model:@message ,url:messages_path,method: :post,local:false ,class:"w-100",html: { id: 'message_form' } do |f| %>
      <div class="d-flex justify-content-center">
        <%= f.text_field :chat ,class:"w-75",placeholder:"ここにチャットを入力してください",id:"message_field" %>
        <%= f.hidden_field :receive_user_id,value: params[:id]%>
        <%= f.submit "送信" ,class:"btn btn-secondary w-25",id:"message-btn"%>
      </div>
    <% end %>
  </div>
</div>

今回は非同期処理でチャット欄を更新するため、一部テンプレートファイルも作成しておきます。

_message.html.erb
<div class="row d-block bg-dark pt-1" style="height:600px;overflow: scroll;" >
  <div>
    <% messages.each do |message|%>
      <% if message.send_user.id==current_user.id%>
        <div class="mb-1 d-flex">
          <%= image_tag message.send_user.get_profile_image, size:'50x50',class:"rounded ml-1 mr-2"%>
          <div class="rounded bg-success text-black text-break" style="max-width:50%"><%= message.chat %></div>
        </div>
      <% else %>
        <div class="d-flex flex-row-reverse mb-1">
          <div><%= image_tag message.send_user.get_profile_image, size:'50x50',class:"rounded ml-2 mr-1"%></div>
          <div class="rounded bg-secondary text-white text-break mw-100" style="max-width:50%;"><%= message.chat %></div>
        </div>
      <% end %>
    <% end %>
  </div>
</div>

非同期通信でメッセージを更新するための js ファイルも作成します。

create.js.erb
$("#messages").html("<%= j(render 'messages/message',messages: @messages) %>");
$("#message_field").val('');

完成

ここまでくればDM機能ができるようになっているはずです!
あとはお好きな view ファイルで link_to を用いてDM画面に遷移するようにします。
今回自分はユーザープロフィールから表示させています。

views/users/_info.html.erb
<% if current_user != user && current_user.following?(user) && user.following?(current_user) %>
      <%= link_to message_path(user.id),class: 'btn btn-success' do %>
        <i class="far fa-comments"></i>
      <% end %>
<% end %>

相互フォローでのDM機能なので、
<% if current_user != user && current_user.following?(user) && user.following?(current_user) %>
と記述し、相互フォロー状態以外ではボタンを表示させないようにします。

終わりに

少し長くなりましたが、以上になります。
ユーザー1対1のDM機能を実装するだけなら、users テーブルと、 messages テーブルだけで実装できるみたいですね。
メッセージのレイアウトは、各自ぜひアレンジしてみてください!

Discussion