
Laravelを使ってWebアプリケーションを開発している若手エンジニアや初心者の方、こんにちは!環境構築や基本的なCRUD操作(データの登録・表示・更新・削除)はマスターできましたか?
もし、ToDoリストや簡単なメモアプリを作れたなら、いよいよ次のステップです。アプリをより実用的で本格的なものにするために、データベースのリレーション(関連付け)は避けて通れない重要な概念になります。
「リレーション」と聞くと、少し難しそうに感じるかもしれません。しかし、Laravelの強力な機能であるEloquent ORMを使えば、SQLをほとんど書くことなく、データ間の繋がりを魔法のように扱うことができます。
この記事では、Webアプリの基本中の基本である「ユーザーと投稿(記事)」という身近な例を通して、初心者でも理解しやすい「1対多(One-to-Many)」のリレーションを徹底的に解説します。
![]() | 気づけばプロ並みPHP 改訂版ーーゼロから作れる人になる! [ 谷藤賢一 ] 価格:2970円 |

この記事で学べること
- 「1対多」リレーションの概念と、なぜそれが必須なのか。
- データベースでの外部キー(Foreign Key)の設定方法。
- Laravelの
hasMany
とbelongsTo
を使ったモデル定義。 - SQLを書かずに、ユーザーから投稿、投稿からユーザー名を取得するEloquentマジック。
- 【応用】アプリの動作を遅くするN+1問題とその簡単な解決策。
基本的な操作から一歩踏み出し、あなたのLaravelスキルを一気に引き上げましょう。この記事を最後まで読み終える頃には、あなたは「誰が書いたか」を完璧に管理できる、より本格的なWebアプリケーションを構築できるようになっているはずです!
さあ、Laravelの醍醐味であるリレーションの世界へ飛び込みましょう!

【初心者必見】LaravelのEloquent入門!たった10分でデータベー…
【初心者必見】LaravelのEloquent入門!たった10分でデータベー…
Laravel初心者向けEloquent入門ガイド!SQLが書けなくても、Laravel Eloquentが魔法のようにデータベースを操作する基本を徹底解説。
Laravel初心者向けEloquent入門ガイド!SQLが書けなくても、Laravel Eloquentが魔法のようにデータベースを操作する基本を徹底解説。
リレーションの土台:マイグレーションで外部キーを設定する 🗝️
Laravelで「1対多」リレーションを定義するための最初のステップは、データベースに物理的な繋がりとなる外部キー(Foreign Key)を構築することです。
今回の例では、users
(親)テーブルとposts
(子)テーブルを作成し、posts
テーブルに外部キー user_id
を持たせます。
マイグレーションでテーブルを準備しよう
ここでは、開発を迅速に進めるために、ユーザーと投稿のテーブルを一つのマイグレーションファイルにまとめ、基本的なカラムとリレーションの定義を行います。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void {
// ユーザーテーブル(親)の作成
Schema::create('users', function (Blueprint $table) {
$table->id(); // 主キー (Primary Key)
$table->string('name');
$table->string('email')->unique(); // 一意性を確保
$table->string('password');
$table->timestamps();
});
// 投稿テーブル(子)の作成
Schema::create('posts', function (Blueprint $table) {
$table->id();
// 外部キー user_id の定義
$table->foreignId('user_id')
->constrained() // usersテーブルのidを参照することを制約
->onDelete('cascade'); // ユーザー削除時に投稿も削除
$table->string('title');
$table->text('body');
$table->timestamps();
});
}
public function down(): void {
// 削除時は子テーブルから先にドロップする
Schema::dropIfExists('posts');
Schema::dropIfExists('users');
}
};
コード徹底解説:Laravelリレーションの強力な一行
このマイグレーションの肝となるのは、以下のたった一行です。
重要な一行
$table->foreignId('user_id')->constrained()->onDelete('cascade');
メソッド | 意味と役割 |
---|---|
foreignId('user_id') |
posts テーブルに、外部キーとなる user_id カラム(符号なしBIGINT型)を作成します。Laravelの規約に基づき、「テーブル名_id」という形式を使います。 |
constrained() |
この一行が最も強力です。Laravelはカラム名から自動的にusers テーブルを参照すると判断し、データベースに参照整合性制約を設定します。これにより、存在しないユーザーIDをposts テーブルに登録することを防ぎます。 |
onDelete('cascade') |
実運用で非常に重要な設定です。カスケード削除と呼ばれ、親(ユーザー)が削除された場合、紐づいている全ての子(投稿)もデータベースから自動的に削除されます。これにより、参照元がない孤立データの発生を防ぎます。 |
このマイグレーションを実行することで、データベースは「ユーザーと投稿は外部キー user_id
で結びついている」という物理的な保証を持つことになります。
$ php artisan migrate
マイグレーションが成功したら、これでリレーションの土台は完成です。次は、Laravelのモデルを使って、この繋がりをコード上で表現します。
![]() | PHPフレームワークLaravel入門第2版 [ 掌田津耶乃 ] 価格:3300円 |

![]() | 動かして学ぶ!Laravel開発入門 (NEXT ONE) [ 山崎 大助 ] 価格:3300円 |

Eloquentモデルの定義:コードに「繋がり」を教える 🤝
データベースに物理的な外部キーができたら、次はLaravelのEloquentモデルを使って、その繋がりを論理的に定義します。これにより、SQLを書かずにデータを行き来する「魔法」が使えるようになります。
「1対多」のリレーションを定義するために、親モデル(User
)と子モデル(Post
)のそれぞれに、専用のメソッドを記述します。
親モデル(User.php):hasMany()
を定義する
User
モデルは複数の投稿を持つので、Laravelの標準メソッドである hasMany()
を使います。
<?php
// app/Models/User.php
use App\Models\Post;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
// …(省略)
/**
* ユーザーが所有する投稿を取得
*/
public function posts()
{
// ユーザーは多くの投稿を持つ
return $this->hasMany(Post::class);
}
}
メソッド名は関連付けるモデルの複数形(posts
)にすることが、Laravelの規約(Convention)です。この規約に従うことで、Laravelは「このユーザーのIDを、Postモデルの外部キー(user_id
)で探せばいいんだな」と自動的に判断してくれます。
子モデル(Post.php):belongsTo()
を定義する
次に、Post
モデルはただ一つのユーザーに属するので、 belongsTo()
メソッドを使います。
<?php
// app/Models/Post.php
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Post extends Model
{
use HasFactory;
/**
* 投稿の作成者であるユーザーを取得
*/
public function user()
{
// 投稿は一つのユーザーに属する
return $this->belongsTo(User::class);
}
}
こちらのメソッド名は、関連付けるモデルの単数形(user
)にすることが規約です。このuser()
というメソッドの裏側で、LaravelはPostレコードのuser_id
を見て、users
テーブルから一致するレコードを引っ張ってきてくれます。
Eloquentマジック!リレーションを使ったデータの取得法 ✨
モデルにリレーションを定義した今、あなたはSQLを一切書かずに、関連データに自由自在にアクセスできます。これこそがLaravelを使う最大のメリットです。
パターンA:投稿から「誰が書いたか」を取得する(belongsTo
の利用)
ある投稿レコードから、その投稿の作者名を知りたい場合を考えましょう。Post
モデルには user()
メソッドを定義しましたが、データにアクセスするときはメソッド名に丸括弧(()
)を付けません。
Laravelが自動的にメソッドを実行し、関連するユーザーモデルのインスタンスを返してくれるのです。
<?php
// IDが5の投稿を取得
$post = App\Models\Post::find(5);
// メソッドとして呼び出さず、プロパティとしてアクセス!
$userName = $post->user->name;
$userEmail = $post->user->email;
echo "この投稿の作者は: " . $userName . " さんです。";
// 例: この投稿の作者は: 山田 太郎 さんです。
この$post->user
という書き方は、開発者にとって非常に直感的で、コードの可読性を一気に向上させます。
パターンB:ユーザーから「すべての投稿」を取得する(hasMany
の利用)
次に、特定のユーザーが書いた投稿を一覧で表示したい場合です。User
モデルに定義した posts()
メソッドを使って、投稿のコレクション(配列に似たデータ構造)を取得します。
<?php
// IDが1のユーザーを取得
$user = App\Models\User::find(1);
// ユーザーが持つすべての投稿を取得
$posts = $user->posts;
// コレクションをループして投稿タイトルを表示
echo "<ul>";
foreach ($posts as $post) {
echo "<li>" . $post->title . "</li>";
}
echo "</ul>";
$user->posts
の結果は、Eloquentのコレクションです。これはPHPの配列と似ていますが、データを操作するための便利なメソッド(フィルター、マップなど)が豊富に用意されています。これで、ユーザー詳細ページやマイページの実装が劇的に簡単になります。

これで迷わない!LaravelビューとBladeテンプレートの仕組みと使い方…
これで迷わない!LaravelビューとBladeテンプレートの仕組みと使い方…
これで迷わない!LaravelのビューとBladeのすべてを解説。HTMLとの違い、@記法の使い方、コンポーネントによる効率的な開発術まで、初心者向けに図解とコードで分かりやすく紹介します。
これで迷わない!LaravelのビューとBladeのすべてを解説。HTMLとの違い、@記法の使い方、コンポーネントによる効率的な開発術まで、初心者向けに図解とコードで分かりやすく紹介します。
【脱初心者へ】パフォーマンス最適化:N+1問題とEager Loading ⚡
リレーションの「魔法」は非常に便利ですが、使い方を誤るとアプリケーションが急激に遅くなる原因になります。これが、若手エンジニアが必ずつまずくN+1問題です。
N+1問題とは何か?
「すべての投稿の一覧を表示し、投稿ごとに作者名を表示する」という、よくある処理を考えてみましょう。
先ほどのパターンAの書き方を、投稿が100件ある状態で実行すると、以下のクエリ(データベースへの問い合わせ)が発生します。
- 投稿をすべて取得するクエリ:1回
- ループ内で、投稿ごとにユーザーを取得するクエリ:100回(N回)
合計で 1 + 100 = 101回 もデータベースにアクセスすることになり、これがアプリケーションの速度を低下させる主犯となります。これが「N+1」と呼ばれる所以です。
解決策:with()
を使ったEager Loading (事前ロード)
この問題を解決するのが、Eager Loading(イーガーローディング:事前ロード)です。関連するデータを事前にまとめて取得しておくことで、クエリの回数を劇的に減らします。Laravelでは、with()
メソッドを使います。
先ほどの101回クエリが走っていた処理を、with()
で書き換えてみましょう。
<?php
// with('user') を追加!
// 投稿と、関連するユーザー情報をまとめて取得 (クエリ2回に減少)
$posts = App\Models\Post::with('user')->get();
foreach ($posts as $post) {
// ユーザー情報は既にロード済み(キャッシュ済み)なので、追加クエリは発生しない
echo "投稿タイトル: " . $post->title . " (投稿者: " . $post->user->name . ")";
}
処理 | クエリ回数 | 速度 |
---|---|---|
Eager Loadingなし | N + 1 回 (例: 101回) | ❌ 遅い |
Eager Loadingあり (with('user') ) |
2 回 | ✅ 速い! |
with('user')
は、Laravelに対して「posts
を取得するとき、関連するuser
の情報も一緒にまとめて取得しておいてね」という指示を出しています。これにより、データベースへのアクセスは投稿取得のための1回と、全ユーザーIDに紐づくユーザー情報取得のための1回の、合計2回だけで済みます。

LaravelでCRUDってなに?初心者でもすぐできるデータ操作入門 – ガ…
LaravelでCRUDってなに?初心者でもすぐできるデータ操作入門 – ガ…
Laravel初心者必見!CRUD(作成・読み取り・更新・削除)の基本をわかりやすく解説。データ操作を簡単に実装できる方法やEloquentの使い方まで丁寧に紹介。
Laravel初心者必見!CRUD(作成・読み取り・更新・削除)の基本をわかりやすく解説。データ操作を簡単に実装できる方法やEloquentの使い方まで丁寧に紹介。
FAQ:初心者がつまずきやすいリレーションの疑問を解決! ❓
ここでは、「1対多」リレーションを学ぶ上で、若手エンジニアや初心者がよく抱く疑問について、一歩踏み込んだ解説を行います。
Q. リレーションを定義しないとどうなる?(手動クエリ vs Eloquent)
A. リレーション(hasMany
やbelongsTo
)をモデルに定義しなくても、生のSQLを書いたり、クエリビルダーを使ったりすれば、データ取得は技術的には可能です。
しかし、その場合、毎回「posts
テーブルをuser_id
でusers
テーブルとJOIN
する」というSQL文を手動で組み立てる必要があり、非常に手間がかかります。
LaravelのEloquentを使うメリットは、以下の通りです。
- 可読性の向上:
$post->user->name
のように、コードが英語の文章のように読めるようになります。 - 保守性の向上: データベースのテーブル名やカラム名が変わっても、モデルのリレーション定義を修正するだけで済み、コード全体を直す必要がありません。
- 機能の恩恵: Eager Loading(
with()
)やリレーションの挿入(後述する$user->posts()->create()
など)といった、Eloquentが提供する便利な機能を使えなくなってしまいます。
結論として、データ間の関連がある場合は、必ずリレーションを定義しましょう。それが「Laravelらしい」開発手法です。
Q. リレーション名(メソッド名)は自由に決めていい?
A. 基本的には自由です。例えば、User.php
のposts()
メソッドをarticles()
やmyContents()
と名付けても、Laravelはエラーを出しません。
ただし、Laravelには暗黙的な規約(Convention)があり、これに従うことで引数を省略できたり、他の開発者との連携がスムーズになったりします。
hasMany
(1側)の場合: リレーション先のモデル名(Post
)の複数形(posts
)にするのが慣習です。belongsTo
(多側)の場合: リレーション先のモデル名(User
)の単数形(user
)にするのが慣習です。
もし、規約と異なる名前を付けたい場合は、メソッドの第2引数以降で外部キー名やローカルキー名などを明示的に指定する必要があります。初心者の方は、まずは規約通りに単数形・複数形を使うことを強くお勧めします。
Q. Eager Loading(with()
)はいつ使うべき?(判断基準)
A. Eager Loading(事前ロード)は、一度に複数の親レコードと、それに関連する子レコードを表示する必要がある場合に必須です。
判断の目安は、以下の通りです。
- ✅ 使うべきケース: 投稿一覧ページで、すべての投稿の横に「投稿者名」を表示する場合。
- ❌ 使わなくても良いケース: ある投稿の詳細ページで、その投稿一つの作者名を表示する場合。この場合は、クエリは2回しか発生しないため、
with()
によるメリットが薄く、逆にコードが複雑になるだけです。
パフォーマンスを計測するためのツールとして、Laravel開発環境ではLaravel Debugbarを使うと、ページが表示されるまでに実行されたSQLのクエリ回数を視覚的に確認できます。クエリが何度も実行されている(N+1問題が発生している)と判明したら、すぐにwith()
を使いましょう。
まとめ:これであなたもリレーションマスターの入り口に! 🎉
お疲れ様でした!この記事を通して、あなたは「1対多」リレーションの物理的な仕組み(外部キー)と、Laravelでの論理的な実装(hasMany
/ belongsTo
)を完璧にマスターしました。さらに、実運用で必須となるN+1問題の解決策(Eager Loading)まで身につけました。
この知識は、あなたがLaravelで構築するあらゆるアプリケーションの土台となります。基本的なCRUD操作だけでなく、データ間の複雑な関連性をコードでシンプルに表現できるようになったことは、大きな進歩です!
💡 本日の重要ポイントの振り返り
一度に多くの概念を学んだので、特に重要なポイントをもう一度確認しましょう。この4点を体で覚えることが、Laravel開発のスピードを飛躍的に高めます。
- 紐付けのキー: 「1対多」リレーションは、親テーブルのIDを子テーブルの外部キー(
user_id
)で参照することで成立します。マイグレーションのconstrained()
がこれを保証してくれます。 - 親の定義: 親モデル(
User
)には、hasMany(Post::class)
を定義し、「私は複数の子(投稿)を持っているよ」とLaravelに伝えます。 - 子の定義: 子モデル(
Post
)には、belongsTo(User::class)
を定義し、「私はこの親(ユーザー)に属しているよ」とLaravelに伝えます。 - アクセス方法: データアクセス時は、
$post->user
のようにメソッドではなくプロパティとして呼び出し、Eloquentマジックの恩恵を受けます。 - パフォーマンス対策: 大量のデータを扱う際は、必ず
with('リレーション名')
を使ってEager Loadingを行い、データベースへのアクセス回数を最小限に抑え、N+1問題を回避します。
次のステップへ!多対多リレーションに挑戦しよう 💪
この「1対多」の知識があれば、次はより複雑で実用的なリレーションに挑戦する準備ができています。次に若手エンジニアがつまずきやすい、しかしマスターするとアプリの機能が劇的に向上するリレーションは「多対多(Many-to-Many)」です。
例えば、「一つの投稿に複数のタグを付けることができ、一つのタグが複数の投稿に付けられる」という機能です。
現在の関係 | 次のステップ |
---|---|
ユーザー ↔ 投稿 (1対多) | 投稿 ↔ タグ (多対多) |
多対多リレーションでは、データを結びつけるために中間テーブルという新しい概念が登場しますが、基本的な考え方は今回の「1対多」の延長線上にあります。リレーションを制覇することは、Laravelを制覇することに他なりません。
ぜひ、あなたが作ったアプリにこのユーザー・投稿リレーションを実装し、その快適なコーディング体験を実感してみてください!実際に動くコードに触れることが、何よりも早い習得法です。
実践チャレンジ!
掲示板の「いいね!」機能を想像してみてください。一つの投稿に複数のユーザーが「いいね!」を押すことができ、一人のユーザーが複数の投稿に「いいね!」を押せます。
これは、ユーザーと投稿の間の「多対多」の関係です。このチャレンジに挑戦すれば、あなたのリレーションスキルはもう中級者レベルです!
![]() | Laravelの教科書 バージョン10対応 [ 加藤 じゅんこ ] 価格:3300円 |

![]() | PHPフレームワークLaravel入門第2版 [ 掌田津耶乃 ] 価格:3300円 |
