Understanding Eloquent ORM in Laravel

Eloquent is Laravel's built-in ORM (Object-Relational Mapping) that makes interacting with a database feel like working with plain PHP objects. Instead of writing long SQL queries,...

Understanding Eloquent ORM in Laravel

Eloquent is Laravel's built-in ORM (Object-Relational Mapping) that makes interacting with a database feel like working with plain PHP objects. Instead of writing long SQL queries, you simply call methods on a model. Mastering Eloquent is what separates developers who write messy code from those who write clean, fast, maintainable Laravel code. This article covers Eloquent thoroughly with real examples.

1. A Model Represents a Table

Every Eloquent model represents one table. By convention, a Post model automatically maps to a posts table (plural, lowercase). Create a model with artisan:

php artisan make:model Post

A minimal model is enough. You rarely need any configuration because Eloquent relies on convention:

class Post extends Model
{
    protected $fillable = ['title', 'body', 'user_id', 'published'];
}

The $fillable property defines which columns may be mass-assigned via create() or update(). This protects you from mass-assignment vulnerabilities.

2. Retrieving Data (Read)

Eloquent offers many methods to fetch data. Here are the most common:

$posts = Post::all();                    // all rows
$post  = Post::find(1);                  // find by primary key
$post  = Post::findOrFail(1);            // 404 if missing
$first = Post::where('published', true)->first();

$published = Post::where('published', true)
    ->orderBy('created_at', 'desc')
    ->take(10)
    ->get();

Important difference: get() returns a Collection, while first() returns a single model or null. Use findOrFail() in a controller so Laravel automatically throws a 404 when the record is missing.

3. Create, Update, and Delete

Write operations in Eloquent are very concise:

// Create
$post = Post::create([
    'title'   => 'Learning Eloquent',
    'body'    => 'Article content...',
    'user_id' => 1,
]);

// Update
$post->update(['title' => 'New Title']);

// Delete
$post->delete();

There is also updateOrCreate(), useful to prevent duplicates: Eloquent looks for a matching row, updates it if found, or creates a new one if not.

Post::updateOrCreate(
    ['slug' => 'learning-eloquent'],   // search conditions
    ['title' => 'Learning Eloquent', 'body' => '...']
);

4. Relationships Between Tables

Eloquent's real power lies in relationships. Imagine a User has many Posts, and each Post has many Comments.

hasMany and belongsTo

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

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

Now you can access relationships like normal properties:

$user = User::find(1);
foreach ($user->posts as $post) {
    echo $post->title;
}

$post = Post::find(1);
echo $post->user->name;   // navigate to the owner

belongsToMany (Many-to-Many)

A many-to-many relationship, for example a Post having many Tags and vice versa, needs a pivot table (usually post_tag):

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

// Attach tags without removing existing ones:
$post->tags()->attach([1, 2, 3]);
// Sync (remove any not in the list):
$post->tags()->sync([2, 3]);

5. The N+1 Problem and Eager Loading

This is a concept you MUST understand. Look at this code:

$posts = Post::all();
foreach ($posts as $post) {
    echo $post->user->name;   // 1 EXTRA query per post!
}

With 100 posts, Laravel runs 1 query to fetch the posts + 100 queries to fetch each user = 101 queries. This is the N+1 problem. The fix is eager loading with with():

$posts = Post::with('user')->get();
foreach ($posts as $post) {
    echo $post->user->name;   // no extra queries
}

Now it is only 2 queries total, no matter how many posts. You can even eager load nested relationships: Post::with('user', 'comments.user')->get().

6. Accessors and Mutators

An accessor transforms a value when it is READ; a mutator transforms it when it is SAVED. In modern Laravel both live in a single method using Attribute:

use Illuminate\Database\Eloquent\Casts\Attribute;

protected function title(): Attribute
{
    return Attribute::make(
        get: fn ($value) => ucfirst($value),
        set: fn ($value) => strtolower($value),
    );
}

With this, the title is always stored lowercase but displayed capitalized automatically.

7. Query Scopes for Clean Code

Scopes store reusable query fragments so controllers stay concise. Define them with the scope prefix:

public function scopePublished($query)
{
    return $query->where('published', true);
}

Call them without the scope prefix, and chain them freely:

$posts = Post::published()->latest()->get();

Best Practices When Using Eloquent

The following habits keep your Eloquent code healthy in the long run:

  • Always declare $fillable: do not rely on $guarded = [] in real apps because it opens a mass-assignment hole.
  • Use casting: convert columns to the right type via the $casts property, for example 'published' => 'boolean' or 'meta' => 'array', so values are cast automatically.
  • Pick the right relationship: confusing hasOne and hasMany makes the data you fetch behave unexpectedly.
  • Use findOrFail in controllers: this keeps the app returning a proper 404 instead of a null error.

A very commonly used casting example:

protected $casts = [
    'published'    => 'boolean',
    'published_at' => 'datetime',
    'meta'         => 'array',
];

With the casts above, a meta column stored as JSON automatically becomes a PHP array when read, and turns back into JSON when saved — without any manual json_encode.

Conclusion

Eloquent makes you think in objects and relationships rather than raw SQL. The keys to mastering it: understand model conventions, use relationships to connect data, and always stay alert to N+1 with eager loading. With accessors, mutators, casting, and scopes, your code stays tidy and readable even as the application grows. Start with one simple model, then add relationships and scopes gradually as your feature needs increase.

Yudhi
Written by
Yudhi
Founder & Lead Developer, GudangCode

Yudhi is the founder of GudangCode and a Laravel developer who has built dozens of ready-to-use business information systems — from POS and HRIS to management apps. He writes guides and articles on GudangCode to help Indonesian developers run, understand, and deploy Laravel source code correctly.

LaravelPHPMySQLSistem Informasi Bisnis See all articles by Yudhi
Want the full source code & apps?

Sign up free to download ready-to-use business applications, information systems, and Laravel source code.

Sign Up Free & Download
Eloquent Orm Laravel Programming & Laravel
Share this article
Back to Blog
📚 Free Learning Hub

Learn Coding for Free at DhieCoderWeb

Explore Laravel, PHP, JavaScript tutorials, source code, web development guides, and practical programming tips.

DhieCoderWeb
100+
Tutorials
Free
Learning
SEO
Tips
Visit Dhiecoderweb.com →

Get Full Access Now!

Join our membership and unlock exclusive access to all premium features. Fast, easy, and ready to use instantly.

Join Membership Now
Tim Support
Online
Isi data dulu untuk mulai chat:
Beri rating & testimoni sebelum menutup:
Live chat by gudangcode.com