LoginSignup
0
0

More than 1 year has passed since last update.

RailsでDM機能を実装してみました

Posted at

RailsでDM機能を実装

ポートフォリオでRailsでアプリケーションを作成し、1対1でDMを送ることができる機能を実装しました。

実装後の挙動

Image from Gyazo

使用技術

Ruby 2.6.5
Ruby on Rails 6.0.5

テーブル設計

まずはテーブル設計です。

9b3e78d84a15a03decc6f6cc9a06e02c.png

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

roomsテーブル メッセージをする部屋

entriesテーブル usersテーブルとroomsテーブルの中間テーブル。userとroomの情報が一つづつ入ります。

messagesテーブル これもusersテーブルとroomsテーブルの中間テーブル。メッセージの内容とuserとroomの情報が入ります。

アソシエーションはこのようになります。

user.rb
  has_many :messages, dependent: :destroy
  has_many :entries, dependent: :destroy
rooms.rb
  has_many :messages, dependent: :destroy
  has_many :entries, dependent: :destroy
entry.rb
  belongs_to :user
  belongs_to :room
message.rb
  belongs_to :user
  belongs_to :room

ルーティング

routes.rb
resources :users, only: [:edit, :update, :show]
resources :messages, only: [:create]
resources :rooms, only: [:create, :show, :index]

userコントローラー

users_controller.rb
class UsersController < ApplicationController
 

  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
      # ユーザーのidとログインしているidが同じでなければ実行
    else
      @currentUserEntry.each do |cu|
        @userEntry.each do |u|
          if cu.room_id == u.room_id
            # 既にroomが作成されている場合
            @isRoom = true
            @roomId = cu.room_id
            # 既に作成されているroom_idを特定
          end
        end
      end
      if @isRoom
      else
        # roomを新しく作成する記述
        @room = Room.new
        @entry = Entry.new
      end
    end
  end



end

ここから理解が大変でした。まずはこの部分から解説します。

    @user = User.find(params[:id])
    @currentUserEntry = Entry.where(user_id: current_user&.id)
    # ログインしているユーザーの情報取得
    @userEntry = Entry.where(user_id: @user.id)
    # メッセージ相手のユーザーの情報を取得

@userの所はuserの情報をfindでテーブルから取得しています。これによってuserの詳細ページでユーザーの名前を表示することができます。

@currentUserEntry@userEntryはログインしているユーザーの情報とメッセージ相手のユーザーの情報を取得しています。

モデル名.where("条件")

whereを使うことでテーブル内の条件に一致したレコードを配列の形で取得することができます。

    if @user.id == current_user&.id
      # ユーザーのidとログインしているidが同じでなければ実行
    else
      @currentUserEntry.each do |cu|
        @userEntry.each do |u|
          if cu.room_id == u.room_id
            # 既にroomが作成されている場合
            @isRoom = true
            @roomId = cu.room_id
            # 既に作成されているroom_idを特定
          end
        end
      end
      if @isRoom
      else
        # roomを新しく作成する記述
        @room = Room.new
        @entry = Entry.new
      end
    end
  end

ここではまず条件分岐でログインしているユーザーのidと同じでなければ実行するようにしています。意味的には自分のマイページではチャットを作ることはできないようにしています。

そして既にroomが作成されている場合はそのroomのidを特定する記述をし、まだroomが作成されていない場合は新しくroomを作るよう記述しています。

users/show.html.erb

次にuserの詳細ページのviewファイルです。

users/show.html.erb
<% if @user.id == current_user&.id %>

<div class="d-grid gap-2 d-md-flex justify-content-md-end">


  <%= link_to "DM一覧", rooms_path, class: "post btn btn-outline-info" %>


 </div>

<% else %>
  <% if @isRoom == true %>
  <%# 既に部屋は存在しているかどうか %>
    <div class="btn btn-secondary"><a class="text-decoration-none text-white" href="/rooms/<%= @roomId %>">チャットへ</div>
  <% 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-secondary chat_btn"%>
    <% end %>
  <% end %>
<% end %>

まず条件分岐では自分のマイページではDM一覧ページに遷移するボタンが表示されます。他の人のページではチャットを始めるボタンを表示するようにしています。その中での条件分岐で始めてチャットをする相手には「チャットを始めるボタン』、既にチャットをしている相手にはチャットへボタンを表示しています。

roomsコントローラーとview

rooms_controller
class RoomsController < ApplicationController
  before_action :authenticate_user!

  def create
    @room = Room.create
    @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 index
    # ログインユーザー所属ルームID取得
    current_entries = current_user.entries
    my_room_id = []
    current_entries.each do |entry|
      my_room_id << entry.room.id
    end
    # 自分のroom_idでuser_idが自分じゃないのを取得
    @another_entries = Entry.where(room_id: my_room_id).where.not(user_id: current_user.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
    else
      redirect_back(fallback_location: root_path)
    end
  end
end

まずcreateアクションについて解説します。

@room = Room.create

これで新しいroomを保存しています。

 @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}"

これはentryテーブルにuser_idとroom_idを保存しています。@entry1は現在ログインしているユーザーでentry2がDMする相手のデータになります。

次にshowアクションについて解説します。
まずroomのidをfindで見つけてきます。

@room = Room.find(params[:id])

次に条件分岐でentryテーブルの中のログインしているユーザーのidとそれに紐づくroomのidを取得してpresent?で値が格納されているとtrueで値がないとfalseになります。

trueならそのroomのメッセージを表示するため@messagesのインスタンス変数の中に@room.messagesを代入しています。

また@message = Message.newで新しくメッセージを入れる箱を作っています。

@entries = @room.entriesはDMの画面で誰が参加しているかを表示するために記述しています。

rooms/index.html.erb
<div class="container">

<% @another_entries.each do |entry| %>
  <%= link_to room_path(entry.room) do %>
  <div class="card mb-4">

  <div class= "card-body">
  <p class="card-text"><%= entry.user.name%>
  さんとのメッセージ
  </p>
  </div>
  </div>

  <% end %>
<% end %>

</div>

ここでは自分がDMをした人との一覧を表示しています。

rooms/show.html.erb
<div class= "container">


<h1>DM</h1>

<h4>参加者</h4>
<% @entries.each do |e| %>
  <h5><strong><a href="/users/<%= e.user.id %>"><%= e.user.name%>さん</a></strong></h5>
<% end %>

<hr>
<% if @messages.present? %>
  <% @messages.each do |m| %>
    <%= m.user.name %>さん
    <div class="date_time text-secondary text-right">
     <%= m.created_at%>
     </div>

    <div class="message pt-3">
    <%= m.content %>
    </div>

    <hr>
  <% end %>
<% else %>
  <h3 class="text-center">メッセージはまだありません</h3>
<% end %>

<%= form_for @message do |f| %>
<div class="d-flex pb-5">
  <%= f.text_field :content, placeholder: "メッセージを入力して下さい" , rows: 15,class:"form-control"%>
  <%= f.hidden_field :room_id, value: @room.id %>
  <%= f.submit "投稿する",class:"btn btn-secondary" %>
  </div>
<% end %>

</div>

ここではDMの画面を表示しています。

messages_controller.rb
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(: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

ここではmessageを保存する処理を書いています。条件分岐でログインしているユーザーのidとroomのidとmessageのデータが入っていると実行されます。

データが入っていないと、何も起こらずにページに戻る記述になっています。

以上になります。

参考記事

RailsでややこしいDM機能を1万字でくわしく解説してみた
RailsでDM(ダイレクトメッセージ)を送れるようにしよう

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0