IT-Q

ポリモーフィックリレーションの代償は何ですか。

外部キー制約を張れないことです。参照先のテーブルが行によって変わるため、どのテーブルを参照するか宣言できません。DB側で整合性を保証できず、孤児レコードが残り得ます。

面接官が見ているのは便利さの裏側を見ているか。

これは「記事とタグのような多対多を設計するとき、正しいのはどれですか。」への追撃質問です。

よくある答えと、面談でどう見られるか

  • 参照先が行ごとに変わるため外部キー制約を張れず、孤児レコードが生まれる
    その通りです。対象が2〜3種類なら、素直に個別テーブルとリレーションを持つ方が安全です。
  • クエリが必ず遅くなる
    JOIN が書きにくいのは事実ですが、morphs() は (型, ID) の複合インデックスを自動で張るので遅いとは限りません。本質的な代償は整合性を DB で保証できないことです。
  • Laravel の新しいバージョンでは非推奨になっている
    非推奨ではありません。現役の機能です。
  • 代償は特にない
    便利さの裏に整合性の犠牲があります。

面接官は何を見ているか

  • 外部キー制約が張れない点を指摘できた
  • JOIN やクエリが複雑になる点に触れた
  • 使うべき場面と避けるべき場面を判断できた

模範解答

最大の代償は外部キー制約を張れないことです。commentable_id が指す先のテーブルが行によって変わるので、「どのテーブルの id を参照するか」を宣言できません。DB 側で整合性を保証できないため、親を消しても子が残る孤児レコードが生まれます。加えて、対象の型ごとに分岐するので JOIN が書きにくくなります。

もう1つ、参画時に効いてくるのが型カラムの中身です。commentable_type には既定でクラスの完全修飾名が文字列で入るため、モデルをリネームしたり名前空間を移動したりすると、既存データが指す先を失います。Relation::enforceMorphMap() で 'post' のような別名に切り離せますが、公式も警告している通り、後から導入する場合はDB に入っている既存の *_type の値を全て変換する必要があります。

コメントや添付ファイルのように対象が本当に多岐にわたる場合は有効ですが、2〜3種類なら素直に個別のテーブルとリレーションを持つ方が安全です。

なお「インデックスが効かない」はよくある誤解です。morphs() は (型, ID) の複合インデックスを自動で張るので、親から子を引く分には効きます。効かないのは外部キー制約の方です。