Skip to content

リレーション設計 ― 逆に書いたらどうなる?

📖この章のキーワード
用語意味なぜ重要か
hasMany「1つの親が複数の子を持つ」リレーションFriend が複数の PointHistory を持つ
belongsTo「子が1つの親に属する」リレーションPointHistory が Friend に属する
外部キー別テーブルの主キーを参照するカラムテーブル間のリレーションを実現する
N+1 問題メインクエリ 1 回 + リレーション取得 N 回の非効率パターン一覧画面で致命的な遅延を招く
Eager Loading (with())リレーションを事前に一括取得する最適化N+1 を 2 クエリに削減

Phase 1: 観察

LIBOT の Friend モデル(LINE ユーザー)のリレーション定義を見てみましょう。

php
// Friend.php (excerpt)
// Label: trade-off — リレーション数が多く、N+1リスクを内包している

class Friend extends Model
{
    // 1 つの Friend は複数のポイント履歴を持つ
    public function point_histories(): HasMany
    {
        return $this->hasMany(FriendPointHistory::class);
    }

    // 1 つの Friend は複数の住所を持つ
    public function addresses(): HasMany
    {
        return $this->hasMany(FriendAddress::class);
    }

    // 1 つの Friend は複数のグループに属する
    public function group_details(): HasMany
    {
        return $this->hasMany(FriendGroupDetail::class);
    }

    // 1 つの Friend は複数のメッセージを持つ
    public function messages(): HasMany
    {
        return $this->hasMany(Message::class);
    }

    // タグ(多対多、ポリモーフィック)
    public function tags(): MorphToMany
    {
        return $this->morphToMany(Tag::class, 'taggable');
    }

    // ... さらに続く(合計 10 以上のリレーション)
}

1 つのモデルに 10 以上のリレーション。


Phase 2: 判断

AI禁止ゾーン
  • hasManybelongsTo逆に書いたら 何が起きますか?(例: Friend が FriendPointHistory に belongsTo)
  • Friend に 10 以上のリレーション があることのリスクは何ですか?
  • Friend::all() で 100 件取得し、各 Friend のタグ一覧を表示するとき、SQL は何回実行 されますか?

AIに聞く前に、自分の頭で考えてみましょう。

テキストを入力すると有効になります