#06 Laravel基礎

Eloquentリレーション — hasOne / hasMany / belongsTo

リレーションとは

データベースの複数のテーブルを関連づける仕組みを リレーション? テーブルとテーブルの関係のこと。「1人のユーザーは複数の投稿を持つ」のような関係をコードで表現できる。 と呼びます。例えば:

  • 1人のユーザーは複数の投稿を持つ(1対多)
  • 1つの投稿は1つのサムネイルを持つ(1対1)
  • 1つの投稿は1人のユーザーに属する(多対1)

Eloquentを使うと、これらの関係をシンプルなメソッドで表現できます。


マイグレーションで外部キーを作る

まず前提として、リレーションにはデータベースの 外部キー? 別のテーブルの主キーを参照するカラム。テーブル間の関係を表す。例:投稿テーブルの`user_id`でユーザーテーブルを参照。 が必要です。

<?php
// 📁 database/migrations/xxxx_create_posts_table.php

Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')   // ← 外部キーカラムを追加
          ->constrained()          // ← users.id を参照すると自動認識
          ->cascadeOnDelete();     // ← ユーザーが削除されたら投稿も削除
    $table->string('title');
    $table->text('body');
    $table->timestamps();
});

foreignId('user_id')->constrained()users テーブルの id を参照する外部キーを作ります。constrained() に引数を渡せば別テーブルも指定できます(例:constrained('admins'))。


1対多:hasMany

「1人のユーザーは複数の投稿を持つ」関係です。

<?php
// 📁 app/Models/User.php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    public function posts()
    {
        return $this->hasMany(Post::class);
        // デフォルトで posts.user_id = users.id として検索する
    }
}

使い方:

// あるユーザーのすべての投稿を取得
$user = User::find(1);
$posts = $user->posts; // → Post のコレクション

多対1:belongsTo

「投稿はユーザーに属する」という逆方向の関係です。

<?php
// 📁 app/Models/Post.php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
        // posts.user_id を使って users.id を検索する
    }
}

使い方:

// ある投稿の作者を取得
$post = Post::find(1);
$author = $post->user; // → User モデル

1対1:hasOne

「ユーザーは1つのプロフィールを持つ」関係です。

// 📁 app/Models/User.php

public function profile()
{
    return $this->hasOne(Profile::class);
    // profiles.user_id = users.id として検索する
}
// 📁 app/Models/Profile.php

public function user()
{
    return $this->belongsTo(User::class);
}

使い方:

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

リレーションを使ったクエリ

リレーションはそのままプロパティとしてアクセスできますが、メソッドとして呼び出すとクエリビルダーとして使えます。

// プロパティアクセス(結果のコレクションを返す)
$posts = $user->posts;

// メソッド呼び出し(クエリをさらに絞り込める)
$publishedPosts = $user->posts()->where('status', 'published')->latest()->get();

whereHas でリレーションを条件にする

// 公開済み投稿を持つユーザーだけを取得
$activeUsers = User::whereHas('posts', function ($query) {
    $query->where('status', 'published');
})->get();

リレーション先のデータを保存する

// ユーザーに紐づいた投稿を作成
$user = User::find(1);

// create() を使う方法(user_id が自動でセットされる)
$post = $user->posts()->create([
    'title' => '新しい投稿',
    'body'  => '本文です',
]);

まとめ

  • hasMany() → 1対多の「持つ側」(User has many Posts)
  • belongsTo() → 多対1の「属する側」(Post belongs to User)
  • hasOne() → 1対1の「持つ側」(User has one Profile)
  • マイグレーションで foreignId()->constrained() を使うと外部キーを簡単に定義できる
  • $user->posts() とメソッド呼び出しにするとクエリを追加できる

次回はEagerロードとN+1問題の解決を学びます。