DEV Community

Cover image for The Art of Laravel Models - Get the Most of Them
varzoeaa
varzoeaa

Posted on

The Art of Laravel Models - Get the Most of Them

If you've ever worked with databases in Laravel, you've probably come across Eloquent models. They're the backbone of Laravel’s ORM(Object-Relational Mapping) system, making database interactions smooth and intuitive. Think of models as the middleman between your database tables and your PHP code—no need to manually write SQL queries every time you need some data! 🤩

With Laravel models, you can work with database records using simple and expressive PHP syntax. And the best part? They come with built-in features like relationships, accessors, mutators, and query scopes to make your life easier. Let’s break it down!

first

Understanding the Basics of Laravel Models

Every model in Laravel extends the Illuminate\Database\Eloquent\Model class, which gives it superpowers. Here’s what a basic model looks like:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    protected $fillable = ['name', 'email', 'password'];
}
Enter fullscreen mode Exit fullscreen mode

A model represents a table in your database. If you follow Laravel conventions, the model User automatically maps to the users table. But if your table name doesn’t match, you can define it manually:

class Product extends Model
{
    protected $table = 'my_products';
}
Enter fullscreen mode Exit fullscreen mode

Simple, right? Now let’s dive into some best practices for handling models efficiently.

second

How to Handle Models Efficiently?

Before diving into your Laravel project, take a moment to set up your models correctly. A well-structured model ensures cleaner code and better performance.

Prevent Mass Assignment Vulnerabilities

Ever heard of mass assignment? It happens when users send a big chunk of data to be saved—without any restrictions on which fields can be updated. Laravel protects you from this by using fillable and guarded attributes.

Using fillable (The Allowlist Approach)

This explicitly defines which fields can be mass-assigned:

class User extends Model
{
    protected $fillable = ['name', 'email', 'password'];
}
Enter fullscreen mode Exit fullscreen mode

Now, only the specified fields can be mass-assigned:

User::create(['name' => 'John Doe', 'email' => 'john@example.com', 'password' => 'secret']);
Enter fullscreen mode Exit fullscreen mode

Using guarded (The Blocklist Approach)

If you prefer to protect only a few fields, guarded is your friend:

class User extends Model
{
    protected $guarded = ['role'];
}
Enter fullscreen mode Exit fullscreen mode

Now, all fields except role can be mass-assigned.

💡 Which one should you use?

  • use fillable for better security as it follows an allowlist approach
  • use guarded = [] if you want to allow all attributes by default and only protect specific ones

Define Relationships Properly

Eloquent relationships help you structure queries efficiently.

One-to-One (hasOne)

A User has one Profile:

class User extends Model
{
    public function profile()
    {
        return $this->hasOne(Profile::class);
    }
}
Enter fullscreen mode Exit fullscreen mode

Fetching the profile:

$user = User::find(1);
$profile = $user->profile;
Enter fullscreen mode Exit fullscreen mode

One-to-Many (hasMany)

A User can have multiple Posts:

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

Retrieve posts:

$posts = User::find(1)->posts;
Enter fullscreen mode Exit fullscreen mode

Check out the docs on relationships for detailed informations.

third

Use Query Scopes for Reusable Queries

Do you keep repeating the same query conditions? Query scopes allow you to define common filters inside the model itself.

Global Scope (applies to all queries)

class User extends Model
{
    protected static function boot()
    {
        parent::boot();
        static::addGlobalScope('active', function ($query) {
            $query->where('status', 'active');
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

Now, User::all() only fetches active users.

Local Scope (applies on demand)

class User extends Model
{
    public function scopeActive($query)
    {
        return $query->where('status', 'active');
    }
}
Enter fullscreen mode Exit fullscreen mode

fourth

Use Accessors & Mutators for Data Formatting

Need to transform data before saving or retrieving it? Accessors and mutators are here to help.

Accessors (Format data on retrieval)

class User extends Model
{
    public function getFullNameAttribute()
    {
        return ucfirst($this->first_name) . ' ' . ucfirst($this->last_name);
    }
}

echo $user->full_name; // John Doe
Enter fullscreen mode Exit fullscreen mode

Mutators (Modify data before storing)

class User extends Model
{
    public function setPasswordAttribute($value)
    {
        $this->attributes['password'] = bcrypt($value);
    }
}

$user->password = 'mypassword';
Enter fullscreen mode Exit fullscreen mode

The password is automatically hashed before saving.

fifth

Advanced Model Features

Once you’re comfortable with the basics, here are some powerful model features to explore:

🔹 Attribute Casting

Laravel allows you to cast attributes to common data types:

class User extends Model
{
    protected $casts = [
        'is_admin' => 'boolean',
        'created_at' => 'datetime',
        'preferences' => 'array',
    ];
}
Enter fullscreen mode Exit fullscreen mode

Now, is_admin will always return a boolean, and preferences will be an array when retrieved.

🔹 Soft Deletes

use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
    use SoftDeletes;
    protected $dates = ['deleted_at'];
}
Enter fullscreen mode Exit fullscreen mode

Now, deleted records stay in the database and can be restored!

🔹 Custom Model Events

Want to run logic when a model is created or updated? Use model events!

class User extends Model
{
    protected static function boot()
    {
        parent::boot();
        static::created(function ($user) {
            Mail::to($user->email)->send(new WelcomeEmail());
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

🔹 Observers for Better Model Event Handling

Instead of defining event logic inside the model, you can use observers to keep your models clean:

class UserObserver
{
    public function created(User $user)
    {
        Mail::to($user->email)->send(new WelcomeEmail());
    }
}
Enter fullscreen mode Exit fullscreen mode

Register the observer inside AppServiceProvider.php. Now, every time a user is created, an email will be sent.

sixth

Final Thoughts

Laravel models are incredibly powerful, but they can be tricky if not used properly. Follow best practices, avoid common pitfalls, and use Laravel’s built-in features to keep your code clean and efficient.

Top comments (0)