0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

DM機能を作成する

Last updated at Posted at 2023-07-07

book投稿アプリにユーザ同士で 1 対 1 の DM ができるようにDM機能を追加します。
※ 140 字まで送信可能にする
:snowflake: 非同期通信で作成します。

:shamrock:前提

・device導入 ・Bootstrap導入
・ユーザー機能実装済(Userモデル)


:four_leaf_clover: モデル作成

4つのモデルを使って実装します。
・Userモデル => Userの情報(devise)
・Roomモデル => 自分と相手の2人のユーザーが入ります
・Entryモデル => どのUserがどのRoomに所属しているかを判断する
・Messageモデル => UserがどのRoomでどんなメッセージを送ったか

:star:userモデルは実装済なのであと3つ作成する

$ rails g model room
$ rails g model entry user:references room:references
$ rails g model message user:references room:references body:text
$ rails db:migrate

:shamrock: コントローラー作成

$ rails g controller rooms show
$ rails g controller messages

:shamrock:リレーション設定

user.rb
has_many :entries, dependent: :destroy
has_many :messages, dependent: :destroy

:snowflake:User モデルは複数の Entry、messages レコードを持ちます。これは、1対多の関係です。また、関連するレコードはユーザーが削除されたときに一緒に削除されるように設定されています (dependent: :destroy)。

room.rb
 has_many :entries, dependent: :destroy
 has_many :messages, dependent: :destroy

:snowflake:Room モデルは複数の Entry、messages レコードを持ちます。これは 1対多の関係です。また、関連する Entry レコードはルームが削除されたときに一緒に削除されるように設定されています (dependent: :destroy)。

entry.rb
belongs_to :user
belongs_to :room

:snowflake:Entry モデルは1つの User レコードに属しています (belongs_to :user)。また、1つの Room レコードに属しています (belongs_to :room)。

message.rb
 belongs_to :user
 belongs_to :room
空でない&最大140文字以下であるバリデーションも追加
 validates :message, presence: true, length: { maximum: 140 }
 def message
    body
   end

:snowflake:Message モデルも1つの User レコードに属しています (belongs_to :user)。また、1つの Room レコードに属しています (belongs_to :room)。

:snowflake:Message モデルには、メッセージの本文を取得するための message メソッドが定義されています。これにより、body カラムの値が message メソッドを通じてアクセスできます。

:shamrock: ルーティング追加

rutes.rb
resources :messages, only: [:create]
resources :rooms, only: [:create, :index, :show]

:shamrock: コントローラー

users_controller.rb
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

:snowflake:before_action :authenticate_user!, only: [:show]
showアクションが実行される前に、ユーザーの認証(ログイン)が行われているか確認している。ユーザーがログインしていない場合にはログインページにリダイレクトさせる。

:snowflake:@user = User.find(params[:id])
params[:id]で、表示するユーザーのIDを取得します。

:snowflake:@currentUserEntry
現在ログイン中のユーザーが関与しているエントリー(チャットルームへの参加)を取得します

:snowflake:@userEntry
表示するユーザーが関与しているエントリーを取得します。

:snowflake:if @user.id == current_user.id
表示するユーザーが自分である場合の処理を行います。

:snowflake:@currentUserEntry.each do |cu|
:snowflake:@userEntry.each do |u|
@currentUserEntry@userEntryに格納されている要素を1つずつ取り出し、一時的な変数cuuに代入して処理を行います。これにより、@currentUserEntry@userEntryの要素を順番にアクセスできます。

:snowflake:if cu.room_id == u.room_id then
cuとuが持つ部屋の番号(room_id)が同じだったら、次のことをする。
:snowflake: @isRoom = true
@isRoomというフラグに「はい」という意味のtrueを設定します。これは、部屋が見つかったことを示すためのフラグです。
:snowflake:@roomId = cu.room_id
見つかった部屋の番号を@roomIdに保存します。これは、後で使うために部屋の番号を保持しておくための変数です。

if @isRoom
      else
        @room = Room.new
        @entry = Entry.new
      end

@isRoomというフラグに基づいて条件分岐を行っています。
@isRoomがtrue(部屋が見つかった場合)であれば、特定の処理を実行します。
@isRoomがfalse(部屋が見つからなかった場合)であれば、以下の処理が実行されます。
@room = Room.new:新しいRoomオブジェクトを作成し、@roomに代入します
@entry = Entry.new:新しいEntryオブジェクトを作成し、@entryに代入します。

rooms_controller.rb
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])
  @rooms = Room.all
  if Entry.where(user_id: current_user.id, room_id: @room.id).present?
    @messages = @room.messages
    @message = Message.new
    @entries = @room.entries
    @user = User.find(@room.user_id) 
    @myUserId = current_user.id
  else
    redirect_back(fallback_location: root_path)
  end
 end
end

:snowflake:@room = Room.create(user_id: current_user.id)
現在のユーザーのIDを持つ新しいルームを作成し、@roomに代入します。

:snowflake:@entry1 = Entry.create(:room_id => @room.id, :user_id => current_user.id)
お部屋に入るためのエントリーを作ります。roomのIDとエントリーした人のIDを@entryに代入する。

:snowflake:@entry2 = Entry.create(params.require(:entry).permit(:user_id, :room_id).merge(:room_id => @room.id))
他の人がエントリーするために、必要な情報を集めます。それは、エントリーした人のIDとroomのIDです。それを使って、新しいエントリーを作成します。

redirect_to "/rooms/#{@room.id}"
作成したルームの詳細ページにリダイレクトします。

@room = Room.find(params[:id]):指定されたIDのroomを見つけます。
@rooms = Room.all:すべてのroomの情報を取得します。

:snowflake:if Entry.where(user_id: current_user.id, room_id: @room.id).present?
ログインしているユーザーが指定されたroomにエントリーしているかをチェックします。

もしエントリーしている場合:
@messages = @room.messages:そのroomのメッセージを取得します。
@message = Message.new:新しいメッセージを作成するための準備をします。
@entries = @room.entries:そのroomのエントリー情報を取得します。
@user = User.find(@room.user_id):roomを作成したユーザーの情報を取得します。
@myUserId = current_user.id:現在ログインしているユーザーのIDを@myUserIdに代入します。

エントリーしていない場合:リダイレクトして、元の場所に戻ります

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(user_id: current_user.id, body: params[:message][:message], room_id: params[:message][:room_id])
    redirect_to "/rooms/#{@message.room_id}"
  else
    redirect_back(fallback_location: root_path)
  end
 end:snowflake:
end
    private 
    def message_params
        params.require(:message).permit(:user_id, :body, :room_id).merge(user_id: current_user.id)
    end

:snowflake: before_action :authenticate_user!, :only => [:create]
ユーザーがログインしているかどうかをチェックしています。
create アクションに対してのみユーザーの認証を行うように指定しています。

:snowflake:if Entry.where(user_id: current_user.id, room_id: params[:message][:room_id]).present?
ログインしているユーザーが指定されたルームにエントリーしているかをチェックしています。

もしエントリーしている場合:
:snowflake:@message = Message.create(user_id: current_user.id, body: params[:message][:message], room_id: params[:message][:room_id])
新しいメッセージを作成し、現在のユーザーのID、メッセージの内容、およびルームのIDを設定します。

:snowflake:redirect_back(fallback_location: root_path)
メッセージが作成されたルームの詳細ページにリダイレクトします。

エントリーしていない場合:
リダイレクトして、元の場所に戻ります

:cherry_blossom:これにより、ユーザーが指定されたルームにエントリーしている場合にメッセージが作成され、それ以外の場合はリダイレクトされるようになりま


:shamrock: views作成

:star:チャットの表示とメッセージの投稿フォーム

rooms/show.haml.erb
<% if @user %>
  <h1 id="room" data-room="<%= @room.id %>" data-user="<%= current_user.id %>"><%= @user.name %> さんとのチャット</h1>
<% end %>
<%= link_to "ユーザー一覧に戻る", users_path %>

<div class="message" style="width: 400px;">
  <% @messages.each do |message| %> 
    <% if message.user_id == current_user.id %>
      <p style="text-align: right;"><%= message.message %></p> 
    <% else %>
      <p style="text-align: left;"><%= message.message %></p>
    <% end %>
  <% end %>
</div>

<div class="errors">
  <%= render "layouts/errors", obj: @message %>
</div>

<%= form_with model: @message, data: {local: false} do |f| %> 
  <%= f.text_field :message %>
  <%= f.hidden_field :room_id, value: @room.id %>
  <%= f.submit "投稿",class: 'form-submit'%>
<% end %>

:point_up:<%= form_with model: @message, data: {local: false} do |f| %> は、**remote: true**でも可

:snowflake:div class="message" タグ内のコードは、@messages 変数に格納されているメッセージを表示しています。each ループを使用して、各メッセージを表示しています。

:snowflake:div class="errors" タグ内のコードは、エラーメッセージを表示するための部分です。render メソッドを使用して、layouts/errors という部分テンプレートを表示します。

:snowflake:<%= form_with model: @message, data: {local: false} do |f| %>
メッセージの投稿フォームを生成しています。@message をモデルとして使用し、data: {local: false} を指定して非同期リクエストを送信するようにしています。フォームにはテキストフィールドと、投稿ボタンも表示されます。

_info.html.erb
<!--メッセージボタン-->
   <% 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">chatへ</a>
  <% else %>
    <%= form_for room do |f| %>
      <%= fields_for entry do |e| %>
        <%= e.hidden_field :user_id, value: user.id %>
      <% end %>
      <%= f.submit "chatを始める", class:"btn btn-primary btn-lg user-show-chat"%>
    <% end %>
  <% end %>
  <% end %>
<% end %>

:snowflake:<% unless user.id == current_user.id %>
ユーザーが自分自身でない場合にのみ表示されるコードブロックを示しています。

:snowflake:<% if (current_user.following? @user) && (user.following? current_user) %>
自分が相手をフォローしていて、相手にもフォローされている場合にのみ表示されるコードブロック(chatへボタン)を示しています。

:snowflake:<% if isRoom == true %>
既にチャットルームが存在する場合にのみ表示されるコードブロック
(chatを始める)を示しています。

:snowflake:<%= form_for room do |f| %> ... <% end %>
チャットルームへのリンクを表示しています。リンク先は /rooms/<%= roomId %> です。

:snowflake:<%= form_for room do |f| %> ... <% end %>
新しいチャットルームを作成するためのフォームを表示しています。
fields_for メソッドを使って entry モデルの隠しフィールドが生成されます。

:star:部分テンプレートを使用しているのでchatボタンを表示したいところに以下のように変数を定義する。

 <%= render 'info', user: @user, isRoom: @isRoom, roomId: @roomId, room: @room, entry: @entry %>

:shamrock: jsファイル

(非同期通信にするよう)
roomsディレクトリ内にcreate.js.erbを作成する。

create.js.erb
$('.message').append("<p style='text-align: right;'><%= @message.body %></p>");
$('input[type=text]').val("");

:shamrock:完成イメージ

参考

0
1
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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?