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.

ORMPHPLaravelActive RecordQuery BuilderRelationships

GitHub Overview

laravel/framework

The Laravel Framework.

Stars33,844
Watchers958
Forks11,444
Created:January 10, 2013
Language:PHP
License:MIT License

Topics

frameworklaravelphp

Star History

laravel/framework Star History
Data as of: 7/19/2025, 09:31 AM

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'
            ]
        ]);
    }
}