Models
Creating and using Eloquent Models
Eloquent Models are PHP classes where each represents a table in the database and uses Active Record pattern. Each instance of Model represents a row in database table.
Main Model Features:
- <strong>Mass Assignment Protection</strong>: Protection against accidentally assigning sensitive fields with <code>$fillable</code> and <code>$guarded</code>
- <strong>Timestamps</strong>: Automatic management of <code>created_at</code> and <code>updated_at</code>
- <strong>Soft Deletes</strong>: Soft deletion with <code>SoftDeletes</code> trait
- <strong>Casting</strong>: Automatic data type conversion when reading and writing
- <strong>Accessors & Mutators</strong>: Transforming and accessing attributes before reading or writing
- <strong>Scopes</strong>: Query scopes for reusable queries
- <strong>Relationships</strong>: Defining relationships between models
- <strong>Events</strong>: Lifecycle events for hooks
- <strong>Observers</strong>: Model observers for event handling
- <strong>Factories</strong>: Model factories for test data
- <strong>Pagination</strong>: Built-in pagination support
Model Structure:
Each Model must extend Illuminate\Database\Eloquent\Model. Laravel automatically infers table name from class name (plural form), but you can override it with $table property.
Convention over Configuration:
Laravel uses conventions to reduce need for configuration:
- Table name: plural form of model name (User → users)
- Primary key: <code>id</code>
- Foreign key: <code>{model}_id</code> (User → user_id)
- Timestamps: <code>created_at</code>, <code>updated_at</code>
- Soft delete: <code>deleted_at</code>
Model Lifecycle:
Models have lifecycle events that you can hook into:
- <code>creating</code>, <code>created</code> - before and after create
- <code>updating</code>, <code>updated</code> - before and after update
- <code>saving</code>, <code>saved</code> - before and after save (create or update)
- <code>deleting</code>, <code>deleted</code> - before and after delete
- <code>restoring</code>, <code>restored</code> - before and after restore (soft delete)
Best Practices:
- Use <code>$fillable</code> or <code>$guarded</code> for mass assignment protection
- Use <code>$hidden</code> for fields that shouldn't be shown in JSON
- Use <code>$casts</code> for type casting
- Use relationships for working with related data
- Use scopes for reusable queries
Examples
Basic Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected $table = 'users';
protected $fillable = [
'name',
'email',
'password'
];
protected $hidden = [
'password',
'remember_token'
];
protected $casts = [
'email_verified_at' => 'datetime',
'is_active' => 'boolean'
];
}
A basic Model with fillable, hidden and casts defined.
Model with Relationships
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function posts()
{
return $this->hasMany(Post::class);
}
public function profile()
{
return $this->hasOne(Profile::class);
}
public function roles()
{
return $this->belongsToMany(Role::class);
}
}
// Usage
$user = User::find(1);
$posts = $user->posts;
$profile = $user->profile;
$roles = $user->roles;
A Model with various relationships defined.
Model with Scopes
<?php
class Post extends Model
{
public function scopePublished($query)
{
return $query->where('status', 'published');
}
public function scopeByAuthor($query, $authorId)
{
return $query->where('author_id', $authorId);
}
protected static function booted()
{
static::addGlobalScope('active', function ($query) {
$query->where('active', 1);
});
}
}
// Usage
$posts = Post::published()->byAuthor(1)->get();
Model with local and global scopes.
Model Events
<?php
class User extends Model
{
protected static function booted()
{
static::creating(function ($user) {
$user->uuid = Str::uuid();
});
static::created(function ($user) {
// Send welcome email
Mail::to($user->email)->send(new WelcomeMail($user));
});
static::updating(function ($user) {
// Log changes
Log::info('User updated', ['user_id' => $user->id]);
});
}
}
Using model events for lifecycle hooks.
Model with Accessors and Mutators
<?php
class User extends Model
{
public function getFullNameAttribute()
{
return "{$this->first_name} {$this->last_name}";
}
public function setPasswordAttribute($value)
{
$this->attributes['password'] = Hash::make($value);
}
public function getIsAdminAttribute()
{
return $this->role === 'admin';
}
}
// Usage
$user = User::find(1);
echo $user->full_name; // Accessor
$user->password = 'secret'; // Mutator
echo $user->is_admin; // Accessor
Using accessors and mutators to transform data.
Model with Soft Deletes
<?php
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use SoftDeletes;
protected $dates = ['deleted_at'];
}
// Usage
$post->delete(); // Soft delete
$post->restore(); // Restore
$post->forceDelete(); // Permanent delete
// Query deleted records
$deletedPosts = Post::onlyTrashed()->get();
$allPosts = Post::withTrashed()->get();
Using SoftDeletes trait for soft deletion.
Custom Primary Key
<?php
class User extends Model
{
protected $primaryKey = 'uuid';
public $incrementing = false;
protected $keyType = 'string';
}
// Or with composite key
class UserRole extends Model
{
protected $primaryKey = ['user_id', 'role_id'];
public $incrementing = false;
}
Defining custom primary key for model.
Use Cases
- Representing database tables in object-oriented way
- Defining relationships between entities
- Mass assignment protection for security
- Data transformation with accessors/mutators
- Query building with scopes
- Lifecycle management with events
- Soft deletion for data retention
Common Mistakes
- Forgetting to define fillable or guarded causing mass assignment vulnerability
- Using all() for large datasets causing memory problems
- N+1 query problem with relationships
- Forgetting return statement in scope methods
- Using model events for complex business logic
- Not using eager loading for relationships
Best Practices
- Always define fillable or guarded
- Use eager loading for relationships
- Use scopes for reusable queries
- Use model events for simple hooks
- Use accessors/mutators for data transformation
- Use factories for test data
- Keep model logic simple and put business logic in service layer
Edge Cases
- Models with composite primary keys
- Models with custom table names
- Models with multiple database connections
- Model events with circular dependencies
- Soft deletes with relationships
- Mass assignment with nested data
- Models with polymorphic relationships
Performance Notes
- Use eager loading to prevent N+1 queries
- Use select() for selecting specific columns
- Use chunking for large datasets
- Use indexes for foreign keys
- Use query caching for frequently accessed data
- Use lazy loading for memory efficiency
Security Notes
- Always use fillable or guarded for mass assignment protection
- Use hidden for sensitive fields
- Use forceFill() only for trusted data
- Ensure relationships are properly authorized
- Don't use mass assignment for user input
- Ensure accessors don't expose sensitive data
Interview Points
- What is Eloquent Model and how does it work?
- What is the difference between fillable and guarded?
- What are model events and when are they used?
- What is the difference between accessor and mutator?
- How can you define custom primary key?
- What are soft deletes and how do they work?
Version Notes
- Laravel 11.x: Improved performance in model operations
- Laravel 11.x: Better support for model events
- Laravel 10.x: Improved eager loading
- Laravel 9.x: Improved model factories