Eloquent is Laravel's built-in ORM (Object-Relational Mapping). It lets you interact with your database using PHP classes and objects instead of writing raw SQL. Each database table has a corresponding "Model" that you use to query, insert, update, and delete data.
What is Eloquent?
Think of Eloquent as a translator between your PHP code and the database. Instead of writing:
SELECT * FROM users WHERE active = 1 ORDER BY name ASC;You write this instead:
$users = User::where('active', 1)->orderBy('name')->get();Much cleaner, safer (prevents SQL injection), and easier to maintain.
1. Creating a Model
Every Eloquent model represents one database table. By convention, a Post model maps to a posts table.
# Create a model
php artisan make:model Post
# Create model + migration + controller + factory + seeder
php artisan make:model Post -mcfsnamespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
// Fields that can be mass assigned
protected $fillable = [
'title',
'body',
'status',
'user_id',
];
// Fields that should be hidden (e.g. in JSON)
protected $hidden = [
'password',
];
// Auto-cast fields to specific types
protected $casts = [
'published_at' => 'datetime',
'is_featured' => 'boolean',
'metadata' => 'array',
];
}$fillable = whitelist (only these fields can be mass assigned). $guarded = blacklist (all fields except these can be mass assigned). Use $guarded = [] to allow all fields. 2. CRUD Operations
Create (Insert Data)
// Method 1: Create and save in one step
$post = Post::create([
'title' => 'My First Post',
'body' => 'This is the content.',
]);
// Method 2: Create instance, set values, then save
$post = new Post();
$post->title = 'My First Post';
$post->body = 'This is the content.';
$post->save();
// Method 3: Find or create (avoid duplicates)
$post = Post::firstOrCreate(
['title' => 'My First Post'], // Search by this
['body' => 'This is the content.'] // Create with this if not found
);Read (Fetch Data)
// Get ALL posts
$posts = Post::all();
// Find by primary key (ID)
$post = Post::find(1);
// Find or throw 404 error
$post = Post::findOrFail(1);
// Get first matching record
$post = Post::where('status', 'published')->first();
// Get with conditions
$posts = Post::where('status', 'published')
->where('user_id', 1)
->orderBy('created_at', 'desc')
->take(10)
->get();
// Count records
$count = Post::where('status', 'published')->count();
// Check if any exist
$exists = Post::where('title', 'Hello')->exists();Update (Modify Data)
// Method 1: Find then update
$post = Post::find(1);
$post->title = 'Updated Title';
$post->save();
// Method 2: Update in one step
$post = Post::find(1);
$post->update([
'title' => 'Updated Title',
'status' => 'published',
]);
// Method 3: Mass update (multiple records)
Post::where('status', 'draft')
->update(['status' => 'archived']);Delete (Remove Data)
// Method 1: Find then delete
$post = Post::find(1);
$post->delete();
// Method 2: Delete by ID directly
Post::destroy(1);
// Method 3: Delete multiple by IDs
Post::destroy([1, 2, 3]);
// Method 4: Delete with condition
Post::where('status', 'archived')->delete();3. Query Scopes
Scopes let you define reusable query conditions inside your model. This keeps your controllers clean.
class Post extends Model
{
// Define a scope (always prefix with "scope")
public function scopePublished($query)
{
return $query->where('status', 'published');
}
public function scopeRecent($query)
{
return $query->orderBy('created_at', 'desc');
}
// Scope with parameter
public function scopeOfStatus($query, string $status)
{
return $query->where('status', $status);
}
}// Clean and readable!
$posts = Post::published()->recent()->take(5)->get();
// With parameter
$drafts = Post::ofStatus('draft')->get();4. Accessors & Mutators
Accessors format data when you read it. Mutators format data when you save it.
use Illuminate\Database\Eloquent\Casts\Attribute;
class User extends Model
{
// Accessor: auto-capitalize name when reading
protected function name(): Attribute
{
return Attribute::make(
get: fn (string $value) => ucwords($value),
);
}
// Mutator: auto-hash password when saving
protected function password(): Attribute
{
return Attribute::make(
set: fn (string $value) => bcrypt($value),
);
}
// Both accessor + mutator together
protected function email(): Attribute
{
return Attribute::make(
get: fn (string $value) => strtolower($value),
set: fn (string $value) => strtolower($value),
);
}
}$user = User::find(1);
// Accessor runs automatically
echo $user->name; // "John Doe" (even if stored as "john doe")
// Mutator runs automatically
$user->password = 'secret123'; // Stored as hashed value5. Soft Deletes
Instead of permanently removing records, soft deletes mark them as "deleted" by setting a deleted_at timestamp. The data stays in the database.
// Add to your migration
$table->softDeletes(); // Adds 'deleted_at' columnuse Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use SoftDeletes;
}$post->delete(); // Soft delete (sets deleted_at)
Post::withTrashed()->get(); // Include soft-deleted records
Post::onlyTrashed()->get(); // Only soft-deleted records
$post->restore(); // Undo soft delete
$post->forceDelete(); // Permanently delete from DB6. Common Query Patterns
// Pagination (15 per page)
$posts = Post::paginate(15);
// Select specific columns only
$posts = Post::select('id', 'title')->get();
// Where with multiple conditions
$posts = Post::where('status', 'published')
->where('views', '>', 100)
->get();
// Where IN
$posts = Post::whereIn('id', [1, 2, 3])->get();
// Where between dates
$posts = Post::whereBetween('created_at', [
'2025-01-01', '2025-12-31'
])->get();
// Search with LIKE
$posts = Post::where('title', 'like', '%laravel%')->get();
// Aggregate functions
$total = Post::count();
$average = Post::avg('views');
$max = Post::max('views');
$sum = Post::sum('views');
// Chunk large datasets (process 100 at a time)
Post::chunk(100, function ($posts) {
foreach ($posts as $post) {
// Process each post
}
});Summary
- βModels β PHP classes that represent database tables
- βCRUD β create, read, update, delete with simple methods
- βScopes β reusable query conditions inside models
- βAccessors & Mutators β auto-format data on read/write
- βSoft Deletes β safe deletion without losing data
- βQuery Patterns β pagination, search, aggregates, chunking