🙆

Railsによる自己結合でUserモデルの親子関係を実装する

2025/02/14に公開

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_toidで照合するが、ここでは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_tohas_manyの関連付けを行うことで実現できる。
  • 友達関係のようなN:Nの関係では、両方向でhas_manyを定義する。
  • 配偶者のような1:1の関係では、belongs_tohas_oneの関連付けをする。

Discussion