LoginSignup
0
0

More than 5 years have passed since last update.

多対多の関連付け

Posted at

背景

自分は現在院生2回生で, 4回生のころから京都のゲーム・Web会社で働いている. 今も細々と続けている中で, Ruby on Railsに触れる機会があったのでその知見を貯めておく.

今回は多対多の関連付けと, その使い方を紹介する.

データ構造

このアプリケーションには, usergroupの定義があって, userは複数のgroupに所属することができて, groupは複数のuserを持つ. よって中間テーブルを用いて多対多の関連付けを定義する.

schema.rb
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つ貼られているが, 以下のようにして簡単に貼ることができる.

create_group_users.rb
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

関連付け

以下がそれぞれのモデルである.

group_user.rb
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で関連づける.

user.rb
class User < ApplicationRecord
  has_many :group_users, dependent: :nullify
  has_many :groups, through: :group_users
end
group.rb
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で中間テーブルを利用する.

処理の書き方

自分が属するグループ全てを返す処理を以下に書く.

groups_controller.rb
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の良さだと思った.

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