🦦

一度やったはずのDM機能まとめ

2024/07/17に公開

DM機能実装方法

https://zenn.dev/h_hana/articles/cbf814763691ee
参考にさせていただいた記事です。

  • 相互フォロワーのみ、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

アソシエーション

app/models/user.rb
has_many :entries, dependent: :destroy
has_many :messages, dependent: :destroy
app/models/message.rb
belongs_to :user
belongs_to :room
  
validates :message, presence: true, length: { maximum: 140 }
#メッセージが空欄はダメ。140字以内
app/models/entry.rb
belongs_to :user
belongs_to :room
app/models/room.rb
has_many :entries, dependent: :destroy
has_many :messages, dependent: :destroy

ルーティング

config/routes.rb
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コントローラーが必要になることもある。

  • 今回作らない理由は...

    エントリーの直接操作が必要ない(例えば、エントリーの作成や削除を個別に行わない。)

  • 必要な時は...

    ユーザーがエントリーを手動で、作成・更新・削除する必要がある場合など

app/controllers/users_controller.rb
  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を作成する

ビュー

app/views/users/show.html.erb
<% 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に移動できる
app/views/users/show.html.erb
  <% 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_fieldfields_forブロック内で使用されるヘルパーメソッドフォームに隠しフィールドを追加する
    隠しフィールドに名前を指定する。(今回はuser_id)

  • value: @user.idのオプションは隠しフィールドの値を指定する。
    今回は、@user.idの値がフィールド値として設定。

  • 隠しフィールドは、ユーザーに表示されないフィールドで、フォーム送信時にサーバーにデータを渡すために使用されます。この例では、@user.idをサーバーに送信するために使用される。

  • e.hidden_field :user_id, value: @user.idは、entry[user_id]という名前の隠しフィールドを生成し、その値を@user.idに設定するってこと。

roomsコントローラー編集

app/controllers/rooms_controller.rb
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のビュー

app/views/rooms/show.html.erb
<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コントローラー編集

app/controllers/messages_controller.rb
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