はじめに
has_many
に条件付きスコープを設定すると、関連を呼び出すだけで自動的に絞り込みがかかります。
使いどころが分かりづらかったので、実際に実装して学んだことをまとめます。
定義例
Companyモデル
class Company < ApplicationRecord
has_many :employees
has_many :engineers, -> { where role: ROLE_ENGINEER }, class_name: "Employee"
has_many :designers, -> { where role: ROLE_DESIGNER }, class_name: "Employee"
has_many :interns, -> { where role: ROLE_INTERN }, class_name: "Employee"
end
-
has_many :engineers, -> { where role: ROLE_ENGINEER }, class_name: "Employee"
:engineers
の中から、role
がROLE_NEGINERRS
のものだけを絞り込む-
->{where ...}
: スコープ付き関連。関連を呼び出し時に自動で条件が付く -
class_name: "Employee"
: 関連名(engineers)とクラス名が一致しないため明示的に指定
-
Employeeモデル
class Employee < ApplicationRecord
belongs_to :company
# roleカラムの値
ROLE_ENGINEER = 1
ROLE_DESIGNER = 2
ROLE_INTERN = 3
end
呼び出し例
company.engineers
company.employees.where(role: ROLE_ENGINEER)
# 両方とも以下のようなSQLが実行され、同じ結果となる
SELECT `employees`.* FROM `employees`
WHERE `employees`.`compay_id` = 1
AND `employees`.`role` = 1
メリット
- 可読性が高い
company.engineers
のように自然な形で呼び出せる - ActiveRecordの機能がそのまま使える
- 再利用しやすい
条件をモデルに集約できる
デメリット
- 関連が増えるとモデル定義が肥大化する
- 条件が複雑になると可読性が落ちる
おわりに
ドメインとして意味のある分類は、モデルのアソシエーションとして定義すると再利用性や可読性が高まります。一方で、一時的な絞り込みやビュー専用の条件など、ドメインに直接関係しないものはスコープやヘルパーで対応した方が使いやすい場合があるので見極めが必要になりそうです。