はじめに
- ユーザーが相互フォローをしている際に、お互いのみが参照できるDM機能を実装する
- 学習内容の備忘録です
前提条件
- ユーザー機能が実装できている
- フォロー、フォロワー機能が実装できている
- Bootstrap4を導入している
手順
- テーブル設計
- アソシエーション
- ルーティングとコントローラー
- ビュー
1.テーブル設計
- まず、元々あるUsersテーブルとDMをするための部屋RoomsテーブルはN:Nの関係
- 次に、中間テーブルであるEntriesテーブルを作成→Users:Entries=1:N
- さらに、中間テーブルMessagesテーブルを作成→Users:Messages=1:N
2.アソシエーション
まず、新しいモデルを作成。
rails g model Room user:references
rails g model Entry user:references room:references
rails g model Message user:references room:references
rails db:migrate
次に、それぞれのモデルへ記述。
has_many :messages, dependent: :destroy
has_many :entries, dependent: :destroy
has_many :messages, dependent: :destroy
has_many :entries, dependent: :destroy
belongs_to :user
belongs_to :room
validates :message, length: { maximum: 140 }
belongs_to :user
belongs_to :room
補足
「validates :message, length: { maximum: 140 }」によって、message属性がnullでないことを検証し、さらに文字列の長さが140文字以下であることを検証。つまり、空欄はダメ、141文字以上もダメ。
3.ルーティングの設定とコントローラーの記述
まず、ルーティングにこちらの記述を追加。
get 'message/:id' => 'messages#show', as: 'message'
resources :messages, only: [:create]
続いて、コントローラーを作成。
rails g controller messages
さらに、messagesコントローラーへ記述。
showアクションとcreateアクションを記述。
# showアクションとcreateアクションを実行する前に、ユーザーの認証(ログイン)を確認
before_action :authenticate_user!, only: [:show, :create]
# showアクションは、特定のユーザーの詳細ページを表示するため
def show
@user = User.find(params[:id])
# 現在のユーザーが参加しているすべてのルームのIDを取得
rooms = current_user.entries.pluck(:room_id)
# 特定のユーザーと現在のユーザーの間で共有されているルームのエントリーを見つける
entries = Entry.find_by(user_id: @user.id, room_id: rooms)
# エントリーが存在する場合、関連するルームを@roomに代入
unless entries.nil?
@room = entries.room
# エントリーが存在しない場合、新しいルームを作成し、保存し、お互いのエントリーを作成
else
@room = Room.new(user_id: current_user.id)
@room.save
Entry.create(user_id: current_user.id, room_id: @room.id)
Entry.create(user_id: @user.id, room_id: @room.id)
end
# @roomに関連するメッセージを@messagesに、新しいメッセージを作成したものは@messageに代入
@messages = @room.messages
@message = Message.new(room_id: @room.id)
end
# createアクションは、新しいメッセージを作成するため
def create
# 現在のユーザーに関連付けられた新しいメッセージを作成
@message = current_user.messages.new(message_params)
# メッセージを保存し、元のURLへリダイレクト
@message.save
redirect_to request.referer
end
private
# フィルタリングも忘れずに
def message_params
params.require(:message).permit(:message, :room_id)
end
4.ビュー
まずは、messageを始めるためのリンクを、ユーザーの詳細ページへ。
ここでは、部分テンプレートに記述。
<div class='row mt-2'>
<% unless user.id == current_user.id %>
<% if current_user.is_followed_by?(user) && user.is_followed_by?(current_user) %>
<% if current_user != @user %>
<%= link_to 'messageを始める', message_path(@user.id)%>
<% end %>
<% end %>
<% end %>
</div>
補足
モデルに「is_followed_by?」メソッドを定義しているため、「<% if current_user.is_followed_by?(user) && user.is_followed_by?(current_user) %>」で相互フォロー状態であることを確認できる。つまり、相互フォロー状態で、自分のユーザー詳細出なければ、「messageを始める」ボタンが出現!
補足2
「is_followed_by?」メソッドについては以下の通り
ちなみに、当時はこのように書いてしまったが、「reverse_of_relationships(反対からの)」よりも、「passive_relationships(受け身の)」の方がリーダブルだと思われる。
def is_followed_by?(user)
reverse_of_relationships.find_by(follower_id: user.id).present?
end
あとは、DMを送り合う部屋への記述!
<h4 class="rooms-title text-center mb-3">チャットルーム</h4>
<div class="row justify-content-center mb-3">
<div class="left-button">
<%= link_to "ユーザー一覧に戻る", users_path, class:"edit-link" %>
</div>
</div>
<div class="row justify-content-center mb-3">
<div class="posts ">
<%= form_with model: @message do |f| %>
<%= f.text_field :message, placeholder: "メッセージを入力して下さい" , size: 70, class:"form-text-field" %>
<%= f.hidden_field :room_id %>
<%= f.submit "投稿",class: 'form-submit' %>
<% end %>
</div>
</div>
<div class="row justify-content-center text-center">
<div class="chat">
<% if @messages.present? %>
<% @messages.each do |m| %>
<div class="chat-box">
<div class="chat-face">
<%= link_to user_path(m.user.id) do %>
<%= image_tag m.user.get_profile_image, size: "50x50", class: "user-image" %>
<% end %>
<%= m.user.name %>
<strong><%= m.message %></strong>
<%= m.created_at.strftime("%Y-%m-%d %H:%M")%>
</div>
</div>
<% end %>
<% end %>
</div>
</div>
あまり、見た目が美しくないので、今後、改善を重ねていきます。
参考サイト
RailsでややこしいDM機能を1万字でくわしく解説してみた
とても、わかりやすく、理解が深まりました。ありがとうございます。
まとめ
- DMを送るためには、EntriesとMessagesの2つの中間テーブルにそれぞれ役割がある
フラッシュメッセージや、レイアウトなどは未完ですが、とりあえずDMは送り合うことができるようになりました。
⚠️学習4ヶ月目の初学者による投稿です。
⚠️間違いがあるかもしれません。ご容赦ください。
⚠️ご指導、ご教授いただけると幸いです。