【Ruby on Rails】DM、チャット機能

# 目標
![chat.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700999/89f8486c-7227-5a69-6f4b-2c23143e80ab.gif)

# 開発環境
ruby 2.5.7
Rails 5.2.4.3
OS: macOS Catalina

# 前提
※　▶◯◯　を選択すると、説明等が出てきますので、
　　よくわからない場合の参考にしていただければと思います。

- [deviseでログイン環境構築](https://qiita.com/japwork/items/275110b810965fbdf72f)
- ユーザー詳細画面と一覧と作成済み


# 流れ
1 modelの作成
2 modelの編集
3 controllerの作成
4 routingの編集
5 viewの編集



# modelの作成
```:ターミナル
$ rails g model Room

$ rails g model Chat user_id:integer room_id:integer message:string

$ rails g model UserRoom user_id:integer room_id:integer

$ rails db:migrate
```
<details><summary>補足１【room】</summary>
ユーザー同士が会話をする部屋。</details>
<details><summary>補足２【chat】</summary>
ユーザーが発言した内容を保存するテーブル。</details>
<details><summary>補足３【user_room】</summary>
userとroomが多対多の関係性のため、中間テーブルとしてこれを管理。</details>


# modelの編集
下記のようなリレーションにしていきます。
![スクリーンショット 2020-09-10 22.00.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700999/83096a2c-fa37-5dfd-e44b-53f7abbceb9f.png)

```rb:app/models/room.rb
  has_many :user_rooms
  has_many :chats
```
<details><summary>補足【リレーション】</summary>
room内では多くのuser_roomがあるので、１対多。
room内では多くのchatがあるので、１対多。</details>

```rb:app/chats/room.rb
  belongs_to :user
  belongs_to :room
```
```rb:app/user_rooms/room.rb
  belongs_to :user
  belongs_to :room
```
```rb:app/models/user.rb
  has_many :user_rooms
  has_many :chats
```
<details><summary>補足【リレーション】</summary>
１ユーザーが多くのuser_roomを保有しているので、１対多。
１ユーザーが多くのchatを行うので、１対多。</details>



# controllerの作成
```:ターミナル
$ rails g controller chats
```
```rb:app/controllers/chats_controller.rb

class ChatsController < ApplicationController
  def show
    @user = User.find(params[:id])
    rooms = current_user.user_rooms.pluck(:room_id)
    user_rooms = UserRoom.find_by(user_id: @user.id, room_id: rooms)

    unless user_rooms.nil?
      @room = user_rooms.room
    else
      @room = Room.new
      @room.save
      UserRoom.create(user_id: current_user.id, room_id: @room.id)
      UserRoom.create(user_id: @user.id, room_id: @room.id)
    end
    @chats = @room.chats
    @chat = Chat.new(room_id: @room.id)
  end
  def create
    @chat = current_user.chats.new(chat_params)
    @chat.save
    redirect_to request.referer
  end

  private
  def chat_params
    params.require(:chat).permit(:message, :room_id)
  end
end
```
下記コメントアウトにて、分かりづらい箇所を補足。

```rb:app/controllers/chats_controller.rb
    def show
      # どのユーザーとチャットするかを取得。
      @user = User.find(params[:id])
      
      # カレントユーザーのuser_roomにあるroom_idの値の配列をroomsに代入。
      rooms = current_user.user_rooms.pluck(:room_id)

      # user_roomモデルから
      # user_idがチャット相手のidが一致するものと、
      # room_idが上記roomsのどれかに一致するレコードを
      # user_roomsに代入。
      user_rooms = UserRoom.find_by(user_id: @user.id, room_id: rooms)
  
      # もしuser_roomが空でないなら
      unless user_rooms.nil?
        # @roomに上記user_roomのroomを代入
        @room = user_rooms.room
      else
        # それ以外は新しくroomを作り、
        @room = Room.new
        @room.save
        # user_roomをカレントユーザー分とチャット相手分を作る
        UserRoom.create(user_id: current_user.id, room_id: @room.id)
        UserRoom.create(user_id: @user.id, room_id: @room.id)
      end
      @chats = @room.chats
      @chat = Chat.new(room_id: @room.id)
    end
```

# routingの編集
```rb:config/routes.rb
  get 'chat/:id' => 'chats#show', as: 'chat'
  resources :chats, only: [:create]
```

# viewの編集
```erb:app/view/users/show.html.erb
<% if current_user != @user %>
  <%= link_to 'chatを始める', chat_path(@user.id)%>
<% end %>
```

```erb:app/view/chats/show.html.erb
<%= form_with model: @chat do |f| %>
    <%= f.text_field :message %>
    <%= f.hidden_field :room_id %>
    <%= f.submit %>
<% end %>
<table>
    <thead>
        <tr>
            <td>投稿者名</td>
            <td>投稿内容</td>
        </tr>
    </thead>
    <tbody>
        <% @chats.each do |chat| %>
            <tr>
                <td><%= chat.user.name %></td>
                <td><%= chat.message %></td>
            </tr>
        <% end %>
    </tbody>
</table>
```
<details><summary>補足【f.hidden_field】</summary>
f.hidden_fieldは、表示はしていないものの、paramsとして送りたいものを送る際に活用します。</details>

# 参考
- [RailsでややこしいDM機能を1万字でくわしく解説してみた](https://qiita.com/rui-watanabe/items/6790c91f374acc25bea2#messages_controller)
- [pluckメソッドとidsメソッドについて](https://qiita.com/pandama09396862/items/aa7b60f5697fa63cbd95)

# P.S.
twitterではQiitaにはアップしていない技術や考え方もアップしていますので、
よければフォローして頂けると嬉しいです。
詳しくはこちら　https://twitter.com/japwork