メインコンテンツへスキップ

Documentation Index

Fetch the complete documentation index at: https://kawax.biz/llms.txt

Use this file to discover all available pages before exploring further.

リレーションとは

データベースのテーブルは互いに関連し合っていることがよくあります。たとえば、ブログ投稿には多数のコメントがあったり、注文はそれを行ったユーザーに関連していたりします。 Eloquentはこうしたテーブル間の関連(リレーション)を簡単に定義・操作する仕組みを提供しています。リレーションはEloquentモデルのメソッドとして定義します。
このページでは UserPostCommentTag などの具体的なモデルを例として使います。 これらのモデルとテーブルはあらかじめ作成されている前提で進めます。
Eloquentが対応する主なリレーションは以下のとおりです。
リレーション説明
hasOne1対1(親が1つの子を持つ)
belongsTo1対1の逆(子が親に属する)
hasMany1対多(親が複数の子を持つ)
belongsToMany多対多(中間テーブルを使う)

hasOne(1対1)

hasOne は、あるモデルが別のモデルを1つだけ持つ関係を表します。たとえば User が1つの Profile を持つ場合です。

リレーションの定義

User モデルに profile メソッドを定義します。
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;

class User extends Model
{
    public function profile(): HasOne
    {
        return $this->hasOne(Profile::class);
    }
}
Eloquentは親モデル名をもとに外部キーを自動的に推測します。この場合、profiles テーブルに user_id カラムがあると想定します。

関連データの取得

定義したリレーションはプロパティとしてアクセスできます。
$user = User::find(1);
$profile = $user->profile;
リレーションメソッドをプロパティとして呼び出すと、Eloquentが自動的にクエリを発行して関連データを返します。これを「ダイナミックリレーションプロパティ」といいます。

belongsTo(1対1の逆)

belongsTohasOne の逆で、外部キーを持つ側のモデルに定義します。ProfileUser に属する関係を表します。

リレーションの定義

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Profile extends Model
{
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }
}
Eloquentはリレーションメソッド名に _id を付けたカラム名(この場合 user_id)を外部キーとして使います。

関連データの取得

$profile = Profile::find(1);
$user = $profile->user;

echo $user->name;

hasMany(1対多)

hasMany は、1つの親モデルが複数の子モデルを持つ最もよく使われるリレーションです。たとえば、1つの Post が複数の Comment を持つ場合です。

リレーションの定義

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Post extends Model
{
    public function comments(): HasMany
    {
        return $this->hasMany(Comment::class);
    }
}
Eloquentは comments テーブルに post_id カラムがあると想定します。

関連データの取得

hasMany のリレーションはコレクションを返します。
$post = Post::find(1);

foreach ($post->comments as $comment) {
    echo $comment->body;
}
クエリに条件を追加することもできます。
$recentComments = Post::find(1)
    ->comments()
    ->latest()
    ->take(5)
    ->get();

逆のリレーション(belongsTo)

コメントから投稿を参照するには、Comment モデルに belongsTo を定義します。
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Comment extends Model
{
    public function post(): BelongsTo
    {
        return $this->belongsTo(Post::class);
    }
}
$comment = Comment::find(1);
echo $comment->post->title;

belongsToMany(多対多)

多対多リレーションは、両方のモデルが互いに複数の関連を持つ場合に使います。たとえば、Post が複数の Tag を持ち、Tag も複数の Post に属する場合です。

テーブル構造

多対多には中間テーブルが必要です。PostTag の場合、post_tag という中間テーブルを用意します。
posts
    id - integer
    title - string

tags
    id - integer
    name - string

post_tag
    post_id - integer
    tag_id - integer
中間テーブル名はEloquentが関連する2つのモデル名をアルファベット順に結合して自動的に推測します(post + tagpost_tag)。

リレーションの定義

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Post extends Model
{
    public function tags(): BelongsToMany
    {
        return $this->belongsToMany(Tag::class);
    }
}
Tag 側にも同様に定義することで、逆方向からも参照できます。
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Tag extends Model
{
    public function posts(): BelongsToMany
    {
        return $this->belongsToMany(Post::class);
    }
}

関連データの取得

$post = Post::find(1);

foreach ($post->tags as $tag) {
    echo $tag->name;
}

関連データの追加・削除

attach で中間テーブルにレコードを追加、detach で削除します。
$post = Post::find(1);

// タグを追加
$post->tags()->attach($tagId);

// タグを削除
$post->tags()->detach($tagId);

// 現在の関連をすべて置き換える
$post->tags()->sync([$tagId1, $tagId2]);
sync を使うと、指定したID以外の中間テーブルレコードを自動的に削除して、渡したIDだけが関連として残るように更新します。

Eagerローディング

N+1問題とは

リレーションをプロパティとしてアクセスすると、Eloquentはその都度クエリを発行します。これを「遅延ロード(Lazy Loading)」といいます。ループ内でリレーションにアクセスすると、N+1問題が発生します。
// 全投稿を取得する1回のクエリ
$posts = Post::all();

foreach ($posts as $post) {
    // 各投稿ごとにユーザーを取得するクエリが発行される(N回)
    echo $post->user->name;
}
投稿が100件あれば、合計101回のクエリが発行されます。これはパフォーマンスに大きな影響を与えます。

with()によるEagerローディング

with() メソッドを使うと、関連データをまとめて取得できます。これにより、クエリが2回だけに抑えられます。
// 投稿とユーザーを2回のクエリで取得
$posts = Post::with('user')->get();

foreach ($posts as $post) {
    // 追加のクエリは発行されない
    echo $post->user->name;
}
実行されるSQLは以下の2つだけです。
select * from posts

select * from users where id in (1, 2, 3, ...)

複数のリレーションを同時にEagerロード

配列で複数のリレーションを指定できます。
$posts = Post::with(['user', 'comments', 'tags'])->get();

ネストしたEagerロード

ドット記法でネストしたリレーションもEagerロードできます。
// 投稿のコメントと、各コメントのユーザーをEagerロード
$posts = Post::with('comments.user')->get();
EagerローディングはN+1問題を防ぐために非常に重要です。ループ内でリレーションにアクセスするときは、常に with() を使う習慣をつけましょう。

次のステップ

Eloquent入門

Eloquentの基本的なCRUD操作に戻って復習します。
Last modified on April 10, 2026