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!
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'];
}
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';
}
Simple, right? Now let’s dive into some best practices for handling models efficiently.
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'];
}
Now, only the specified fields can be mass-assigned:
User::create(['name' => 'John Doe', 'email' => 'john@example.com', 'password' => 'secret']);
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'];
}
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);
}
}
Fetching the profile:
$user = User::find(1);
$profile = $user->profile;
One-to-Many (hasMany)
A User can have multiple Posts:
class User extends Model
{
public function posts()
{
return $this->hasMany(Post::class);
}
}
Retrieve posts:
$posts = User::find(1)->posts;
Check out the docs on relationships for detailed informations.
✅ 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');
});
}
}
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');
}
}
✅ 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
Mutators (Modify data before storing)
class User extends Model
{
public function setPasswordAttribute($value)
{
$this->attributes['password'] = bcrypt($value);
}
}
$user->password = 'mypassword';
The password is automatically hashed before saving.
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',
];
}
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'];
}
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());
});
}
}
🔹 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());
}
}
Register the observer inside AppServiceProvider.php. Now, every time a user is created, an email will be sent.
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)