一度やったはずのDM機能まとめ
DM機能実装方法
参考にさせていただいた記事です。
-
相互フォロワーのみ、DM機能が使える(DMボタンが表示される)
* プロフィールの下にメールマークのやつ。
テーブル
モデル作成
rails g model Entry user:references room:references
rails g model Room user:references
rails g model Message user:references room:references message:text
「 references型 」
新しく作成するテーブルのカラムに、作成済みのテーブルを指定する時に使う。
(既存のテーブルを参照する。reference=参照って意味)
rails g model
で、モデルとマイグレーションファイルを生成するときのカラムの型を指定する時に使う。
例えば、
rails g model post name:string body:string user:references
-
references型を指定したカラム名は、テーブル名_id。
ってことは、上記だと、
テーブル名:userなので、user_id
が生成されるってこと。必ず、
rails db:migrate
アソシエーション
has_many :entries, dependent: :destroy
has_many :messages, dependent: :destroy
belongs_to :user
belongs_to :room
validates :message, presence: true, length: { maximum: 140 }
#メッセージが空欄はダメ。140字以内
belongs_to :user
belongs_to :room
has_many :entries, dependent: :destroy
has_many :messages, dependent: :destroy
ルーティング
resources :users, only: [:index, :show,・・・]do
:
:
end
resources :messages, only: [:create]
resources :rooms, only: [:create, :show]
userより外に記入したほうが楽なのかな?
最初、user内で、やってみたけど、
リソースが独立していて、親子関係を明示する必要がない場合はネストしなくて良いので、今回は独立させた。
コントローラー作成
rails g controller rooms
rails g controller messages
※ 今回の場合、entryコントローラーを作らなくて良いが、
基本的には、エントリーデータを管理するために、entryコントローラーが必要になることもある。
-
今回作らない理由は...
エントリーの直接操作が必要ない(例えば、エントリーの作成や削除を個別に行わない。)
-
必要な時は...
ユーザーがエントリーを手動で、作成・更新・削除する必要がある場合など
def show
@user = User.find(params[:id])
@current_entry = Entry.where(user_id: current_user.id)
@another_entry = Entry.where(user_id: @user.id)
unless @user.id == current_user.id
@current_entry.each do |current|
@another_entry.each do |another|
if current.room_id == another.room_id then
@is_room = true
@room_id = current.room_id
end
end
end
if @is_room
else
@room = Room.new
@entry = Entry.new
end
end
@post = Post.new
@posts = @user.posts
end
解説
@current_entry = Entry.where(user_id: current_user.id)
@another_entry = Entry.where(user_id: @user.id)
-
現在のユーザーが参加している全てのエントリーデータを取得している。
-
ユーザーが参加しているチャットルームを識別するために使われる。
-
ログインしてるユーザーをentriesテーブルに記録するため、
whereメソッド
で、current_user.id
を探す。 -
"DMを送る"ボタンを押された側のユーザーを、entriesテーブルに記録する為に
whereメソッド
でuser.id
を探す
unless @user.id == current_user.id
@current_entry.each do |current|
@another_entry.each do |another|
if current.room_id == another.room_id then
@is_room = true
@room_id = current.room_id
end
end
end
if @is_room
else
@room = Room.new
@entry = Entry.new
end
end
@post = Post.new
@posts = @user.posts
end
-
unless
を使用することでroomsが作成されている場合とされていない場合に条件分岐させる -
作成済みだったら、
@current_entry
と@another_entry
を
eachで一つずつ取り出して、それぞれentriesテーブル内にあるroom_id
が共通しているユーザー同士に対して、
@room_id = current.room_id
の変数を指定する
- ここで、既に作成されている
room_id
を特定することができる -
@is_room = true
はこれがfalseである時、else以降で、新たにroomを作成する
ビュー
<% unless @user.id == current_user.id %>
<% if (current_user.following? @user) && (@user.following? current_user) %>
<% if @is_room == true %>
<p class="user-show-room"><a href="/rooms/<%= @room_id %>" class="btn btn-light btn-sm" ><i class="fa-regular fa-envelope"></i> DMを送る</a></p></p>
<% else %>
<%= form_with model: @room, local: true do |f| %>
<%= f.fields_for @entry do |e| %>
<%= e.hidden_field :user_id, value: @user.id %>
<% end %>
<%= f.submit "DMをはじめる", class:"btn btn-light btn-sm" %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<% unless @user.id == current_user.id %>
<% if (current_user.following? @user) && (@user.following? current_user) %>
<% if @is_room == true %>
<p class="user-show-room"><a href="/rooms/<%= @room_id %>" class="btn btn-light btn-sm" ><i class="fa-regular fa-envelope"></i> DMを送る</a></p></p>
- 現在ログインしているユーザーではないかつ相互フォロー状態の条件分岐
-
@is_room
を使用してroomsが作成されているか条件分岐 -
@is_room
がtrueの時、DMを送るボタンが表示され、roomに移動できる
<% else %>
<%= form_with model: @room, local: true do |f| %>
<%= f.fields_for @entry do |e| %>
<%= e.hidden_field :user_id, value: @user.id %>
<% end %>
<%= f.submit "DMをはじめる", class:"btn btn-light btn-sm" %>
-
@is_room
がfalseだっだときこっち。
`form_withを使用して、コントローラーにパラメーターを送る -
model: @room
でモデルを指定し、local: true
を指定することで、Ajaxではなく通常のHTMLフォームを送信するように設定 -
レコードには親モデルのroomsテーブルと子モデルのentriesテーブルの両方を保存する必要があるため、親には、
form_with @room
、子にはfields_for @entry
-
hidden_field
はfields_for
ブロック内で使用されるヘルパーメソッドフォームに隠しフィールドを追加する
隠しフィールドに名前を指定する。(今回はuser_id) -
value: @user.id
のオプションは隠しフィールドの値を指定する。
今回は、@user.idの値がフィールド値として設定。 -
隠しフィールドは、ユーザーに表示されないフィールドで、フォーム送信時にサーバーにデータを渡すために使用されます。この例では、@user.idをサーバーに送信するために使用される。
-
e.hidden_field :user_id, value: @user.idは、entry[user_id]という名前の隠しフィールドを生成し、その値を@user.idに設定するってこと。
roomsコントローラー編集
class RoomsController < ApplicationController
before_action :authenticate_user!
def create
@room = Room.create(user_id: current_user.id)
@current_entry = Entry.create(user_id: current_user.id, room_id: @room.id)
@another_entry = Entry.create(params.require(:entry).permit(:user_id, :room_id).merge(:room_id => @room.id))
redirect_to room_path(@room)
end
def show
@room = Room.find(params[:id])
if Entry.where(user_id: current_user.id, room_id: @room.id).present?
@messages = @room.messages
@message = Message.new
@entries = @room.entries
@my_account = current_user.id
else
redirect_back(fallback_location: root_path)
end
end
end
-
room以外に子モデルのentryもcreateさせる必要があるので、entryテーブルに入る相互フォロー同士のユーザーを保存させるための記述をする
-
ログイン中のユーザーに対しては`@current_entry
entryテーブルにroom.createで作成された@roomに紐づくidと、
ログイン中のユーザーのidを保存させる記述 -
フォローされている側のユーザーは`@another_entry
ログイン中のユーザーと同じく@roomに紐づくidを保存する記述をする -
redirect_to room_path(@room)
でcreateと同時にチャットルームが開くように、redirectで記述
def show
@room = Room.find(params[:id])
if Entry.where(user_id: current_user.id, room_id: @room.id).present?
@messages = @room.messages
@message = Message.new
@entries = @room.entries
@my_account = current_user.id
else
redirect_back(fallback_location: root_path)
end
-
roomsのshowアクションでは、一つのチャットルームを表示させる必要がありため、
findメソッド
を使う -
entriesテーブルにログイン中のユーザーのidと紐づいたチャットルームのidを
whereメソッド
で探しレコードがあるか確認する -
条件がfalseだった場合、redirect_backで前のページへ戻る
-
条件がtrueだった場合messagesテーブルにチャットルームのidと紐付いたメッセージを表示させるために、アソシエーションを利用した
@room.messages
の記述。
roomsのビュー
<div class="container">
<div class="row">
<% @entries.each do |e| %>
<% if @my_account != e.user.id %>
<h4><strong><%= e.user.name %>さんとのメッセージ</strong></h4>
<% end %>
<% end %>
</div>
<div class="row my-3">
<%= link_to "投稿一覧ページに戻る", posts_path, class:"btn btn-secondary btn-sm" %>
</div>
<div class="row">
<table class="table table-hover">
<% if @messages.present? %>
<% @messages.each do |m| %>
<tr>
<td><%= m.user.name %></td>
<td><%= m.message %></td>
</tr>
<% end %>
<% end %>
</table>
</div>
<div class="row">
<%= form_for @message do |f| %>
<%= f.text_field :message, placeholder: "メッセージを入力して下さい" , size: 50, class:"form-text-field" %>
<%= f.hidden_field :room_id, value: @room.id %>
<%= f.submit "送信",class:"btn btn-success"%>
<% end %>
</div>
</div>
<div class="row">
<% @entries.each do |e| %>
<% if @my_account != e.user.id %>
<h4><strong><%= e.user.name %>さんとのメッセージ</strong></h4>
<% end %>
<% end %>
</div>
-
`@entriesの相互フォロワー同士の情報が欲しいため、eachでフォローとフォロワーの情報を取得している
-
<% if @my_account != e.user.id %>
roomsコントローラーで@my_accountにログイン中のユーザーidを代入しているため、
「!=」にすることで、DM送信相手の名前を表示させる
<% if @messages.present? %>
- DMが存在するのか確認し、メッセージのやりとりがあればeachでメッセージを表示させる
<div class="row">
<%= form_for @message do |f| %>
<%= f.text_field :message, placeholder: "メッセージを入力して下さい" , size: 50, class:"form-text-field" %>
<%= f.hidden_field :room_id, value: @room.id %>
<%= f.submit "送信",class:"btn btn-success"%>
<% end %>
</div>
</div>
- DMを送信するためのフォームを、form_withで作成
<%= form_with model: @message, local: true do |f| %>では、@messageの他にこのmessageがどのroomに所属しているかを判断するためにhidden_field
にroomの情報を持たせる
messagesコントローラー編集
before_action :authenticate_user!
def create
message = Message.new(message_params)
message.user_id = current_user.id
if message.save
redirect_to room_path(message.room)
else
redirect_back(fallback_location: root_path)
end
end
private
def message_params
params.require(:message).permit(:room_id, :message)
end
end
完成。
Discussion