【Rails】Ajax チャット機能の実装

主にこちらの記事を参考にして実装しました。


https://qiita.com/IsakiMatsuo/items/9923d0724c69a2dfa259

#前提
Deviseで認証機能実装済み(user)
フォロー機能の実装も完了済み
Bootstrap,kaminari導入済み

#モデル作成

```
$ rails g model chats
```

```Ruby:db/migrate/000000000000_create_chats.rb
class CreateChats < ActiveRecord::Migration[5.2]
  def change
    create_table :chats do |t|
      t.integer :user_id
      t.integer :room_id
      t.text :message

      t.timestamps
    end
  end
end
```

```Ruby:db/migrate/000000000000_create_user_rooms.rb
class CreateUserRooms < ActiveRecord::Migration[5.2]
  def change
    create_table :user_rooms do |t|
      t.integer :user_id
      t.integer :room_id

      t.timestamps
    end
  end
end
```

```Ruby:db/migrate/000000000000_create_rooms.rb
class CreateRooms < ActiveRecord::Migration[5.2]
  def change
    create_table :rooms do |t|
      t.timestamps
    end
  end
end
```


#関連付け　アソシエーション
![名称未設定ファイル-ページ4 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/942060/8ccfd386-8065-0746-cc7f-923ab09d4ed8.png)


```Ruby:app/models/user.rb
class User < ApplicationRecord
  has_many :user_rooms
  has_many :chats
  has_many :rooms, through: :user_rooms
...
end
```

```Ruby:app/models/room.rb
class Room < ApplicationRecord
  has_many :user_rooms
  has_many :chats
end
```

```Ruby:app/models/user_room.rb
class UserRoom < ApplicationRecord
  belongs_to :user
  belongs_to :room
end
```

```Ruby:app/models/chat.rb
class Chat < ApplicationRecord
  belongs_to :user
  belongs_to :room
end
```

#ルーティング
```Ruby:config/routes.rb
get 'chat/:id' => 'chats#show', as: 'chat'
resources :chats, only: [:create]
```

#コントローラー

```Ruby:app/controllers/chats_controller.rb
class ChatsController < ApplicationController
  before_action :authenticate_user!

  def show
    @user = User.find(params[:id])
    # ログインしているユーザーのidが入ったroom_idのみを配列で取得（該当するroom_idが複数でも全て取得）
    rooms = current_user.user_rooms.pluck(:room_id)
    # user_idが@userかつroom_idが上で取得したrooms配列の中にある数値のもののみ取得
    user_rooms = UserRoom.find_by(user_id: @user.id, room_id: rooms)

    if user_rooms.nil? # 上記で取得できなかった場合の処理
      # 新しいroomを作成して保存
      @room = Room.new
      @room.save
      # @room.idと@user.idをUserRoomのカラムに保存
      UserRoom.create(user_id: @user.id, room_id: @room.id)
      # @room.idとcurrent_user.idをUserRoomのカラムに保存
      UserRoom.create(user_id: current_user.id, room_id: @room.id)
    else
      # 取得している場合は、user_roomsに紐づいているroomテーブルのレコードを@roomに代入
      @room = user_rooms.room
    end
    # if文の中で定義した@roomに紐づくchatsテーブルのレコードを代入
    @chats = @room.chats.page(params[:page]).per(13)
    # @room.idを代入したChat.newを用意しておく
    @chat = Chat.new(room_id: @room.id)
  end

  def create
    @chat = current_user.chats.new(chat_params)
    @chat.save
  end

  private

  def chat_params
    params.require(:chat).permit(:message, :room_id)
  end
end

```


#ビュー 
リンク

```erb:app/views/users/show.html.erb
<% if current_user.following?(@user) %>
  <%= link_to "Message", chat_path(@user.id) %>
<% end %>
```

・if current_user.following?(@user) 
（フォローしているユーザーのみ表示）


```erb:app/views/chats/show.html.erb
<div class="mx-auto">
  <div class="message">

    <h3 class="text-left border-bottom m-3"><%= @user.nickname %></h3>
    <% @chats.each do |chat| %>
      <% if chat.user_id == current_user.id %>
        <p class="text-right mr-3">
          <%= chat.created_at.strftime('%Y/%m/%d %H:%M') %></p>
          <p class="text-right mr-3"><%= chat.message %></p>
      <% else %>
        <p class="text-left ml-3">
          <%= chat.created_at.strftime('%Y/%m/%d %H:%M') %></p>
          <p class="text-left ml-3"><%= chat.message %></p>
      <% end %>
    <% end %>
  </div>

  <div class="text-right">
    <%= form_with model: @chat do |f| %>
      <%= f.text_field :message %>
      <%= f.hidden_field :room_id %>
      <%= f.hidden_field :visited_id, :value => @user.id %>
    <% end %>
  </div>

  <div class="d-flex justify-content-center">
    <%= paginate @chats %>
  </div>
</div>
```

#jsファイル追加
```erb:app/views/chats/create.je.erb
$('.message').append("<p class='text-right mr-3'><%= @chat.message %></p>");<!--部分的な更新-->
$('input[type=text]').val("")<!--更新時に入力フォームの中身を空にする-->
```