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