背景
自分は現在院生2回生で, 4回生のころから京都のゲーム・Web会社で働いている. 今も細々と続けている中で, Ruby on Railsに触れる機会があったのでその知見を貯めておく.
今回は多対多の関連付けと, その使い方を紹介する.
データ構造
このアプリケーションには, user
とgroup
の定義があって, user
は複数のgroup
に所属することができて, group
は複数のuser
を持つ. よって中間テーブルを用いて多対多の関連付けを定義する.
ActiveRecord::Schema.define(version: 20_190_219_052_352) do
create_table "group_users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.bigint "user_id", null: false
t.bigint "group_id", null: false
t.index ["group_id"], name: "index_group_users_on_group_id"
t.index %w[user_id group_id], name: "index_group_users_on_user_id_and_group_id"
t.index ["user_id"], name: "index_group_users_on_user_id"
end
create_table "groups", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.string "name", null: false
t.integer "status", default: 1, null: false
end
create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.string "name", null: false
end
end
テーブル名をgroup_users
にして中間テーブルを作る. このテーブルをもとにgroup
からuser
, user
からgroup
を参照する. ここでindexが3つ貼られているが, 以下のようにして簡単に貼ることができる.
class CreateGroupUsers < ActiveRecord::Migration[5.2]
def change
create_table :group_users do |t|
t.references :user, null: false, foreign_key: true
t.references :group, null: false, foreign_key: true
t.timestamps
end
add_index :group_users, %i[user_id group_id]
end
end
関連付け
以下がそれぞれのモデルである.
class GroupUser < ApplicationRecord
validates :user_id, presence: true
validates :group_id, presence: true
belongs_to :user
belongs_to :group
end
1行のgroup_user
が1行のuser
と1行のgroup
に対応しているので, belongs_to
で関連づける.
class User < ApplicationRecord
has_many :group_users, dependent: :nullify
has_many :groups, through: :group_users
end
class Group < ApplicationRecord
has_many :group_users, dependent: :nullify
has_many :users, through: :group_users
def shape_response
{ id: id, name: name }
end
end
1行が複数のgroup
, 複数のuser
と対応しているのでhas_many
. through
で中間テーブルを利用する.
処理の書き方
自分が属するグループ全てを返す処理を以下に書く.
def index
groups = current_user.groups
ok("groups": groups.map(&:shape_response))
end
current_user
は自分のuser
データを返す. それに.groups
と書くだけで自分と紐づくグループが取ってこれる.
ここで, groups
のカラムであるstatus
のみを除いたものを返したい場合は, 上記のように書ける. これは
# 冗長に書いた場合
ok("groups": groups.map { |group|
group.shape_response
})
を省略して記述した場合である. モデルでメソッドを定義しておくとこんなに綺麗に書ける。
感想
関連付けを正確に行うことによって, めちゃめちゃ綺麗に書けるのがrailsの良さだと思った.