DM機能を作成する

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 レコードを持ちます。これは、**１対多**の関係です。また、関連するレコードはユーザーが削除されたときに一緒に削除されるように設定されています (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
空でない＆最大１４０文字以下であるバリデーションも追加
　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つずつ取り出し、一時的な変数``cu``と``u``に代入して処理を行います。これにより、@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に保存します。これは、後で使うために部屋の番号を保持しておくための変数です。
```diff_ruby
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ボタンを表示したいところに以下のように変数を定義する。
```diff_ruby
 <%= 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:完成イメージ
https://gyazo.com/58d03096966a4b94bc69220e0e05106c

### 参考

https://zenn.dev/goldsaya/articles/71758cf3024dc1

https://zenn.dev/hikaru_q/articles/d3923fce0f1014