0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

グループやルームの管理者が誰かわかるようにする

Posted at

現在、討論するためのプラットフォームWebアプリを開発してます。そこで、ユーザーAが管理者となって討論ルームを作成し、そのルームに複数のユーザーが参加するわけですが、その仕組みを作る方法についてアウトプットします。方法は知っている限り2つあります。

1つ目はルーム作成者(=オーナー)を管理するためのテーブルを他に作る方法。
2つ目はルームテーブル内にオーナーを管理するためのカラムを追加する方法。

今回は2つ目の方法について、特に難しかったアソシエーションを中心に解説します。難しかった原因は「ユーザーとルームは多対多のアソシエーションであるのにも関わらず、オーナー管理の部分だけ見るとユーザー:ルームは1:多の関係になってしまうことです(ルームには必ず1人だけ管理者が存在し、ユーザーは複数のルームの管理者になれる)」というものです。解決方法を要約すると、「ユーザーとルームの多対多アソシエーションを作成するのに加え、ルームからユーザーへblongs_toのアソシエーションを組む」となります。

1つ目の方法の概要やメリットについては最後におまけで少しだけ言及します。

前提

機能について

あるユーザーAがいます。そのAさんがルームを作成します。ルームを作成したAさんがそのルームの管理者となります。ルームには他のユーザーが参加します。また、ユーザーAは他にもルームを作ることができます。
⇨従って、ユーザーとルームは多対多の関係となります。

機能を作成するためにやること

やるべきことは大雑把に整理すると2つです。1つは中間テーブルを作り、ユーザーとルームの関係を管理すること。もう1つは管理者を管理するためにルームテーブルに管理者のカラムを追加すること。
スクリーンショット 2022-07-05 7.39.33.png

マイグレーションファイル

今回ユーザーはdeviseで作成しています。

user_migrate_file
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
      #(中略)
  end
  #(中略)
end

ルームに追加しているリファレンス型カラムのownerが管理者を登録するためのカラムとなります。

room_migrate
class CreateRooms < ActiveRecord::Migration[6.1]
  def change
    create_table :rooms do |t|
      t.string :title, null: false
      t.references :owner
      t.datetime :deadline, null: false
      t.timestamps
    end
  end
end

マイグレート後、アソシエーションを組みます。

アソシエーション

ユーザーモデル内ではルームテーブルと中間テーブルに対するアソシエーションを記述します。

models/user
class User < ApplicationRecord
   #(中略)
  #association
  has_many :user_rooms,dependent: :destroy
  has_many :rooms ,through: :user_rooms
end

ここからが肝で、ルームモデルには多対多のためのアソシエーションに加えて、管理者カラムのためのアソシエーションを組みます。

models/room
class Room < ApplicationRecord
  has_many :user_rooms,dependent: :destroy
  has_many :users, through: :user_rooms
  #↓で管理者を登録。
  belongs_to :owner, class_name: "User", foreign_key: :owner_id
end

解決方法の解説

抜粋
belongs_to :owner, class_name: "User", foreign_key: :owner_id

ルームの管理者(以下:owner)は1人なので、roomからownerへbelongs_toの関係があることがわかります。ownerテーブルがあればいいのですが、ownerは結局はuserと同じなので、仮にownerテーブルを作ると、テーブルが増えて手間も増えます。だから、ここでは、userテーブルを利用するけれども、テーブルの名前をuserではなくownerと読み替えさせるようにします。読み替えさせる方法がclass_nameであり、上記を再度書き換えると下記になります。

抜粋
belongs_to :自分で設定するテーブルの読み方, class_name: "元となるテーブル名", foreign_key: :owner_id

"foreign_key: :owner_id"は記述しなくてもテーブルの作成やオーナー管理は問題なく行えます。しかし、記述しない場合、ユーザーを削除しようとするときに、エラー原因になります。ownerカラムはマイグレーションファイルで設定した通り、リファレンス型のカラムなので、外部キーが必要になります。ただ、外部キー設定をマイグレーションファイル内に記述すると、外部キー先との関係が多対多になっているため、これもまたエラーの原因になります。ゆえに、アソシエーション部分で、ownerのカラムが外部キーになっていることを宣言します。※owner_idと記述しているのは、実はカラム名がowner_idになっているからです。マイグレーションファイルで"t.reference :カラム名"として記述しているので、テーブル作成時にはカラム名_idとしてカラムに登録されます。

自信満々に書いているように見えますが、間違っていたらコメントくださるとありがたいです。

おまけ

記事冒頭で紹介した解決方法「ルーム作成者(=オーナー)を管理するためのテーブルを他に作る方法」ですが、実は自分は最初この方法でやっていました。問題なく機能は実現できていたのですが、記述の多さやふと振り返った時に可読性が悪いなどがあり、思い切ってテーブル設計全体を変更することにしました。ちなみに解決方法のイメージとしては下図みたいな感じです。
スクリーンショット 2022-07-05 8.24.27.png
roomからユーザー名を表示するのにも苦労が多く、このテーブル設計には嫌な思い出しかないです。ただ、利用するメリットがある場合もあると思ってます。例えば、管理者を複数人選択できるようにする場合などです。roomテーブル内のみで、オーナーを管理する場合、ユーザーがオーナーの人数を自由に設定できるようにすると、ルームAは管理者が2人、ルームBは管理者が3人などということが起こり得ます。owner1、owner2、owner3・・・とカラムを用意すると、どこかのセルにはnullが入ってしまうことがあるかもしれません。nullが発生してしまうと、そもそも、nullが発生しないために、RDBというDB設計を使っているのに、その意義が薄まってしまうことになります。話がそれましたが、管理者を自由に複数人選択することができる機能を実装する場合、ownerテーブルを作成する必要が出てくるかもしれません。

参考サイト

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?