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