Laravel

Understanding Eloquent ORM in Laravel

Administrator
By Administrator
Published Oct 07, 2025
8 min read
Understanding Eloquent ORM in Laravel

Understanding Eloquent ORM in Laravel

Eloquent ORM is Laravel's built-in Object-Relational Mapping (ORM) system. It provides an beautiful, simple ActiveRecord implementation for working with your database. With Eloquent, each database table has a corresponding "Model" which is used to interact with that table. This comprehensive guide will walk you through everything you need to know about Eloquent ORM.

What is Eloquent ORM?

Eloquent is a powerful ORM that allows you to interact with your database using expressive PHP syntax instead of writing raw SQL queries. It abstracts away the complexity of database operations and provides a clean, object-oriented interface for working with your data.

Basic Model Creation

Creating a Model

Create models using Artisan commands:

// Create a basic model
php artisan make:model User

// Create a model with migration
php artisan make:model User -m

// Create a model with controller
php artisan make:model User --controller

// Create a model with migration and controller
php artisan make:model User -m --controller

// Create a model with factory and seeder
php artisan make:model User -f --seed

Basic Model Structure

Here's what a basic Eloquent model looks like:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class User extends Model
{
    use HasFactory, SoftDeletes;

    // Specify which fields are mass assignable
    protected $fillable = [
        'name',
        'email',
        'password',
        'role'
    ];

    // Hide sensitive fields from JSON output
    protected $hidden = [
        'password',
        'remember_token'
    ];

    // Specify custom primary key
    protected $primaryKey = 'user_id';

    // Specify if auto-incrementing is disabled
    public $incrementing = false;

    // Specify custom primary key type
    protected $keyType = 'string';

    // Specify custom table name
    protected $table = 'users';

    // Enable or disable timestamps
    public $timestamps = true;

    // Custom timestamp columns
    const CREATED_AT = 'created_at';
    const UPDATED_AT = 'updated_at';
}

Basic Database Operations

Creating Records

Insert new records into the database:

<?php

namespace App\Http\Controllers;

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

class UserController extends Controller
{
    public function create()
    {
        // Create a single record
        $user = User::create([
            'name' => 'John Doe',
            'email' => '[email protected]',
            'password' => bcrypt('password123')
        ]);

        // Create multiple records
        $users = User::insert([
            ['name' => 'Jane Smith', 'email' => '[email protected]', 'password' => bcrypt('password123')],
            ['name' => 'Bob Johnson', 'email' => '[email protected]', 'password' => bcrypt('password123')]
        ]);

        // Create or update
        $user = User::updateOrCreate(
            ['email' => '[email protected]'],
            ['name' => 'John Updated Doe']
        );

        return 'User created successfully!';
    }
}

Reading Records

Retrieve records from the database:

// Get all records
$users = User::all();

// Get first record
$user = User::first();

// Find by primary key
$user = User::find(1);

// Find or fail
$user = User::findOrFail(1);

// Find by condition
$user = User::where('email', '[email protected]')->first();

// Get with conditions
$activeUsers = User::where('active', true)->get();

// Get with multiple conditions
$users = User::where('active', true)
            ->where('role', 'admin')
            ->get();

// Get with ordering
$users = User::orderBy('created_at', 'desc')->get();

// Limit and offset
$users = User::skip(10)->take(5)->get();

// Get count
$count = User::count();

// Check if records exist
$exists = User::where('email', '[email protected]')->exists();

// Get first or create
$user = User::firstOrCreate(
    ['email' => '[email protected]'],
    ['name' => 'John Doe']
);

Updating Records

Update existing records:

// Update single record
$user = User::find(1);
$user->update([
    'name' => 'Updated Name',
    'email' => '[email protected]'
]);

// Update multiple records
User::where('active', false)
    ->update(['active' => true]);

// Mass update
$affected = User::where('votes', '>', 100)
    ->update(['status' => 'VIP']);

// Increment/decrement
$user = User::find(1);
$user->increment('login_count');
$user->decrement('login_count');

// Increment with amount
$user->increment('balance', 100);

// Decrement with amount
$user->decrement('balance', 50);

Deleting Records

Delete records from the database:

// Delete single record
$user = User::find(1);
$user->delete();

// Delete by condition
User::where('active', false)->delete();

// Force delete (ignores soft deletes)
User::where('deleted_at', '!=', null)->forceDelete();

// Mass delete
$deleted = User::where('created_at', '<', '2023-01-01')->delete();

// Restore soft deleted records
User::withTrashed()->where('deleted_at', '!=', null)->restore();

Query Builder Methods

Where Clauses

Advanced filtering with where clauses:

// Basic where
$users = User::where('active', true)->get();

// Multiple where
$users = User::where('active', true)->where('role', 'admin')->get();

// Or where
$users = User::where('active', true)->orWhere('role', 'admin')->get();

// Where between
$users = User::whereBetween('age', [18, 30])->get();

// Where not between
$users = User::whereNotBetween('age', [18, 30])->get();

// Where in
$users = User::whereIn('id', [1, 2, 3, 4, 5])->get();

// Where not in
$users = User::whereNotIn('id', [1, 2, 3, 4, 5])->get();

// Where null
$users = User::where('email_verified_at', null)->get();

// Where not null
$users = User::whereNotNull('email_verified_at')->get();

// Where has (for relationships)
$users = User::whereHas('posts', function($query) {
    $query->where('published', true);
})->get();

// Where has multiple
$users = User::whereHas('posts', function($query) {
    $query->where('published', true);
})->whereHas('comments', function($query) {
    $query->where('approved', true);
})->get();

Advanced Query Methods

Complex query operations:

// Select specific columns
$users = User::select('name', 'email')->get();

// Select with alias
$users = User::select('name as user_name', 'email')->get();

// Distinct
$users = User::select('role')->distinct()->get();

// Group by
$users = User::select('role')
             ->selectRaw('count(*) as user_count')
             ->groupBy('role')
             ->get();

// Having
$users = User::select('role')
             ->selectRaw('count(*) as user_count')
             ->groupBy('role')
             ->having('user_count', '>', 5)
             ->get();

// Join
$users = User::join('posts', 'users.id', '=', 'posts.user_id')
             ->select('users.name', 'posts.title')
             ->get();

// Left join
$users = User::leftJoin('posts', 'users.id', '=', 'posts.user_id')
             ->select('users.name', 'posts.title')
             ->get();

// Multiple joins
$users = User::join('posts', 'users.id', '=', 'posts.user_id')
             ->join('categories', 'posts.category_id', '=', 'categories.id')
             ->select('users.name', 'posts.title', 'categories.name as category')
             ->get();

Model Relationships

One to One Relationship

Define one-to-one relationships:

<?php

// In Profile model
class Profile extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

// In User model
class User extends Model
{
    public function profile()
    {
        return $this->hasOne(Profile::class);
    }
}

// Usage
$user = User::find(1);
$profile = $user->profile;

// Or inverse
$profile = Profile::find(1);
$user = $profile->user;

// Eager loading
$user = User::with('profile')->find(1);

One to Many Relationship

Define one-to-many relationships:

<?php

// In Post model
class Post extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}

// In User model
class User extends Model
{
    public function posts()
    {
        return $this->hasMany(Post::class);
    }

    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}

// Usage
$user = User::find(1);
$posts = $user->posts;
$comments = $user->comments;

// For a specific post
$post = Post::find(1);
$user = $post->user;
$postComments = $post->comments;

// With constraints
$recentPosts = $user->posts()->where('created_at', '>=', '2023-01-01')->get();

// Count relationships
$postCount = $user->posts()->count();

Many to Many Relationship

Define many-to-many relationships:

<?php

// In Post model
class Post extends Model
{
    public function categories()
    {
        return $this->belongsToMany(Category::class);
    }

    public function tags()
    {
        return $this->belongsToMany(Tag::class);
    }
}

// In Category model
class Category extends Model
{
    public function posts()
    {
        return $this->belongsToMany(Post::class);
    }
}

// In Tag model
class Tag extends Model
{
    public function posts()
    {
        return $this->belongsToMany(Post::class);
    }
}

// Usage
$post = Post::find(1);
$categories = $post->categories;
$tags = $post->tags;

// For a specific category
$category = Category::find(1);
$posts = $category->posts;

// Attach relationships
$post->categories()->attach([1, 2, 3]);
$post->tags()->sync([1, 2, 3]);

// Detach relationships
$post->categories()->detach([1, 2]);
$post->categories()->detach(); // Detach all

// Toggle relationships
$post->categories()->toggle([1, 2]);

// Update pivot table data
$post->categories()->sync([1 => ['featured' => true], 2 => ['featured' => false]]);

Accessors and Mutators

Accessors

Modify data when retrieving from the database:

<?php

class User extends Model
{
    // Accessor for name
    public function getFirstNameAttribute($value)
    {
        return ucfirst($value);
    }

    // Accessor for full name
    public function getFullNameAttribute()
    {
        return $this->first_name . ' ' . $this->last_name;
    }

    // Accessor for age group
    public function getAgeGroupAttribute()
    {
        if ($this->age < 18) {
            return 'Minor';
        } elseif ($this->age < 30) {
            return 'Young Adult';
        } elseif ($this->age < 50) {
            return 'Adult';
        } else {
            return 'Senior';
        }
    }

    // Accessor for password (hash automatically)
    public function setPasswordAttribute($value)
    {
        $this->attributes['password'] = bcrypt($value);
    }

    // Accessor for email (lowercase)
    public function setEmailAttribute($value)
    {
        $this->attributes['email'] = strtolower($value);
    }
}

// Usage
$user = User::find(1);
echo $user->first_name; // Automatically capitalized
echo $user->full_name; // "John Doe"
echo $user->age_group; // "Young Adult"

Scopes

Global Scopes

Apply conditions to all queries:

<?php

class ActiveUserScope implements Scope
{
    public function apply(Builder $builder, Model $model)
    {
        $builder->where('active', true);
    }
}

// Apply to model
class User extends Model
{
    protected static function boot()
    {
        parent::boot();
        static::addGlobalScope(new ActiveUserScope());
    }
}

Local Scopes

Reusable query constraints:

<?php

class User extends Model
{
    // Local scope for active users
    public function scopeActive($query)
    {
        return $query->where('active', true);
    }

    // Local scope for admin users
    public function scopeAdmin($query)
    {
        return $query->where('role', 'admin');
    }

    // Local scope for recent users
    public function scopeRecent($query, $days = 7)
    {
        return $query->where('created_at', '>=', now()->subDays($days));
    }

    // Local scope for users with age
    public function scopeWithAge($query, $minAge, $maxAge)
    {
        return $query->whereBetween('age', [$minAge, $maxAge]);
    }
}

// Usage
$activeUsers = User::active()->get();
$adminUsers = User::admin()->get();
$recentUsers = User::recent(30)->get(); // 30 days
$usersByAge = User::withAge(18, 30)->get();

// Chain scopes
$activeAdmins = User::active()->admin()->get();

Collections and JSON

Working with Collections

Powerful collection methods:

// Map through collection
$names = User::all()->map(function($user) {
    return $user->name;
});

// Filter collection
$activeUsers = User::all()->filter(function($user) {
    return $user->active;
});

// Sort collection
$sortedUsers = User::all()->sortBy('name');

// Group collection
$usersByRole = User::all()->groupBy('role');

// Chunk collection
$chunks = User::all()->chunk(10);

// Find in collection
$user = User::all()->first(function($user) {
    return $user->email === '[email protected]';
});

// Check conditions
$hasActiveUsers = User::all()->contains(function($user) {
    return $user->active;
});

// Sum values
$totalAge = User::all()->sum('age');

// Average values
$averageAge = User::all->avg('age');

// Get unique values
$uniqueRoles = User::all->unique('role');

JSON Serialization

Convert models to JSON:

// Convert single model to JSON
$user = User::find(1);
$json = $user->toJson();

// Convert collection to JSON
$users = User::all();
$json = $users->toJson();

// Convert to array
$userArray = $user->toArray();

// Convert collection to array
$usersArray = $users->toArray();

// Custom JSON serialization
class User extends Model
{
    protected $appends = ['full_name'];

    public function getFullNameAttribute()
    {
        return $this->first_name . ' ' . $this->last_name;
    }
}

// Usage
$user = User::find(1);
echo $user->full_name; // Available in JSON output

Conclusion

Eloquent ORM is one of Laravel's most powerful features. By mastering these concepts and techniques, you can build complex database-driven applications with clean, maintainable code. Remember to follow best practices like using proper relationships, accessors, mutators, and scopes to keep your code organized and efficient.

Key takeaways:

  • Use models as the primary interface to your database
  • Leverage relationships for complex data structures
  • Use accessors and mutators for data transformation
  • Apply scopes for reusable query constraints
  • Work with collections for powerful data manipulation
  • Follow naming conventions and best practices

With Eloquent ORM, you can build sophisticated applications while writing clean, expressive PHP code. Happy coding! 🚀

Related Articles

Using Laravel Sanctum for API Authentication

Using Laravel Sanctum for API Authentication

Oct 07, 2025

Introduction to Laravel Sanctum Laravel Sanctum is a simple authentication library for SPAs (Sing...

How to Send Emails in Laravel Using Mailables

How to Send Emails in Laravel Using Mailables

Oct 07, 2025

Introduction to Laravel Mailables Sending emails in Laravel has never been easier thanks to Maila...

Laravel Queues and Jobs: Complete Beginner's Guide

Laravel Queues and Jobs: Complete Beginner's Guide

Oct 07, 2025

What Are Laravel Queues? Laravel Queues allow you to defer processing of time-consuming tasks. In...

How to Handle File Uploads in Laravel

How to Handle File Uploads in Laravel

Oct 07, 2025

Introduction to File Uploads in Laravel File uploads are a fundamental feature in most web applic...