Railsによる自己結合でUserモデルの親子関係を実装する
Railsで自己結合の実装方法をまとめます。
Railsで自己結合
例として、Userモデル内で自己結合し「親子関係」を表現します。
以下のようなusersテーブルがあるとします。
基本的には、usersテーブル内に親と子を区別せずに格納し、あるユーザのparent_login_idカラムを他のユーザのlogin_idと照合することで、親子関係を構築できます。
usersテーブル
| カラム | 説明 |
|---|---|
| id | 主キー |
| name | ユーザ名 |
| login_id | ユーザ固有のログインID |
| parent_login | 自分の親ユーザのlogin_idを保持する。親がいない場合はNULL |
Userモデルで以下のようにすると自己結合ができます。
user.rb
class User < ApplicationRecord
# 親ユーザと子ユーザの関連付け
belongs_to :parent, class_name: "User", foreign_key: "parent_login_id", primary_key: "login_id", optional: true
has_many :children, class_name: "User", foreign_key: "parent_login_id", primary_key: "login_id"
...
関連付けの定義
以下解説です。
1.親との関連を定義
belongs_to :parentで親との関連を定義します。
belongs_to :parent, class_name: "User", foreign_key: "parent_login_id", primary_key: "login_id", optional: true
-
belongs_to :parent
各Userインスタンスは、自分の親ユーザを参照できるようになる。 -
class_name: "User"
参照先のモデルはUser。 -
foreign_key: "parent_login_id"
parent_login_idをキーに親ユーザを検索する。 -
primary_key: "login_id"
親ユーザ側ではlogin_idカラムと照合する。通常のbelongs_toはidで照合するが、ここではlogin_idで探す。 -
optional: true
親ユーザが存在しない場合でもエラーとならないようにする。
この設定により、例えばあるユーザのparent_login_idが"parent123"であれば、Railsは
SELECT "users".* FROM "users" WHERE "users"."login_id" = 'parent123' LIMIT 1;
というクエリを発行し、親ユーザのレコードを取得します。
2. 子との関連を定義
has_many :childrenで子との関連を定義します。
has_many :children, class_name: "User", foreign_key: "parent_login_id", primary_key: "login_id"
-
has_many :children
各Userインスタンスは、自分を親として持つ子ユーザの複数のレコードにアクセスできる。 -
class_name: "User"
子ユーザもUserテーブル内のレコード。 -
foreign_key: "parent_login_id"
parent_login_idをキーに検索。 -
primary_key: "login_id"
自分のlogin_idカラムの値が、子ユーザのparent_login_idカラムに一致するレコードが対象になる。
これにより以下のクエリが発行される。
SELECT "users".* FROM "users" WHERE "users"."parent_login_id" = 'parent123';
画面に出力
以下のようにして出力できます。
<h1>ユーザー詳細: <%= @user.login_id %></h1>
<!-- 親ユーザーの情報 -->
<div>親ユーザー: <%= @user.parent.login_id %></div>
<!-- 子ユーザーの情報 -->
<div>子ユーザー(1人): <%= @user.children.last.login_id %></div>
まとめ
自己結合では、
- 親子関係のような
1:Nの関係では、同一モデル内でbelongs_toとhas_manyの関連付けを行うことで実現できる。 - 友達関係のような
N:Nの関係では、両方向でhas_manyを定義する。 - 配偶者のような
1:1の関係では、belongs_toとhas_oneの関連付けをする。
Discussion