rails DM機能実装

2023/12/28に公開

DM機能の作成

今回はDM機能を作成していきます。

テーブル

ユーザーを管理するUsersテーブル

ユーザーはRoomsテーブルに属していて、相互フォロワー同士で1つずつチャットルームが作られていく。(1つのチャットルームに入るユーザーは2人(複数))

1人1人のユーザーは他のユーザーと相互フォロー関係になり、たくさんのチャットルームを持つ可能性がある。そのため、UsersテーブルとRoomsテーブルは多対多の関係!

この様な多対多の関係をテーブルにする場合に、必要なものが中間テーブル。

Entriesテーブルでチャットルームに入っているユーザー情報を管理

また、Roomsでは複数(2人)のユーザーが複数のメッセージを送るので多対多の関係。

Messagesテーブルでユーザーが送ったメッセージ情報を管理

model

テーブルを作成したところで次はmodelの作成。
足りないmodelを追加していきます。

rails g model room user:references
rails g model Entry user:references room:references
rails g model message user:references room:references message:text

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
belongs_to :user
belongs_to :room

validates :message, presence: true, length: { maximum: 140 }

下の1行は空欄不可、140字以内に設定。

コントローラの作成

rails g controller rooms
rails g controller messages

ルーティングの設定

resources :messages, only: [:create]
resources :rooms, only: [:create,:show]

コントローラの設定

class UsersController < ApplicationController

  before_action :authenticate_user!, only: [:show]

  def show
    @user = User.find(params[:id])
    @currentUserEntry=Entry.where(user_id: current_user.id)
    @userEntry=Entry.where(user_id: @user.id)
    if @user.id == current_user.id
    else
      @currentUserEntry.each do |cu|
        @userEntry.each do |u|
          if cu.room_id == u.room_id then
            @isRoom = true
            @roomId = cu.room_id
          end
        end
      end
      if @isRoom
      else
        @room = Room.new
        @entry = Entry.new
      end
    end
  end

わかりにくい部分のみ解説します。

@currentUserEntry=Entry.where(user_id: current_user.id)
@userEntry=Entry.where(user_id: @user.id)

roomがcreateされた時に現在ログインしているユーザーと、
「チャットへ」ボタンを押されたユーザーの両方をEntriesテーブルに記録する必要があるので、
whereメソッドを使いもう1人のユーザーを検索。

if @user.id == current_user.id
    else
      @currentUserEntry.each do |cu|
        @userEntry.each do |u|
          if cu.room_id == u.room_id then
            @isRoom = true
            @roomId = cu.room_id
          end
        end
      end
      if @isRoom
      else
        @room = Room.new
        @entry = Entry.new
      end
    end

はじめに、現在ログインしているユーザーではないという条件をつけ,
すでにroomが作成されている,いない場合に分岐。

作成されている場合、 @currentUserEntryと@userEntryをeachで一つずつ取り出し、
それぞれEntriesテーブル内にあるroom_idが共通しているユーザー同士に対して
@roomId = cu.room_idという変数を指定する。

これですでに作成されているroom_idを特定する。

@isRoom = trueは、falseのとき(Roomを作成するとき)の条件を分岐するための記述。

次にroomのcontrollerを設定します。

class RoomsController < ApplicationController
  before_action :authenticate_user!

  def create
    @room = Room.create(user_id: current_user.id)
    @entry1 = Entry.create(:room_id => @room.id, :user_id => current_user.id)
    @entry2 = Entry.create(params.require(:entry).permit(:user_id, :room_id).merge(:room_id => @room.id))
    redirect_to "/rooms/#{@room.id}"
  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
    #Roomで相手の名前表示するために記述
      @myUserId = current_user.id
    else
      redirect_back(fallback_location: root_path)
    end
  end
end

ここでは、Room以外にモデルのEntryもcreateさせなければいけないので、
Entriesテーブルに入る相互フォロー同士のユーザーを保存させるための記述を行う。

、@entry1は、現在ログインしているユーザーに対して
EntriesテーブルにRoom.createで作成された@roomに紐づくidと、
現在ログインしているユーザーのidを保存させる

@entry2では、フォローされている側の情報をEntriesテーブルに保存するための記述。
users/show.html.erbのfields_for @entryで保存したparamsの情報(:user_id, :room_id)を
許可し、現在ログインしているユーザーと同じく@roomにひもづくidを保存する記述をしている。

class MessagesController < ApplicationController
  before_action :authenticate_user!, :only => [:create]

  def create
    if Entry.where(:user_id => current_user.id, :room_id => params[:message][:room_id]).present?
      @message = Message.create(params.require(:message).permit(:message,:user_id, :content, :room_id).merge(:user_id => current_user.id))
      redirect_to "/rooms/#{@message.room_id}"
    else
      redirect_back(fallback_location: root_path)
    end
  end
end

ビューの作成

<% unless @user.id == current_user.id %>
  <% if (current_user.following? @user) && (@user.following? current_user)  %>
  <% if @isRoom == true %>
    <p class="user-show-room"><a href="/rooms/<%= @roomId %>" class="btn btn-primary btn-lg">チャットへ</a>
  <% else %>
    <%= form_for @room do |f| %>
      <%= fields_for @entry do |e| %>
        <%= e.hidden_field :user_id, value: @user.id %>
      <% end %>
      <%= f.submit "チャットを始める", class:"btn btn-primary btn-lg user-show-chat"%>
    <% end %>
  <% end %>
  <% end %>
<% end %>
<% @entries.each do |e| %>
  <% if @myUserId != e.user.id %>
      <h2><%= e.user.name %>さんとのトークルーム</h2>
    </div>
  <% end %>
<% end %>
<%= link_to "ユーザー一覧に戻る", users_path %>

   <% if @messages.present? %>
     <% @messages.each do |m| %>
       <% @myUserId == m.user.id  %>
       <p><%= m.user.name %></p>
         <p><%= m.message %></p>
     <% end %>
   <% end %>

<%= 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: 'form-submit'%>
 <% end %>

最後に
今までで一番理解に苦しみました。
理解しにくい部分があればコメントをいただけるとありがたいです。

Discussion