自分用にまとめます。
必要なテーブル
users
ユーザーテーブル
rooms
チャットルームをするための場所
entries
チャットルームに入るユーザーを管理するテーブル
messages
メッセージを管理するテーブル
実装
####モデル作成
$ rails g model room name:string
$ rails g model entry user:references room:references
$ rails g model message user:references room:references content:text
$ rails db:migrate
user.rb
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :messages, dependent: :destroy
has_many :entries, dependent: :destroy
end
entry.rb
class Entry < ApplicationRecord
belongs_to :user
belongs_to :room
end
room.rb
class Room < ApplicationRecord
has_many :messages, dependent: :destroy
has_many :entries, dependent: :destroy
end
message.rb
class Message < ApplicationRecord
belongs_to :user
belongs_to :room
end
####コントローラ作成
$ rails g controller users index show
$ rails g controller rooms
$ rails g controller messages
routes.rb
resources :users, only: [:show,:edit,:update]
resources :messages, only: [:create]
resources :rooms, only: [:create,:show]
users_controller.rb
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
# current_userをEntriesテーブルから探す
@currentUserEntry = Entry.where(user_id: current_user.id)
# DMを送る対象のユーザーをEntriesテーブルから探す
@userEntry = Entry.where(user_id: @user.id)
if @user.id != current_user.id
# currentUserと@userのEntriesをそれぞれ一つずつ取り出し、2人のroomが既に存在するかを確認
@currentUserEntry.each do |cu|
@userEntry.each do |u|
# 2人のroomが既に存在していた場合
if cu.room_id == u.room_id
@isRoom = true
#room_idを取り出す
@roomId = cu.room_id
end
end
end
# 2人ののroomが存在しない場合
unless @isRoom
#roomとentryを新規に作成する
@room = Room.new
@entry = Entry.new
end
end
end
end
users/show.html.slim
- if @user.id != current_user.id
# 相互フォローしていることが条件
- if (current_user.following? @user) && (@user.following? current_user)
# 既にroomがある場合(以前にDMのやりとりしている)
- if @isRoom == true
# そのroomに入る
=link_to room_path(@roomId) do
i.fas.fa-envelope
# roomがない場合(新規のDM)
- else
# RoomとEntry2つのモデルにデータを送る
= form_with @room, local: true do |f|
= fields_for @entry do |e|
# Entryにはuser_idを送る必要があるため、@user.idを指定
= e.hidden_field :user_id, value: @user.id
# 今回はリンクにアイコン使用のためbutton_tagを使用。通常であればf.submitでOK
= button_tag type: 'submit' do
i.fas.fa-envelope
rooms_controller
class RoomsController < ApplicationController
def create
@room = Room.create
# current_userのEntry
@entry1 = Entry.create(room_id: @room.id, user_id: current_user.id)
# DMを受け取る側のEntry(users/showでuser_idは渡しているため、room_idを拾って、マージしている)
@entry2 = Entry.create((entry_params).merge(room_id: @room.id))
redirect_to room_path(@room)
end
def show
@room = Room.find(params[:id])
# entriesテーブルにcurrent_user.idと紐付いたチャットルームがあるかどうか確認
if Entry.where(user_id: current_user.id,room_id: @room.id).present?
@messages = @room.messages
@message = Message.new
# チャットルームのユーザ情報を表示させるため代入
@entries = @room.entries
else
redirect_back(fallback_location: root_path)
end
end
private
def entry_params
params.require(:entry).permit(:user_id, :room_id)
end
end
rooms/show.html.slim
.row
.col-lg-2
.col-lg-8.frame-notification
<hr>
# メッセージがあれば、一覧を表示
- if @messages.present?
- @messages.each do |message|
.row.balloon5
# 今回は、自分のメッセージは右側に表示させたいため以下の条件分岐
- if current_user.name == message.user.name
.col-lg-12.text-right
.chatting
.say
= message.content
<br>
small= message.created_at.to_s(:datetime_jp)
- else
.col-lg-2.text-center
= attachment_image_tag message.user, :image, format: 'jpeg', fallback: "noimage.png", size: "50x40"
small= link_to message.user.name, user_path(message.user), class: "d-block mt-1"
<br>
.col-lg-10.text-left
.chatting
.says
p= message.content
<br>
small= message.created_at.to_s(:datetime_jp)
<hr>
- else
p メッセージはありません。
.row
.col-lg-2
.col-lg-8.text-center
= form_with model: @message, local: true do |f|
= f.text_field :content, placeholder: "メッセージを入力して下さい" , size: 50
= f.hidden_field :room_id, value: @room.id
= f.submit "投稿", class: 'btn btn-outline-secondary btn-sm ml-2'
messages_controller.rb
class MessagesController < ApplicationController
def create
#formで送られてきたroom_idとcurrent_user.idを持ったデータがEntryにあるかを確認
if Entry.where(user_id: current_user.id, room_id: params[:message][:room_id]).present?
# contentとroom_idはformで送られてきているため、user_idをマージして新規メッセージを作成
@message = Message.create((message_params).merge(user_id: current_user.id))
redirect_back(fallback_location: root_path)
else
flash[:alert] = "メッセージ送信に失敗しました。"
redirect_back(fallback_location: root_path)
end
end
private
def message_params
params.require(:message).permit(:user_id, :content, :room_id)
end
end
完成!