Illuminate Database (Laravel Eloquent)
Illuminate Database, developed as part of the Laravel framework, is "an elegant and expressive Active Record implementation" and the most popular ORM library in PHP. Adopting the simple Active Record pattern, it provides an intuitive mapping between database tables and model classes. Combined with a powerful query builder, it enables complex database operations without writing SQL queries. It comprehensively provides features necessary for modern web development including relationships, migrations, seeding, and soft deletes, significantly improving developer productivity.
GitHub Overview
laravel/framework
The Laravel Framework.
Topics
Star History
Library
Illuminate Database (Laravel Eloquent)
Overview
Illuminate Database, developed as part of the Laravel framework, is "an elegant and expressive Active Record implementation" and the most popular ORM library in PHP. Adopting the simple Active Record pattern, it provides an intuitive mapping between database tables and model classes. Combined with a powerful query builder, it enables complex database operations without writing SQL queries. It comprehensively provides features necessary for modern web development including relationships, migrations, seeding, and soft deletes, significantly improving developer productivity.
Details
Illuminate Database 2025 edition fully supports PHP 8.2+ and provides an ORM experience that maximizes modern PHP features such as type hints, enums, and attributes. Eloquent's beautiful syntax allows intuitive definition of complex relationships like one-to-one, one-to-many, and many-to-many, while proper use of lazy loading and eager loading helps avoid N+1 problems. The query builder's fluent interface enables SQL-like yet PHP-friendly descriptions, and database abstraction allows the same code to work with multiple database engines (MySQL, PostgreSQL, SQLite, SQL Server).
Key Features
- Active Record Pattern: Intuitive model-centric data manipulation
- Relationships: Natural expression of one-to-one, one-to-many, many-to-many relationships
- Query Builder: Fluent and expressive query construction
- Migrations: Database schema version control
- Eager Loading: Efficient solution to N+1 problems
- Soft Deletes: Data protection through logical deletion
Pros and Cons
Pros
- Intuitive and readable code writing, excellent for team development
- Perfect integration with Laravel ecosystem and convention-over-configuration
- Rich relationship support with automated processing
- Powerful migration and seeding features for team collaboration
- Advanced features like soft deletes, scopes, and accessors
- Comprehensive documentation and large community
Cons
- Laravel framework dependency makes standalone use difficult
- Active Record pattern constraints cause performance issues in large systems
- Complex queries may require raw SQL
- Error messages can be unclear, making debugging difficult
- High memory usage requires optimization in high-load environments
- Magic methods make IDE type inference difficult
Reference Pages
Code Examples
Setup
# Create Laravel project
composer create-project laravel/laravel my-project
cd my-project
# Or standalone usage
composer require illuminate/database illuminate/events
<?php
// config/database.php or direct configuration
use Illuminate\Database\Capsule\Manager as Capsule;
$capsule = new Capsule;
$capsule->addConnection([
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'my_database',
'username' => 'username',
'password' => 'password',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
]);
// Set event dispatcher (required for Eloquent)
$capsule->setEventDispatcher(new \Illuminate\Events\Dispatcher);
// Make Eloquent globally available
$capsule->setAsGlobal();
$capsule->bootEloquent();
Basic Usage
<?php
// Migration example (database/migrations/create_users_table.php)
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUsersTable extends Migration
{
public function up()
{
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->date('birth_date')->nullable();
$table->enum('status', ['active', 'inactive', 'suspended'])->default('active');
$table->json('preferences')->nullable();
$table->rememberToken();
$table->timestamps();
$table->softDeletes();
$table->index(['status', 'created_at']);
});
}
public function down()
{
Schema::dropIfExists('users');
}
}
// Eloquent model (app/Models/User.php)
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class User extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'name',
'email',
'password',
'birth_date',
'status',
'preferences',
];
protected $hidden = [
'password',
'remember_token',
];
protected $casts = [
'email_verified_at' => 'datetime',
'birth_date' => 'date',
'preferences' => 'array',
'status' => UserStatus::class, // Enum cast
];
// Accessor (transformation when getting attributes)
public function getNameAttribute($value)
{
return ucfirst($value);
}
// Mutator (transformation when setting attributes)
public function setPasswordAttribute($value)
{
$this->attributes['password'] = bcrypt($value);
}
// Virtual attribute
public function getAgeAttribute()
{
return $this->birth_date?->diffInYears(now());
}
// Scope (reusable query filters)
public function scopeActive($query)
{
return $query->where('status', UserStatus::Active);
}
public function scopeAdults($query)
{
return $query->whereRaw('DATEDIFF(CURDATE(), birth_date) >= ?', [365 * 18]);
}
public function scopeSearch($query, $term)
{
return $query->where('name', 'like', "%{$term}%")
->orWhere('email', 'like', "%{$term}%");
}
}
// Enum class (PHP 8.1+)
enum UserStatus: string
{
case Active = 'active';
case Inactive = 'inactive';
case Suspended = 'suspended';
public function label(): string
{
return match($this) {
self::Active => 'Active',
self::Inactive => 'Inactive',
self::Suspended => 'Suspended',
};
}
}
Data Operations
<?php
// Basic CRUD operations
class UserService
{
// Create
public function createUser(array $userData): User
{
return User::create([
'name' => $userData['name'],
'email' => $userData['email'],
'password' => $userData['password'],
'birth_date' => $userData['birth_date'] ?? null,
'preferences' => $userData['preferences'] ?? [],
]);
}
// Read
public function findUser(int $id): ?User
{
return User::find($id);
}
public function findUserByEmail(string $email): ?User
{
return User::where('email', $email)->first();
}
// Update
public function updateUser(int $id, array $updates): bool
{
$user = User::find($id);
if (!$user) {
return false;
}
return $user->update($updates);
}
// Delete (soft delete)
public function deleteUser(int $id): bool
{
$user = User::find($id);
if (!$user) {
return false;
}
return $user->delete();
}
// Force delete
public function forceDeleteUser(int $id): bool
{
$user = User::withTrashed()->find($id);
if (!$user) {
return false;
}
return $user->forceDelete();
}
// Restore
public function restoreUser(int $id): bool
{
$user = User::withTrashed()->find($id);
if (!$user) {
return false;
}
return $user->restore();
}
}
// Complex searches using query builder
class UserRepository
{
public function getActiveUsers(int $limit = 10): Collection
{
return User::active()
->orderBy('created_at', 'desc')
->limit($limit)
->get();
}
public function searchUsers(string $term, array $filters = []): Collection
{
$query = User::search($term);
if (isset($filters['status'])) {
$query->where('status', $filters['status']);
}
if (isset($filters['age_min'])) {
$query->whereRaw('DATEDIFF(CURDATE(), birth_date) >= ?', [365 * $filters['age_min']]);
}
if (isset($filters['age_max'])) {
$query->whereRaw('DATEDIFF(CURDATE(), birth_date) <= ?', [365 * $filters['age_max']]);
}
if (isset($filters['created_after'])) {
$query->where('created_at', '>=', $filters['created_after']);
}
return $query->orderBy('name')
->paginate($filters['per_page'] ?? 15);
}
public function getUserStats(): array
{
return [
'total' => User::count(),
'active' => User::active()->count(),
'new_this_month' => User::where('created_at', '>=', now()->startOfMonth())->count(),
'adults' => User::adults()->count(),
'by_status' => User::selectRaw('status, COUNT(*) as count')
->groupBy('status')
->pluck('count', 'status')
->toArray(),
];
}
public function getBulkUsers(array $ids): Collection
{
return User::whereIn('id', $ids)->get();
}
public function updateBulkUsers(array $ids, array $updates): int
{
return User::whereIn('id', $ids)->update($updates);
}
}
Relationships
<?php
// Post model
class Post extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'title',
'content',
'status',
'user_id',
'category_id',
'published_at',
];
protected $casts = [
'published_at' => 'datetime',
'status' => PostStatus::class,
];
// Many-to-one: Post belongs to one user
public function user()
{
return $this->belongsTo(User::class);
}
// Many-to-one: Post belongs to one category
public function category()
{
return $this->belongsTo(Category::class);
}
// One-to-many: Post has many comments
public function comments()
{
return $this->hasMany(Comment::class);
}
// Many-to-many: Post has many tags
public function tags()
{
return $this->belongsToMany(Tag::class)
->withTimestamps()
->withPivot('order');
}
// One-to-one: Post has one statistics record
public function statistics()
{
return $this->hasOne(PostStatistics::class);
}
// Scopes
public function scopePublished($query)
{
return $query->where('status', PostStatus::Published)
->where('published_at', '<=', now());
}
public function scopeByCategory($query, $categoryId)
{
return $query->where('category_id', $categoryId);
}
}
// Add relationships to User model
class User extends Model
{
// Existing code...
// One-to-many: User has many posts
public function posts()
{
return $this->hasMany(Post::class);
}
// One-to-many: User has many comments
public function comments()
{
return $this->hasMany(Comment::class);
}
// One-to-one: User has one profile
public function profile()
{
return $this->hasOne(UserProfile::class);
}
// Many-to-many: User follow relationships
public function following()
{
return $this->belongsToMany(User::class, 'user_follows', 'follower_id', 'following_id')
->withTimestamps();
}
public function followers()
{
return $this->belongsToMany(User::class, 'user_follows', 'following_id', 'follower_id')
->withTimestamps();
}
// Has Many Through: User's post comments (through posts)
public function postComments()
{
return $this->hasManyThrough(Comment::class, Post::class);
}
}
// Category, Comment, Tag, UserProfile models
class Category extends Model
{
protected $fillable = ['name', 'description', 'slug'];
public function posts()
{
return $this->hasMany(Post::class);
}
}
class Comment extends Model
{
use SoftDeletes;
protected $fillable = ['content', 'user_id', 'post_id', 'parent_id'];
public function user()
{
return $this->belongsTo(User::class);
}
public function post()
{
return $this->belongsTo(Post::class);
}
// Self-referencing relationship: parent/child comments
public function parent()
{
return $this->belongsTo(Comment::class, 'parent_id');
}
public function children()
{
return $this->hasMany(Comment::class, 'parent_id');
}
}
class Tag extends Model
{
protected $fillable = ['name', 'slug'];
public function posts()
{
return $this->belongsToMany(Post::class)
->withTimestamps()
->withPivot('order');
}
}
class UserProfile extends Model
{
protected $fillable = [
'bio',
'website',
'location',
'birth_date',
'avatar_url',
];
protected $casts = [
'birth_date' => 'date',
];
public function user()
{
return $this->belongsTo(User::class);
}
}
// Relationship usage examples
class BlogService
{
public function getUserWithPosts(int $userId): ?User
{
return User::with(['posts' => function ($query) {
$query->published()->orderBy('published_at', 'desc');
}])->find($userId);
}
public function getPostWithRelations(int $postId): ?Post
{
return Post::with([
'user:id,name,email',
'category:id,name',
'tags:id,name',
'comments.user:id,name',
'statistics'
])->find($postId);
}
public function getPopularPosts(int $limit = 10): Collection
{
return Post::with(['user:id,name', 'category:id,name'])
->published()
->has('comments', '>=', 5)
->orderByDesc('created_at')
->limit($limit)
->get();
}
public function createPostWithTags(array $postData, array $tagIds): Post
{
$post = Post::create($postData);
$post->tags()->attach($tagIds);
return $post->load('tags');
}
}
Transactions and Advanced Operations
<?php
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Collection;
class AdvancedUserService
{
// Transaction processing
public function transferUserPosts(int $fromUserId, int $toUserId): bool
{
return DB::transaction(function () use ($fromUserId, $toUserId) {
$fromUser = User::findOrFail($fromUserId);
$toUser = User::findOrFail($toUserId);
// Transfer posts
Post::where('user_id', $fromUserId)
->update(['user_id' => $toUserId]);
// Transfer comments
Comment::where('user_id', $fromUserId)
->update(['user_id' => $toUserId]);
// Update statistics
$fromUser->update(['posts_count' => 0]);
$toUser->increment('posts_count', $fromUser->posts()->count());
return true;
});
}
// Batch processing
public function processInactiveUsers(): array
{
$result = [
'processed' => 0,
'deleted' => 0,
'notified' => 0,
];
// Efficient processing of large datasets using chunks
User::where('last_login_at', '<', now()->subMonths(6))
->where('status', UserStatus::Active)
->chunk(100, function (Collection $users) use (&$result) {
foreach ($users as $user) {
$result['processed']++;
if ($user->last_login_at < now()->subYear()) {
// Delete users with no login for over a year
$user->delete();
$result['deleted']++;
} else {
// Notify users with no login for over 6 months
$this->sendInactiveNotification($user);
$result['notified']++;
}
}
});
return $result;
}
// Custom queries
public function getUserEngagementStats(): Collection
{
return User::selectRaw('
users.id,
users.name,
COUNT(DISTINCT posts.id) as posts_count,
COUNT(DISTINCT comments.id) as comments_count,
MAX(posts.created_at) as last_post_at,
MAX(comments.created_at) as last_comment_at,
AVG(post_statistics.views) as avg_post_views
')
->leftJoin('posts', 'users.id', '=', 'posts.user_id')
->leftJoin('comments', 'users.id', '=', 'comments.user_id')
->leftJoin('post_statistics', 'posts.id', '=', 'post_statistics.post_id')
->groupBy('users.id', 'users.name')
->having('posts_count', '>', 0)
->orderByDesc('posts_count')
->get();
}
// Utilizing model events
public function setupUserEvents(): void
{
User::creating(function (User $user) {
// Automatically create profile when user is created
if (!$user->profile) {
$user->profile()->create([]);
}
});
User::updated(function (User $user) {
// Clear cache when user info is updated
Cache::forget("user.{$user->id}");
});
User::deleting(function (User $user) {
// Clean up related data when user is deleted
$user->posts()->delete();
$user->comments()->delete();
$user->profile()->delete();
});
}
// Complex search and filtering
public function advancedUserSearch(array $criteria): Collection
{
$query = User::query();
// Apply dynamic filters
if (!empty($criteria['name'])) {
$query->where('name', 'like', "%{$criteria['name']}%");
}
if (!empty($criteria['email'])) {
$query->where('email', 'like', "%{$criteria['email']}%");
}
if (!empty($criteria['status'])) {
$query->whereIn('status', (array) $criteria['status']);
}
if (!empty($criteria['age_range'])) {
[$min, $max] = $criteria['age_range'];
$query->whereRaw('DATEDIFF(CURDATE(), birth_date) BETWEEN ? AND ?',
[365 * $min, 365 * $max]);
}
if (!empty($criteria['posts_count_min'])) {
$query->has('posts', '>=', $criteria['posts_count_min']);
}
if (!empty($criteria['joined_after'])) {
$query->where('created_at', '>=', $criteria['joined_after']);
}
if (!empty($criteria['has_profile'])) {
if ($criteria['has_profile']) {
$query->has('profile');
} else {
$query->doesntHave('profile');
}
}
// Sorting
$sortBy = $criteria['sort_by'] ?? 'created_at';
$sortOrder = $criteria['sort_order'] ?? 'desc';
$query->orderBy($sortBy, $sortOrder);
// Eager loading relationships
$with = $criteria['with'] ?? [];
if (!empty($with)) {
$query->with($with);
}
return $query->get();
}
// Utilizing factories and seeding
public function createTestData(): array
{
return DB::transaction(function () {
// Create 10 users
$users = User::factory(10)->create();
// Create 3-7 posts for each user
$users->each(function (User $user) {
Post::factory(rand(3, 7))->create(['user_id' => $user->id]);
});
// Create categories and tags
$categories = Category::factory(5)->create();
$tags = Tag::factory(20)->create();
// Randomly assign tags to posts
Post::all()->each(function (Post $post) use ($tags) {
$post->tags()->attach(
$tags->random(rand(1, 4))->pluck('id')
);
});
return [
'users' => $users->count(),
'posts' => Post::count(),
'categories' => $categories->count(),
'tags' => $tags->count(),
];
});
}
private function sendInactiveNotification(User $user): void
{
// Notification logic (implementation omitted)
\Log::info("Sent inactive notification to user {$user->id}");
}
}
Error Handling
<?php
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\QueryException;
use Illuminate\Validation\ValidationException;
class RobustUserService
{
public function safeCreateUser(array $userData): array
{
try {
DB::beginTransaction();
// Validation
$this->validateUserData($userData);
// Create user
$user = User::create($userData);
// Create profile
$user->profile()->create([
'bio' => $userData['bio'] ?? '',
'location' => $userData['location'] ?? '',
]);
// Initial setup
$this->setupInitialUserSettings($user);
DB::commit();
return [
'success' => true,
'user' => $user->load('profile'),
'message' => 'User created successfully'
];
} catch (ValidationException $e) {
DB::rollBack();
return [
'success' => false,
'errors' => $e->errors(),
'message' => 'Validation failed'
];
} catch (QueryException $e) {
DB::rollBack();
if ($e->getCode() === '23000') { // Duplicate error
return [
'success' => false,
'errors' => ['email' => ['Email already exists']],
'message' => 'Email already exists'
];
}
\Log::error('Database error in user creation', [
'exception' => $e,
'userData' => $userData
]);
return [
'success' => false,
'errors' => ['database' => ['Database error occurred']],
'message' => 'Database error'
];
} catch (\Exception $e) {
DB::rollBack();
\Log::error('Unexpected error in user creation', [
'exception' => $e,
'userData' => $userData
]);
return [
'success' => false,
'errors' => ['system' => ['System error occurred']],
'message' => 'System error'
];
}
}
public function safeUpdateUser(int $userId, array $updates): array
{
try {
$user = User::findOrFail($userId);
// Permission check
if (!$this->canUpdateUser($user)) {
return [
'success' => false,
'errors' => ['permission' => ['Permission denied']],
'message' => 'Permission denied'
];
}
// Validation
$this->validateUserUpdateData($updates, $user);
DB::beginTransaction();
$user->update($updates);
// Update related data
if (isset($updates['profile'])) {
$user->profile()->updateOrCreate([], $updates['profile']);
}
DB::commit();
return [
'success' => true,
'user' => $user->fresh('profile'),
'message' => 'User updated successfully'
];
} catch (ModelNotFoundException $e) {
return [
'success' => false,
'errors' => ['user' => ['User not found']],
'message' => 'User not found'
];
} catch (ValidationException $e) {
DB::rollBack();
return [
'success' => false,
'errors' => $e->errors(),
'message' => 'Validation failed'
];
} catch (\Exception $e) {
DB::rollBack();
\Log::error('Error updating user', [
'userId' => $userId,
'updates' => $updates,
'exception' => $e
]);
return [
'success' => false,
'errors' => ['system' => ['Update failed']],
'message' => 'Update failed'
];
}
}
public function safeBulkOperation(array $userIds, callable $operation): array
{
$results = [
'success' => [],
'failed' => [],
'total' => count($userIds)
];
foreach ($userIds as $userId) {
try {
$result = $operation($userId);
$results['success'][] = [
'id' => $userId,
'result' => $result
];
} catch (\Exception $e) {
$results['failed'][] = [
'id' => $userId,
'error' => $e->getMessage()
];
\Log::error('Bulk operation failed for user', [
'userId' => $userId,
'exception' => $e
]);
}
}
return $results;
}
private function validateUserData(array $data): void
{
$validator = validator($data, [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
'password' => 'required|string|min:8',
'birth_date' => 'nullable|date|before:today',
]);
if ($validator->fails()) {
throw new ValidationException($validator);
}
}
private function validateUserUpdateData(array $data, User $user): void
{
$validator = validator($data, [
'name' => 'sometimes|string|max:255',
'email' => 'sometimes|email|unique:users,email,' . $user->id,
'birth_date' => 'sometimes|nullable|date|before:today',
]);
if ($validator->fails()) {
throw new ValidationException($validator);
}
}
private function canUpdateUser(User $user): bool
{
// Permission check logic (example)
return auth()->user()->can('update', $user);
}
private function setupInitialUserSettings(User $user): void
{
// Initial settings logic
$user->update([
'preferences' => [
'notifications' => true,
'newsletter' => false,
'theme' => 'light'
]
]);
}
}