【Rails】マッチングアプリのDMフォームを作成する方法

#環境
macOS 10.15.7
Rails 5.2.4.4
Docker 19.03.13

#前提
●ログインユーザーと特定のユーザー同士のみがDMページへ移動してやり取りを行うことを前提
●ログインユーザーがDM中のユーザーの一覧が閲覧できるページを作成
●メニューバーからDM中のユーザーの一覧への移動が可能

#特定のユーザー同士がやり取りできるDMページの作成
まずは特定のユーザー同士がやり取りできるDMページの作成をします。
##モデルの作成
UseRoomテーブルを作成してカラムは以下のようにします。

```ruby:terminal
rails g model UserRoom user_id:integer room_id:integer
```
Roomテーブルを作成します。

```ruby:terminal
rails g model Room
```
messageテーブルを作成してカラムは以下のようにします。

```ruby:terminal
rails g model Message user_id:integer room_id:integer message:string
```

次に以下のように関連付けさせます。

```ruby:app/models/room.rb
has_many :user_rooms
has_many :messages
```

```ruby:app/models/message.rb
belongs_to :user
belongs_to :room
```

```ruby:app/models/user.rb
has_many :user_rooms
has_many :messages
```
マイグレートをします。

```ruby:terminal
rails db:migrate
```

##コントローラーの作成

```ruby:terminal
rails g controller messages
```
コントローラーに以下のように追記します。

```ruby:app/controllers/messages_controller.rb
class MessagesController < ApplicationController
  before_action :authenticate_user
  def show
    # これからDMを行うユーザーを取得する
    @user = User.find(params[:id])
    # ログイン中のユーザーのuser_roomにあるroom_idの値の配列を取得する
    rooms = @current_user.user_rooms.pluck(:room_id)
    # room_idが上記のroomsに一致するレコードを取得する
    user_rooms = UserRoom.find_by(user_id: @user.id, room_id: rooms)
    # user_roomが見つかったら@roomに代入する
    unless user_rooms.nil?
      @room = user_rooms.room
    else
      # user_roomがない場合は新しくroomを作る
      @room = Room.new
      @room.save
      # user_roomをログインしているユーザーと相手の分を作成する
      UserRoom.create([{:user_id => @current_user.id, :room_id => @room.id}, {:user_id => @user.id, :room_id => @room.id}])
    end
    # メッセージの履歴を取得
    @messages = @room.messages
    # 新規メッセージ用オブジェクトの作成
    @message = Message.new(room_id: @room.id)
    render("users/message_form")
  end
  def create
    # メッセージの作成
    # message_paramsからパラメータを取得する
    @message = @current_user.messages.new(message_params)
    @message.save
    redirect_to request.referer
  end
  private
  def message_params
    params.require(:message).permit(:message, :room_id)
  end
end
```

##routes.rbの編集
下記のコードを追記します。

```ruby:routes.rb
  get 'message/:id' => 'messages#show', as: 'message'
  resources :messages, only: [:create]
```

##viewの作成
今回はメッセージフォームの下にユーザー同士のコメントが表示されるようになります。

```html:message_form.html.erb


<div class="dm">
  <h1>DMを送る</h1>
  <div class="message-form">
    <%= form_with model: @message do |f| %>
      <%= f.text_area :message %>
      <!-- メッセージ作成時に必要な :room_idをおいておく -->
      <%= f.hidden_field :room_id %>
      <%= f.submit "投稿" %>
    <% end %>
  </div>
  <table>
    <thead>
        <tr>
            <td>投稿者名</td>
            <td>投稿内容</td>
        </tr>
    </thead>
    <tbody>
        <% @messages.each do |message| %>
            <tr>
                <td><%= message.user.name %></td>
                <td><%= message.message %></td>
            </tr>
        <% end %>
    </tbody>
</table>
</div>
```

#DM中のユーザーを一覧画面で表示
次にDM中のユーザーを一覧画面で表示できるページを作成します。
##viewの作成
viewを下記のようにします。

```html:message_user.html.erb
<% @other_rooms.each do |room| %>
  <!-- やり取り相手の名前を表示 -->
  <%= link_to(room.user.name, "/users/#{room.user.id}") %> <br>
  <!-- やり取りの中で最新のメッセージを取得して、リンクとして表示する -->
  <%= link_to(Message.find_by(id: room.room.message_ids.last)&.message, "/message/#{room.user.id}") %> <br>
<% end %>
```

一覧に表示させるためにこちらを追記します。

```ruby:posts_controller.rb
def show_last_messages
    # ログイン中のユーザーのやり取り一覧を取得する
    current_rooms = @current_user.user_rooms
    # 空の配列を作り、ここにRoomのIDだけを格納する
    my_room_ids = []
    current_rooms.each do |user_room|
      my_room_ids << user_room.room.id
    end
    # ログイン中のユーザーを相手のIDに合わせ取得しする
    @other_rooms = UserRoom.where(room_id: my_room_ids).where.not(user_id: @current_user.id)
    render("users/message_users.html.erb")
end
```

##routes.rbの編集
こちらを追記します。

```ruby:routes.rb
get "users/last_message" => "posts#show_last_messages"
```

##一覧ページに飛ぶためのリンク作成
わたしの場合はメニューバーから飛べるようにしたかったため下記のように追記しました。

```html:application.html.erb
<ul class="header-menus">
  <% if @current_user %>
    <li>
      <%= link_to("こんにちは！#{@current_user.name}さん", "/users/#{@current_user.id}") %>
    </li>
　　<!--下記のように追記-->
    <li>
      <%= link_to("DM一覧", "/users/last_message") %>
    </li>
    <li>
      <%= link_to("ログアウト", "/logout") %>
    </li>
  <% else %>
    <li>
      <%= link_to("新規登録", "/signup") %>
    </li>
    <li>
      <%= link_to("ログイン", "/login") %>
    </li>
  <% end %>
```

今回は一例ですがご参考までになれたら幸いです^^