ほぼCRUDのみの知識でチャット機能を実装

ちょっとずつ色々いじれるようになってきたポンコツ大学生エンジニアが助けを経て頑張ってチャット機能を実装してみました🙇
Railsの勉強をはじめて半年ほどですので、理解が至らないことや間違いなどあると思うので指摘していただけるとありがたいです(^^)


#チャット機能を作るということは....

まず自分と相手がいないと始まりません！
なので、認証機能を作って`current_user`で今ログインしているuserを得られるとします

また、チャットが始まる、ということは同時にチャットルームもできるということです。なので　

`$bin/rails g model user`
`$bin/rails  model chat_user` 
`$bin/rails g model chat`
`$bin/rails g model message`

で、まず必要となるモデルを設計します
user、message,chatモデルは当然必要ですが、
chatとuserは多対多の関係であるので、中間テーブルである`chat_user.rb`を作っておきます

で、モデルのアソシエーションは以下のようにしておきます

```
userは多くのchat,messageを持ち
chatもまた多くのuserを持ち
messageはuserにbelongs_toする。
```

といったところですね😎

```ruby:model/user.rb

class User < ActiveRecord::Base

  has_many :chat_users　
  has_many :messages
  has_many :chats, through: :chat_users 

end
```
```ruby:model/chat.rb
class Chat < ActiveRecord::Base

  has_many :chat_users
  has_many :users, through: :chat_users
  has_many :messages

end
```
```ruby:model/chat_user.rb
class ChatUser < ActiveRecord::Base

  belongs_to :chat
  belongs_to :user

end

```

そしてマイグレーションファイルです

```ruby:db/migrate/init_shema

lass InitSchema < ActiveRecord::Migration
  def change
    create_table(:users) do |t|

        t.string :email, null: false
        t.string :name, null: false
        t.string :password_digest
        t.string :remember_digest
    end

    create_table :chats do |t|
        t.integer :service_id, null: false, index: true
        t.integer :cny, null: false
        t.datetime :start_at#, null: false
        t.timestamps null: false
    end

    create_table :chat_users do |t|
        t.integer :user_id, null: false
        t.integer :chat_id, null: false
    end

    create_table :messages do |t|
        t.integer :user_id, null: false
        t.integer :chat_id, null: false
        t.text :contents, null: false
        t.date :created_at, null: false
    end

##本来でしたらindexなども貼ったり、いろいろ改善点はありますがいったんこれで最小限のmigrationにしておきます

end
```


#実際にチャット機能を始めるための実装

まずroutingを作ります

```ruby:config/routes.rb
Rails.application.routes.draw do

  resources :users
  resources :chats

end
```

とりあえず、雑ではありますがチャットできるuserのスコープをuser全体にして誰とででも会話をできるようにします
`@users = User.all`ですね

```ruby:controller/users_controller.rb
class UsersController < ApplicationController

  def index
    @users = User.all
    @chat = Chat.new
  end
end
```

そしてviewです

楽するため
簡単に書きますが、new_chat_pathのparamsとしてそのuserのidを持たせ、
そしてそのidを自動的に`heidden_field`を使ってchatのcreateに飛ばします

```haml:users/index.html.haml
.container
  .row
    - @users.each do |user|
      %ul
        %li= link_to user.name, new_chat_path(reciever_id: user.id)
      = form_for @chat, url: chats_path, html: {role: 'form', class:"form-horizontal"} do |f|
        = f.hidden_field :reciever_id, value: params[:reciever_id]
      .form-group
        .col-sm-offset-3
          .col-sm-9= f.submit "このサービスで登録", class:"btn btn-primary"

```
で、このformがcreateで送信されてチャットルームができ、かつそのルームにuserが入ってこなくてはいけないので、`chat.rb`に次のメソッドを追加します

```ruby:model/chat.rb

class Chat < ActiveRecord::Base
  has_many :chat_users
  has_many :users, through: :chat_users
  has_many :service_categories
  has_many :messages


   ##追加分##
  def join(user_id)
    self.chat_users.create(user_id: user_id)
  end

```

そしてcreateアクションの中身です😇

```ruby:controller/chats_controller.rb
class ChatsController < ApplicationController
  def create
    @chat = Chat.create
    @chat.join(current_user.id)
    @chat.join(params[:chat][:reciever_id])
    redirect_to @chat
  end
end
```

redirectされたchat#showを追加します

```ruby:chats_controller.rb

  def show
    @chat = Chat.find(params[:id])
    @message = Message.new
    @message_logs = @chat.messages#チャットが持つmesaage、すなわちチャットログです
  end 

```
そしてここで、userが実際にチャットをするので、Message.newに情報を送信するformが必要です

```haml:chats/show.html.haml
.container
  .row
    - @message_logs.each do |message|
      %p= message.contents
    = form_for @message, url: messages_path, html: {role: 'form', class:"form-horizontal"} do |f|
      .form-group
        = f.label :contents, class: "col-sm-3 control-label"
        .col-sm-9= f.text_area :contents, class: "form-control"
      .form-group
        = f.hidden_field :chat_id, value: @chat.id
      .form-group
        .col-sm-4.col-sm-offset-3= f.submit "この内容で送信する", class:"btn btn-primary transition"
```

そしてこの情報をMessageに格納するためのmessage#createが必要ですね


```ruby:messages_controller.rb
class MessagesController < ApplicationController

  def create
    @message = current_user.messages.create(message_params)
    redirect_to :back if @message.save
  end


  private

  def message_params
    params.require(:message).permit(
    :chat_id,
    :user_id,
    :contents,
    :chat_id
    )
  end
end
```

最低限のチャット機能の完成です！！😇