Laravel Eloquent

Laravel EloquentはLaravelフレームワークのActive Record実装ORMです。エレガントで流暢なAPIによりデータベース操作を直感的に記述可能で、リレーションシップ、イベント、アクセサ・ミューテータ等の豊富な機能を提供します。

ORMPHPLaravelActive RecordWeb開発

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フレームワークのActive Record実装ORMです。エレガントで流暢なAPIによりデータベース操作を直感的に記述可能で、リレーションシップ、イベント、アクセサ・ミューテータ等の豊富な機能を提供します。

詳細

Eloquent ORMは、データベース操作を美しく表現力豊かなPHPコードで記述できるように設計されています。Active Recordパターンを採用し、各データベーステーブルに対応するモデルクラスを通じてCRUD操作を実行します。Laravelエコシステムとの深い統合により、マイグレーション、ファクトリー、シーダーなどの関連機能と連携し、効率的なWeb開発を支援します。

主な特徴

  • 流暢なAPI: 直感的で読みやすいメソッドチェーン
  • リレーションシップ: 豊富なリレーション定義機能
  • Eloquentコレクション: 強力なデータ操作メソッド
  • イベント: モデルライフサイクルでのフック機能
  • キャスト: 自動的な型変換機能

メリット・デメリット

メリット

  • PHP開発者にとって学習しやすい直感的なAPI
  • Laravel エコシステムとの完全な統合
  • 豊富なリレーションシップ機能により複雑なデータ構造も簡潔に表現
  • アクセサ・ミューテータによる柔軟なデータ変換
  • 中小規模プロジェクトでの高い開発生産性

デメリット

  • 大規模システムでのパフォーマンス課題
  • Active Recordパターンによるビジネスロジックとデータアクセスの密結合
  • 複雑なクエリの表現に制限がある場合がある
  • Laravel フレームワーク依存のため他のフレームワークでは使用困難

参考ページ

書き方の例

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

# Laravel プロジェクト作成
composer create-project laravel/laravel myproject
cd myproject

# データベース設定
php artisan make:migration create_users_table
php artisan make:migration create_posts_table
// .env ファイル
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_app
DB_USERNAME=root
DB_PASSWORD=

// config/database.php(必要に応じて調整)
'mysql' => [
    'driver' => 'mysql',
    'url' => env('DATABASE_URL'),
    'host' => env('DB_HOST', '127.0.0.1'),
    'port' => env('DB_PORT', '3306'),
    'database' => env('DB_DATABASE', 'forge'),
    'username' => env('DB_USERNAME', 'forge'),
    'password' => env('DB_PASSWORD', ''),
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
],

基本的なCRUD操作(モデル定義、作成、読み取り、更新、削除)

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

namespace App\Models;

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

class User extends Model
{
    use HasFactory;

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

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

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

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

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

namespace App\Models;

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

class Post extends Model
{
    use HasFactory;

    protected $fillable = [
        'title',
        'content',
        'user_id',
        'published_at',
    ];

    protected $casts = [
        'published_at' => 'datetime',
    ];

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

// マイグレーション実行
// php artisan migrate

// 基本的なCRUD操作
use App\Models\User;
use App\Models\Post;

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

// 読み取り
$allUsers = User::all();
$userById = User::find(1);
$userByEmail = User::where('email', '[email protected]')->first();

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

// 一括更新
User::where('name', 'like', '%田中%')->update(['status' => 'active']);

// 削除
$user = User::find(1);
$user->delete();

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

高度なクエリとリレーションシップ

// 複雑な条件クエリ
$activeUsers = User::where('status', 'active')
    ->where('created_at', '>=', now()->subDays(30))
    ->where('email', 'like', '%@company.com')
    ->orderBy('created_at', 'desc')
    ->limit(10)
    ->get();

// リレーションシップクエリ
$usersWithPosts = User::with('posts')->get();

$usersWithRecentPosts = User::with(['posts' => function ($query) {
    $query->where('created_at', '>=', now()->subDays(7))
          ->orderBy('created_at', 'desc');
}])->get();

// 条件付きリレーション読み込み
$users = User::when(request('include_posts'), function ($query) {
    return $query->with('posts');
})->get();

// 集約クエリ
$userStats = User::selectRaw('
        COUNT(*) as total_users,
        AVG(posts_count) as avg_posts_per_user,
        MAX(created_at) as latest_user_date
    ')
    ->withCount('posts')
    ->first();

// サブクエリ
$activeUsers = User::whereHas('posts', function ($query) {
    $query->where('published_at', '>=', now()->subDays(30));
})->get();

// 結合クエリ
$postsWithAuthors = Post::join('users', 'posts.user_id', '=', 'users.id')
    ->select('posts.*', 'users.name as author_name')
    ->where('posts.published_at', '>=', now()->subDays(7))
    ->get();

// スコープクエリ
class User extends Model
{
    public function scopeActive($query)
    {
        return $query->where('status', 'active');
    }
    
    public function scopeWithRecentPosts($query, $days = 30)
    {
        return $query->whereHas('posts', function ($q) use ($days) {
            $q->where('created_at', '>=', now()->subDays($days));
        });
    }
}

// スコープ使用例
$activeUsersWithRecentPosts = User::active()
    ->withRecentPosts(7)
    ->get();

マイグレーションとスキーマ管理

# マイグレーション作成
php artisan make:migration create_users_table
php artisan make:migration add_status_to_users_table

# マイグレーション実行
php artisan migrate

# マイグレーション巻き戻し
php artisan migrate:rollback
php artisan migrate:rollback --step=3

# マイグレーション状態確認
php artisan migrate:status
// database/migrations/xxxx_create_users_table.php
<?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->enum('status', ['active', 'inactive'])->default('active');
            $table->rememberToken();
            $table->timestamps();
            
            $table->index(['status', 'created_at']);
            $table->index('email');
        });
    }

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

// database/migrations/xxxx_create_posts_table.php
return new class extends Migration
{
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('content');
            $table->foreignId('user_id')->constrained()->onDelete('cascade');
            $table->timestamp('published_at')->nullable();
            $table->timestamps();
            
            $table->index(['user_id', 'published_at']);
            $table->index('published_at');
        });
    }

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

パフォーマンス最適化と高度な機能

// トランザクション
use Illuminate\Support\Facades\DB;

DB::transaction(function () {
    $user = User::create([
        'name' => '山田花子',
        'email' => '[email protected]',
        'password' => bcrypt('password123'),
    ]);
    
    Post::create([
        'title' => '最初の投稿',
        'content' => 'Laravel Eloquentを使った投稿です',
        'user_id' => $user->id,
        'published_at' => now(),
    ]);
});

// バッチ操作
$userData = [
    ['name' => 'ユーザー1', 'email' => '[email protected]', 'password' => bcrypt('password')],
    ['name' => 'ユーザー2', 'email' => '[email protected]', 'password' => bcrypt('password')],
    ['name' => 'ユーザー3', 'email' => '[email protected]', 'password' => bcrypt('password')],
];

User::insert($userData);

// バッチ更新(Laravel 8+)
User::whereIn('id', [1, 2, 3])->update(['status' => 'verified']);

// チャンク処理(大量データ)
User::chunk(100, function ($users) {
    foreach ($users as $user) {
        // 処理ロジック
        $user->process();
    }
});

// イーガーローディングでN+1問題を回避
$posts = Post::with('user')->get();

// 遅延イーガーローディング
$posts = Post::all();
$posts->load('user');

// カスタムキャスト
class User extends Model
{
    protected $casts = [
        'preferences' => 'array',
        'last_login_at' => 'datetime',
        'is_admin' => 'boolean',
    ];
}

// アクセサとミューテータ
class User extends Model
{
    // アクセサ
    public function getFullNameAttribute(): string
    {
        return $this->first_name . ' ' . $this->last_name;
    }
    
    // ミューテータ
    public function setPasswordAttribute($value): void
    {
        $this->attributes['password'] = bcrypt($value);
    }
}

// モデルイベント
class User extends Model
{
    protected static function boot()
    {
        parent::boot();
        
        static::creating(function ($user) {
            $user->uuid = Str::uuid();
        });
        
        static::created(function ($user) {
            // ユーザー作成後の処理
            Mail::to($user)->send(new WelcomeEmail());
        });
    }
}

// クエリビルダー最適化
$users = User::select(['id', 'name', 'email'])
    ->where('status', 'active')
    ->orderBy('created_at', 'desc')
    ->paginate(15);

フレームワーク統合と実用例

// API リソースコントローラー
<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;

class UserController extends Controller
{
    public function index(): JsonResponse
    {
        $users = User::with('posts')
            ->paginate(15);
            
        return response()->json($users);
    }
    
    public function show(User $user): JsonResponse
    {
        $user->load('posts');
        return response()->json($user);
    }
    
    public function store(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users',
            'password' => 'required|string|min:8',
        ]);
        
        $user = User::create($validated);
        
        return response()->json($user, 201);
    }
    
    public function update(Request $request, User $user): JsonResponse
    {
        $validated = $request->validate([
            'name' => 'sometimes|string|max:255',
            'email' => 'sometimes|email|unique:users,email,' . $user->id,
        ]);
        
        $user->update($validated);
        
        return response()->json($user);
    }
    
    public function destroy(User $user): JsonResponse
    {
        $user->delete();
        
        return response()->json(['message' => 'User deleted successfully']);
    }
}

// ジョブキューとの統合
<?php

namespace App\Jobs;

use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class ProcessUserData implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(
        private User $user
    ) {}

    public function handle(): void
    {
        // 重い処理をバックグラウンドで実行
        $this->user->update([
            'processed_at' => now(),
            'status' => 'processed',
        ]);
    }
}

// ジョブディスパッチ
ProcessUserData::dispatch($user);

// ファクトリーとシーダー
// database/factories/UserFactory.php
<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;

class UserFactory extends Factory
{
    public function definition(): array
    {
        return [
            'name' => fake()->name(),
            'email' => fake()->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => bcrypt('password'),
            'remember_token' => Str::random(10),
        ];
    }
}

// database/seeders/UserSeeder.php
<?php

namespace Database\Seeders;

use App\Models\User;
use Illuminate\Database\Seeder;

class UserSeeder extends Seeder
{
    public function run(): void
    {
        User::factory()
            ->count(50)
            ->hasPosts(3)
            ->create();
    }
}

// Bladeテンプレートでの使用
{{-- resources/views/users/index.blade.php --}}
@extends('layouts.app')

@section('content')
<div class="container">
    <h1>ユーザー一覧</h1>
    
    @foreach ($users as $user)
        <div class="card mb-3">
            <div class="card-body">
                <h5 class="card-title">{{ $user->name }}</h5>
                <p class="card-text">{{ $user->email }}</p>
                <p class="card-text">
                    <small class="text-muted">
                        投稿数: {{ $user->posts_count }}
                    </small>
                </p>
            </div>
        </div>
    @endforeach
    
    {{ $users->links() }}
</div>
@endsection

// コントローラー
<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;

class UserController extends Controller
{
    public function index()
    {
        $users = User::withCount('posts')
            ->paginate(10);
            
        return view('users.index', compact('users'));
    }
}