初めに
私は普段、適当にプログラミングを勉強している@takakouと申します。今回作成した「相互フォロー同士の「DM機能(非同期通信)」は自分のアプリに、相互フォローをしているユーザ同士の1対1のDM機能を実装したい方向けです。色々DM機能の作成方法について記事を漁ってみたのですが、相互フォローしているユーザ同士の1対1のDM機能に特化した記事が少ないと感じたため、今回の記事の執筆に至りました。
前提
フォロー・フォロワー機能が実装されているという前提のもと今回の記事を執筆していきます。前提としての機能が完成していない場合は下記の@Hbk__17さんの執筆された記事を参考にしていただき、フォロー・フォロワーの機能を実装していただくことを推奨いたします。
動作環境
・PC : MacBook Air(M1,2020)
・RAM : 8GB
・OS : macOS Monterey(ver12.1)
・Ruby : 3.1.2
・Rails : 6.1.6.1
・Bootstrap : 4.5
レイアウトを整えるのはメインをBootstrap、サブでstyleオプション等用います。流行りのCSSフレームワーク等は取り入れてません。レイアウトは割と適当なので完成後にアレンジすることをお勧めいたします。
静的コード解析ツールRubocopを用いてコードの修正をしてあります。
構成
DM機能でよくみる構成が下記になります。
上記のテーブル構成でも問題なくDM機能を実装することができますが、roomを作り、そこに人が入ることによりDM機能を作成しているのでLINEで言うグループ機能っぽい構成になってます。しかし、1対1のDM機能を実装するだけだったらこんなにテーブルは要らないので、今回は別の構成でDM機能の作成を行います。
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になっているのでそれぞれの指定を行います。class_nameなどの記述がわからない方は下記の記事を参考にしてみてください。
class Message < ApplicationRecord
belongs_to :send_user, class_name: 'User'
belongs_to :receive_user, class_name: 'User'
validates :chat, presence: true
end
UserやRelationshipのモデルの作成や記述は終わっている前提ですので記述はしません。
リレーションの確認
1.データ作成
@message=Message.new(send_user_id:1,receive_user_id:2,chat:"こんにちは")
@message.save
事前にユーザの登録などをしておいてください。
2.作成したデータの検索
@message=Messgae.find(1)
3.リレーションの確認
pry(main)> @message.send_user
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, email: "a@a.a", name: "koki", introduction: "eee", created_at: "2022-08-20 12:01:32.485377000 +0000", updated_at: "2022-08-20 17:40:23.711492000 +0000">
[9] pry(main)> @message.receive_user
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
=> #<User id: 2, email: "jjj@jjj", name: "aaa", introduction: "", created_at: "2022-08-20 17:40:44.220449000 +0000", updated_at: "2022-12-17 09:52:31.508726000 +0000">
リレーションが通っていることが確認できたらモデルの記述は以上になります。
messagesコントローラの作成
rails g controller messages
コントローラにおいて、相互フォローの確認の処理も行っています。
class MessagesController < ApplicationController
before_action :check, only: [:message]
def message
@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="container bg-dark">
<div id="messages">
<%= render 'messages',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:800px;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(50,50),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(50,50),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/messages',messages: @messages) %>");
$("#message_field").val('');
完成形
完成すると下記の図のようなレイアウトでDM機能ができるようになっているはずです!
終わりに
「もっとこうすると良い」等の修正依頼は随時受け付けておりますので指摘をお願いいたします。少しでも、どこかの誰かの役に立ってくれれば、とても嬉しいです。