Laravel Eloquent

Laravel EloquentはLaravelフレームワークの中核をなすActiveRecord ORMです。「設定より規約」アプローチにより、データベース操作を直感的で流暢なインターフェースで提供し、優れた開発者体験を実現します。モデルとデータベーステーブルの関係を規約ベースで自動推論し、複雑なSQLクエリを簡潔で読みやすいPHPコードで表現可能。マイグレーション、リレーションシップ、イベント、アクセサ/ミューテータなど、Web開発に必要な包括的機能を内蔵し、Laravelエコシステムとの深い統合により開発効率を最大化します。

ORMPHPActive RecordLaravelフレームワーク開発生産性

GitHub概要

laravel/framework

The Laravel Framework.

スター33,844
ウォッチ958
フォーク11,444
作成日:2013年1月10日
言語:PHP
ライセンス:MIT License

トピックス

frameworklaravelphp

スター履歴

laravel/framework Star History
データ取得日時: 2025/7/19 09:31

ライブラリ

Laravel Eloquent

概要

Laravel EloquentはLaravelフレームワークの中核をなすActiveRecord ORMです。「設定より規約」アプローチにより、データベース操作を直感的で流暢なインターフェースで提供し、優れた開発者体験を実現します。モデルとデータベーステーブルの関係を規約ベースで自動推論し、複雑なSQLクエリを簡潔で読みやすいPHPコードで表現可能。マイグレーション、リレーションシップ、イベント、アクセサ/ミューテータなど、Web開発に必要な包括的機能を内蔵し、Laravelエコシステムとの深い統合により開発効率を最大化します。

詳細

Laravel Eloquent 2025年版はPHP Webアプリケーション開発における最も親しみやすいORM選択肢として確固たる地位を築いています。Active Recordパターンの実装により、各モデルクラスがデータベーステーブルと1:1で対応し、オブジェクト指向的なデータ操作を可能にします。強力なクエリビルダー、自動的なタイムスタンプ管理、ソフトデリート、Mass Assignment対策、Eloquentコレクション、リレーションシップ(一対一、一対多、多対多、ポリモーフィック)など、実用的な機能を豊富に提供。Laravel Livewire、Laravel Nova、Laravel Breezeといった公式パッケージとの統合により、フルスタック開発を効率化します。

主な特徴

  • 直感的なActive Record実装: モデルクラス中心のシンプルなデータ操作
  • 強力なリレーションシップ: 一対一、一対多、多対多、ポリモーフィック関係対応
  • 流暢なクエリビルダー: SQLを意識しない直感的なクエリ記述
  • 自動マイグレーション: スキーマ変更の体系的管理
  • Eloquentコレクション: 配列ライクな高機能データ操作
  • イベント統合: モデル操作時の自動フック処理

メリット・デメリット

メリット

  • PHP開発者に最も親しみやすい設計とLaravelエコシステム完全統合
  • 規約ベースの設計により設定ファイルが最小限で学習コストが低い
  • 強力なリレーションシップ機能とEager Loading によるN+1問題回避
  • マイグレーション、シーダー、ファクトリーによる開発・テスト効率化
  • Laravel製ツール(Tinker、Artisan、Telescope)との相乗効果
  • 豊富なコミュニティリソースとLaravel 11での継続的進化

デメリット

  • Laravel フレームワーク以外での利用は基本的に不可能
  • 大量データ処理時はActive Recordパターンの性能制約が顕在化
  • 複雑なSQLクエリは生SQLまたはクエリビルダー併用が必要
  • データベース抽象化が強く、SQLの詳細制御が困難な場面有り
  • マジックメソッドの多用によりIDEの補完機能が制限される
  • テーブル命名規約に従わない既存システムとの統合困難

参考ページ

書き方の例

インストールと基本セットアップ

# Laravel プロジェクト作成(Eloquent含む)
composer create-project laravel/laravel my-app
cd my-app

# データベース設定(.env ファイル)
cp .env.example .env
php artisan key:generate

# データベース接続設定
# .env ファイルを編集
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=my_database
DB_USERNAME=root
DB_PASSWORD=

# データベース接続確認
php artisan tinker
>>> DB::connection()->getPdo();

モデル作成とマイグレーション

# モデルとマイグレーション同時作成
php artisan make:model User --migration
php artisan make:model Post -m
php artisan make:model Comment -mc  # モデル、マイグレーション、コントローラー

# ファクトリーとシーダーも同時作成
php artisan make:model Product -mfs
<?php
// app/Models/User.php - ユーザーモデル
namespace App\Models;

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

class User extends Model
{
    use SoftDeletes;

    protected $fillable = [
        'name',
        'email',
        'email_verified_at',
        'password',
    ];

    protected $hidden = [
        'password',
        'remember_token',
    ];

    protected $casts = [
        'email_verified_at' => 'datetime',
        'password' => 'hashed',
    ];

    // 一対多リレーションシップ
    public function posts(): HasMany
    {
        return $this->hasMany(Post::class);
    }

    // アクセサ例
    public function getFullNameAttribute(): string
    {
        return $this->first_name . ' ' . $this->last_name;
    }
}

// database/migrations/xxxx_create_users_table.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();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
            $table->softDeletes();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('users');
    }
};

基本的なCRUD操作

<?php
use App\Models\User;
use App\Models\Post;

// Create - データ作成
$user = User::create([
    'name' => '田中太郎',
    'email' => '[email protected]',
    'password' => bcrypt('password123')
]);

// または new + save
$user = new User();
$user->name = '田中太郎';
$user->email = '[email protected]';
$user->password = bcrypt('password123');
$user->save();

// Read - データ取得
$users = User::all(); // 全ユーザー取得
$user = User::find(1); // IDで取得
$user = User::where('email', '[email protected]')->first();

// 複数条件での検索
$users = User::where('created_at', '>=', now()->subDays(30))
    ->where('email_verified_at', '!=', null)
    ->orderBy('created_at', 'desc')
    ->limit(10)
    ->get();

// Update - データ更新
$user = User::find(1);
$user->name = '田中次郎';
$user->save();

// または一括更新
User::where('id', 1)->update(['name' => '田中次郎']);

// Delete - データ削除
$user = User::find(1);
$user->delete(); // ソフトデリート(deleted_at設定)

// 物理削除
$user->forceDelete();

// 一括削除
User::where('email_verified_at', null)->delete();

リレーションシップとEager Loading

<?php
// app/Models/Post.php
namespace App\Models;

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

class Post extends Model
{
    protected $fillable = ['title', 'content', 'user_id'];

    // 多対一リレーション
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    // 一対多リレーション
    public function comments(): HasMany
    {
        return $this->hasMany(Comment::class);
    }
}

// app/Models/Comment.php
class Comment extends Model
{
    protected $fillable = ['content', 'post_id', 'user_id'];

    public function post(): BelongsTo
    {
        return $this->belongsTo(Post::class);
    }

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

// リレーションシップ操作例
$user = User::find(1);

// 関連データの取得
$posts = $user->posts; // ユーザーの投稿一覧
$postCount = $user->posts()->count(); // 投稿数

// 関連データの作成
$post = $user->posts()->create([
    'title' => '新しい投稿',
    'content' => '投稿内容'
]);

// Eager Loading(N+1問題回避)
$posts = Post::with(['user', 'comments.user'])->get();

foreach ($posts as $post) {
    echo $post->user->name; // 追加のクエリなし
    foreach ($post->comments as $comment) {
        echo $comment->user->name; // 追加のクエリなし
    }
}

// 条件付きEager Loading
$posts = Post::with(['comments' => function ($query) {
    $query->where('approved', true)->orderBy('created_at', 'desc');
}])->get();

高度なクエリとスコープ

<?php
// app/Models/Post.php でローカルスコープ定義
class Post extends Model
{
    // ローカルスコープ
    public function scopePublished($query)
    {
        return $query->where('status', 'published');
    }

    public function scopeByUser($query, $userId)
    {
        return $query->where('user_id', $userId);
    }

    public function scopeRecent($query, $days = 7)
    {
        return $query->where('created_at', '>=', now()->subDays($days));
    }
}

// スコープの使用
$posts = Post::published()->recent(30)->get();
$userPosts = Post::byUser(1)->published()->orderBy('created_at', 'desc')->get();

// 複雑なクエリビルダー
$posts = Post::select('posts.*', 'users.name as author_name')
    ->join('users', 'posts.user_id', '=', 'users.id')
    ->where('posts.created_at', '>=', now()->subMonth())
    ->whereHas('comments', function ($query) {
        $query->where('approved', true);
    })
    ->withCount(['comments', 'likes'])
    ->orderBy('comments_count', 'desc')
    ->paginate(15);

// サブクエリ
$latestPosts = Post::select('user_id', DB::raw('MAX(created_at) as latest_post'))
    ->groupBy('user_id');

$users = User::joinSub($latestPosts, 'latest_posts', function ($join) {
    $join->on('users.id', '=', 'latest_posts.user_id');
})->get();

// 条件分岐クエリ
$query = Post::query();

if ($request->has('category')) {
    $query->where('category_id', $request->category);
}

if ($request->has('search')) {
    $query->where(function ($q) use ($request) {
        $q->where('title', 'like', '%' . $request->search . '%')
          ->orWhere('content', 'like', '%' . $request->search . '%');
    });
}

$posts = $query->paginate(20);

イベントとオブザーバー

<?php
// app/Models/Post.php - モデルイベント
class Post extends Model
{
    protected static function boot()
    {
        parent::boot();

        // 作成時イベント
        static::creating(function ($post) {
            if (auth()->check()) {
                $post->user_id = auth()->id();
            }
        });

        // 更新時イベント
        static::updating(function ($post) {
            $post->updated_by = auth()->id();
        });

        // 削除時イベント
        static::deleting(function ($post) {
            // 関連コメントも削除
            $post->comments()->delete();
        });
    }
}

// app/Observers/PostObserver.php - オブザーバーパターン
class PostObserver
{
    public function created(Post $post): void
    {
        // 投稿作成通知
        Notification::send($post->user, new PostCreatedNotification($post));
    }

    public function updated(Post $post): void
    {
        // キャッシュクリア
        Cache::forget("post.{$post->id}");
    }

    public function deleted(Post $post): void
    {
        // 画像ファイル削除
        Storage::delete($post->image_path);
    }
}

// app/Providers/AppServiceProvider.php でオブザーバー登録
public function boot(): void
{
    Post::observe(PostObserver::class);
}

多対多リレーションシップとピボットテーブル

<?php
// app/Models/User.php
class User extends Model
{
    // 多対多リレーション(ユーザー ⟷ ロール)
    public function roles(): BelongsToMany
    {
        return $this->belongsToMany(Role::class)
            ->withTimestamps()
            ->withPivot(['assigned_at', 'assigned_by']);
    }

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

// app/Models/Role.php
class Role extends Model
{
    public function users(): BelongsToMany
    {
        return $this->belongsToMany(User::class);
    }

    public function permissions(): BelongsToMany
    {
        return $this->belongsToMany(Permission::class);
    }
}

// 多対多関係の操作
$user = User::find(1);

// ロール追加
$user->roles()->attach(1); // role_id = 1 を追加
$user->roles()->attach([1, 2, 3]); // 複数追加
$user->roles()->attach(1, ['assigned_at' => now()]); // ピボットデータ付き

// ロール削除
$user->roles()->detach(1); // role_id = 1 を削除
$user->roles()->detach([1, 2]); // 複数削除
$user->roles()->detach(); // すべて削除

// 同期(指定したIDのみ残す)
$user->roles()->sync([1, 2, 3]);

// 条件チェック
if ($user->roles()->where('name', 'admin')->exists()) {
    // 管理者権限あり
}

// ピボットデータ取得
$userRoles = $user->roles()->withPivot(['assigned_at', 'assigned_by'])->get();
foreach ($userRoles as $role) {
    echo $role->pivot->assigned_at;
    echo $role->pivot->assigned_by;
}

ファクトリーとシーダー(テスト・開発データ)

<?php
// database/factories/UserFactory.php
namespace Database\Factories;

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;

class UserFactory extends Factory
{
    protected $model = User::class;

    public function definition(): array
    {
        return [
            'name' => $this->faker->name(),
            'email' => $this->faker->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => bcrypt('password'), // テスト用固定パスワード
            'remember_token' => Str::random(10),
        ];
    }

    // 状態バリエーション
    public function unverified(): static
    {
        return $this->state(fn (array $attributes) => [
            'email_verified_at' => null,
        ]);
    }

    public function admin(): static
    {
        return $this->state(fn (array $attributes) => [
            'name' => 'Administrator',
            'email' => '[email protected]',
        ]);
    }
}

// database/factories/PostFactory.php
class PostFactory extends Factory
{
    public function definition(): array
    {
        return [
            'title' => $this->faker->sentence(),
            'content' => $this->faker->paragraphs(3, true),
            'status' => $this->faker->randomElement(['draft', 'published']),
            'user_id' => User::factory(),
        ];
    }
}

// database/seeders/DatabaseSeeder.php
class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        // 管理者ユーザー作成
        $admin = User::factory()->admin()->create();

        // 一般ユーザー50人作成、それぞれ1〜5個の投稿
        User::factory(50)
            ->has(Post::factory()->count(rand(1, 5)))
            ->create();

        // 特定のリレーション構造
        User::factory()
            ->has(
                Post::factory()
                    ->has(Comment::factory()->count(3))
                    ->count(5)
            )
            ->create();
    }
}

// テストでの使用
$users = User::factory(10)->create();
$post = Post::factory()->for($admin)->create();