【rails 】他ユーザーが作成したルームに後から参加する、誰がルームを作成したかわかるようにする

テックキャンプ現役受講生がWebアプリを開発するのDay９です。アプリ完成の目標をとりあえず6月末までとしていますので、残り1週間です。終わる気がしません。しかし、嘆いても仕方ないので、明日以降ももやるべきことをコツコツやっていくのみです。さて、今日は他者が作ったルームに、後から他のユーザーが参加する方法について「DB設計、アソシエーション、コントローラーへの記述」の３部分に分けてアウトプットします。

※当方はエンジニア初心者ですので、もっとスマートな方法があるかもしれません。その点ご容赦ください。

# 今回のシチュエーションの設定
登場するテーブルは４つです。Userとroom、User_room、Ownerの４つです。
Userはroomを作成することができ、Roomの中ではチャット等の会話ができる空間を想定しています。
roomを一番はじめに作ったユーザーはOwner（オーナー）として登録し、Ownerにはroomの名前変更等の権限を与えられるものとします。
User＿roomはUserとroomの中間テーブルです。下図を見ていただくとイメージが湧きやすいと思います。
# DB設計
![テックキャンプ-12.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2707093/5e481f66-e86c-d821-c300-114e02b3d297.jpeg)
# マイグレーションファイルの記述
```ruby:user
class DeviseCreateUsers < ActiveRecord::Migration[6.1]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""
      t.string :nickname, null: false
      #以下省略
```
```ruby:room
class CreateRooms < ActiveRecord::Migration[6.1]
  def change
    create_table :rooms do |t|
      t.string :name, null: false
      t.timestamps
    end
  end
end
```
```ruby:中間テーブル
class CreateUserRooms < ActiveRecord::Migration[6.1]
  def change
    create_table :user_rooms do |t|
      t.references :user, foreign_key: true
      t.references :room, foreign_key: true
      t.timestamps
    end
    add_index :user_rooms, [:user_id, :room_id], unique: true #←この記述でルームとユーザーの組み合わせが一意であることを設定しています。
  end
end
```
```ruby:Owner
class CreateOwners < ActiveRecord::Migration[6.1]
  def change
    create_table :owners do |t|
      t.string :name, null: false
      t.references :owner, foreign_key: { to_table: :users }#ユーザーの外部キーですが、別の名前をつけてわかりやすくしています。
      t.references :room, foreign_key: true
      t.timestamps
    end
  end
end
```
Userの外部キーはカラムに"User＿id"と登録されますが、この名前を変更することができます。OwnerテーブルではUserの外部キーに、任意の名前（Owner）をつけてわかりやすくしています。
# アソシエーション
アソシエーションに関係のあるところのみ抽出して記述しています。
```ruby:
class User < ApplicationRecord
  has_many :user_rooms
  has_many :rooms ,through: :user_rooms
  has_many :owners
end
```
```ruby:room
class Room < ApplicationRecord
  has_many :user_rooms
  has_many :users, through: :user_rooms
  has_one :owner
end
```
```ruby:中間テーブル
class UserRoom < ApplicationRecord
  belongs_to :user
  belongs_to :room
end
```
```ruby:Owner
class Owner < ApplicationRecord
  belongs_to :owner, class_name: "User"
  belongs_to :room
end
```
Ownerは中間テーブルではないので、アソシエーションにご注意を。
# コントローラーの記述
リクエストからは取得する情報は以下の通り
・Roomの名前
・ログインしているユーザーの情報（これはdevisを導入していると、current_userで簡単に取得可能）
ヘルパーメソッドのform_withでparams送信します
```ruby:new.html.erb
<%= form_with(model: @room, local: true) do |f| %>
<%= f.text_field :name, placeholder: "ディスカッションのテーマを入力してください",class:"input-name"%>
<%= f.submit "作成", class:"input-name submit-bottom"%>
<% end %>
```
コントローラー内部で三段階に分けて処理をします。
①roomのテーブルに保存
②中間テーブルに情報を保存
③保存したroomの情報をもとにOwnerテーブルにデータを保存
```ruby:controller
class RoomsController < ApplicationController
  def create
    @room = Room.new(room_params)
    if @room.save
      Owner.create(name:current_user.nickname, owner_id: current_user.id,room_id:@room.id)
      redirect_to room_path(@room)
    else
      render 'new'
    end
  end

private
  def room_params
    params.require(:room).permit(:name).merge(user_ids: [current_user.id])
  end
end
```
中間テーブルの保存は「user_ids:〜」という部分で可能になります。↓を参照ください。

https://qiita.com/harukioo/items/7976775c15280d259d38

# まとめ
・”他ユーザーが作成したルームに後から参加する”という機能を実装するには中間テーブルを利用すレバOK。
・”誰がルームを作成したかわかるようにする”には新しく作成したのかわかるように、そのデータを管理するテーブルを新たに作成する。
技術的に難しいところはなく、発想次第で解ける問題でした。

# 反省点
ルーム作成者を明確にするには、そのデータを管理するためのテーブルを新たに作成する必要があることがわかりました。今後、機能を追加する際は、現状のテーブルだけでうまく対処しようとせず、その機能を管理するためのテーブルを作ることも視野に入れなければなりません。また、１つの送信で、２つのテーブルのデータを書き換えているので、今後form_objectを導入するべきだと思いました。
# 解決したかったけど、できなかったこと。
Ownerテーブルにnameというカラムを導入していますが、これはUserテーブルにあるのと同じものです。データが冗長になっていると思いました。これを解決するために、最初の方はアソシエーションでどうにはOwnerの名前をUserのnameカラムから引っ張ってこようと思っていましたが、アソシエーションによる呼び出しの仕方がわからず、どうにもできませんでした。今説明したことを抽象化すると
「親：子のアソシエーションが１対多の時、アソシエーションを利用し子から親を１つ特定する」
ということだと思います。今後も勉強して、いつか理解したいです。
# 参考サイト
https://kimuraysp.hatenablog.com/entry/2017/09/05/235816

https://teratail.com/questions/305686

https://teratail.com/questions/271246