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

前提
・フォロー・フォロワー機能が実装されている。
・レイアウトは Bootstrap を用いる。
モデルの作成
今回は users テーブルと、 messages テーブルを用いて実装をしていきます。
まずは Message モデルを作成します。
rails g model Message send_user_id:integer receive_user_id:integer chat:string
migrationを行います。
rails db:migrate
モデルの記述
リレーションを設定します。
今回は send_user_id と receive_user_id の親 model が両方とも User になっているのでそれぞれの指定を行います。また、今回はメッセージを140字まで送信可能とするためlength: { maximum: 140 }と記述します。
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 コントローラーの作成、記述
rails g controller messages
コントローラーにおいて、相互フォローの確認の処理も行っています。
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 に下記の文を追加します。
get 'messages/:id' => 'messages#message', as: 'message'
post 'messages' => 'messages#create', as: 'messages'
view ファイルの作成
views/messages フォルダ配下に 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>
今回は非同期処理でチャット欄を更新するため、一部テンプレートファイルも作成しておきます。
<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 ファイルも作成します。
$("#messages").html("<%= j(render 'messages/message',messages: @messages) %>");
$("#message_field").val('');
完成
ここまでくればDM機能ができるようになっているはずです!
あとはお好きな view ファイルで link_to を用いてDM画面に遷移するようにします。
今回自分はユーザープロフィールから表示させています。
<% 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