第17回: Eloquentリレーション — テーブルの「関係」をコードで表現する

章: 第2章: データベース応用

$post['user_id'] からユーザーを取得するために毎回クエリを書いていませんか?

投稿の著者を取得するたびに WHERE id = ? を書くのは非効率です。Eloquentのリレーションを使えば、$post->user->name と書くだけで関連データを取得できます。

hasMany・belongsToなどのリレーションメソッドを定義することで、テーブルのJOINをPHPのオブジェクト関係として表現できます。

リレーションの実装


<?php
class User extends Model {
    public function posts(): HasMany {
        return $this->hasMany(Post::class);
    }
}
class Post extends Model {
    public function user(): BelongsTo {
        return $this->belongsTo(User::class);
    }
}
$user = User::find(1);
foreach ($user->posts as $post) {
    echo $post->title;
}

リレーションの種類と使いどころ

リレーション 方向
hasOne 1対1(所有側) User → Profile
belongsTo 1対1・1対多(従属側) Post → User
hasMany 1対多(所有側) User → Posts
belongsToMany 多対多 Post ↔ Tag

チェックポイント: $user->posts はプロパティアクセスで「全件コレクション」を取得します。$user->posts() はリレーションのクエリビルダを返すので、.where('status', 'published')->get() のように条件を追加できます。

イーガーローディングでN+1を防ぐ


<?php
// NG: N+1が発生する
$posts = Post::all();
foreach ($posts as $post) {
    echo $post->user->name; // ループのたびにクエリ発行
}

// OK: withで一括取得
$posts = Post::with('user')->get();
foreach ($posts as $post) {
    echo $post->user->name; // 追加クエリなし
}

チェックポイント: with('user') を忘れると前回学んだN+1問題がリレーションで発生します。常にイーガーローディングを意識しましょう。

まとめ & 次のステップ

  • hasMany / belongsTo でテーブルの関連をモデルに定義する
  • プロパティアクセスでコレクション取得、メソッドアクセスでクエリビルダを取得
  • with() イーガーローディングでN+1問題を防ぐ

次回はEloquentスコープとキャストを学びます。クエリの条件をモデルに封じ込め、型変換を自動化する方法です。

Related Articles