
Understanding Eloquent ORM in Laravel
Eloquent ORM is Laravel's built-in Object-Relational Mapping (ORM) system. It provides an beautiful, simple ActiveRecord implementation for working with your database. With Eloquent, each database table has a corresponding "Model" which is used to interact with that table. This comprehensive guide will walk you through everything you need to know about Eloquent ORM.
What is Eloquent ORM?
Eloquent is a powerful ORM that allows you to interact with your database using expressive PHP syntax instead of writing raw SQL queries. It abstracts away the complexity of database operations and provides a clean, object-oriented interface for working with your data.
Basic Model Creation
Creating a Model
Create models using Artisan commands:
// Create a basic model
php artisan make:model User
// Create a model with migration
php artisan make:model User -m
// Create a model with controller
php artisan make:model User --controller
// Create a model with migration and controller
php artisan make:model User -m --controller
// Create a model with factory and seeder
php artisan make:model User -f --seed
Basic Model Structure
Here's what a basic Eloquent model looks like:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class User extends Model
{
use HasFactory, SoftDeletes;
// Specify which fields are mass assignable
protected $fillable = [
'name',
'email',
'password',
'role'
];
// Hide sensitive fields from JSON output
protected $hidden = [
'password',
'remember_token'
];
// Specify custom primary key
protected $primaryKey = 'user_id';
// Specify if auto-incrementing is disabled
public $incrementing = false;
// Specify custom primary key type
protected $keyType = 'string';
// Specify custom table name
protected $table = 'users';
// Enable or disable timestamps
public $timestamps = true;
// Custom timestamp columns
const CREATED_AT = 'created_at';
const UPDATED_AT = 'updated_at';
}
Basic Database Operations
Creating Records
Insert new records into the database:
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
class UserController extends Controller
{
public function create()
{
// Create a single record
$user = User::create([
'name' => 'John Doe',
'email' => '[email protected]',
'password' => bcrypt('password123')
]);
// Create multiple records
$users = User::insert([
['name' => 'Jane Smith', 'email' => '[email protected]', 'password' => bcrypt('password123')],
['name' => 'Bob Johnson', 'email' => '[email protected]', 'password' => bcrypt('password123')]
]);
// Create or update
$user = User::updateOrCreate(
['email' => '[email protected]'],
['name' => 'John Updated Doe']
);
return 'User created successfully!';
}
}
Reading Records
Retrieve records from the database:
// Get all records
$users = User::all();
// Get first record
$user = User::first();
// Find by primary key
$user = User::find(1);
// Find or fail
$user = User::findOrFail(1);
// Find by condition
$user = User::where('email', '[email protected]')->first();
// Get with conditions
$activeUsers = User::where('active', true)->get();
// Get with multiple conditions
$users = User::where('active', true)
->where('role', 'admin')
->get();
// Get with ordering
$users = User::orderBy('created_at', 'desc')->get();
// Limit and offset
$users = User::skip(10)->take(5)->get();
// Get count
$count = User::count();
// Check if records exist
$exists = User::where('email', '[email protected]')->exists();
// Get first or create
$user = User::firstOrCreate(
['email' => '[email protected]'],
['name' => 'John Doe']
);
Updating Records
Update existing records:
// Update single record
$user = User::find(1);
$user->update([
'name' => 'Updated Name',
'email' => '[email protected]'
]);
// Update multiple records
User::where('active', false)
->update(['active' => true]);
// Mass update
$affected = User::where('votes', '>', 100)
->update(['status' => 'VIP']);
// Increment/decrement
$user = User::find(1);
$user->increment('login_count');
$user->decrement('login_count');
// Increment with amount
$user->increment('balance', 100);
// Decrement with amount
$user->decrement('balance', 50);
Deleting Records
Delete records from the database:
// Delete single record
$user = User::find(1);
$user->delete();
// Delete by condition
User::where('active', false)->delete();
// Force delete (ignores soft deletes)
User::where('deleted_at', '!=', null)->forceDelete();
// Mass delete
$deleted = User::where('created_at', '<', '2023-01-01')->delete();
// Restore soft deleted records
User::withTrashed()->where('deleted_at', '!=', null)->restore();
Query Builder Methods
Where Clauses
Advanced filtering with where clauses:
// Basic where
$users = User::where('active', true)->get();
// Multiple where
$users = User::where('active', true)->where('role', 'admin')->get();
// Or where
$users = User::where('active', true)->orWhere('role', 'admin')->get();
// Where between
$users = User::whereBetween('age', [18, 30])->get();
// Where not between
$users = User::whereNotBetween('age', [18, 30])->get();
// Where in
$users = User::whereIn('id', [1, 2, 3, 4, 5])->get();
// Where not in
$users = User::whereNotIn('id', [1, 2, 3, 4, 5])->get();
// Where null
$users = User::where('email_verified_at', null)->get();
// Where not null
$users = User::whereNotNull('email_verified_at')->get();
// Where has (for relationships)
$users = User::whereHas('posts', function($query) {
$query->where('published', true);
})->get();
// Where has multiple
$users = User::whereHas('posts', function($query) {
$query->where('published', true);
})->whereHas('comments', function($query) {
$query->where('approved', true);
})->get();
Advanced Query Methods
Complex query operations:
// Select specific columns
$users = User::select('name', 'email')->get();
// Select with alias
$users = User::select('name as user_name', 'email')->get();
// Distinct
$users = User::select('role')->distinct()->get();
// Group by
$users = User::select('role')
->selectRaw('count(*) as user_count')
->groupBy('role')
->get();
// Having
$users = User::select('role')
->selectRaw('count(*) as user_count')
->groupBy('role')
->having('user_count', '>', 5)
->get();
// Join
$users = User::join('posts', 'users.id', '=', 'posts.user_id')
->select('users.name', 'posts.title')
->get();
// Left join
$users = User::leftJoin('posts', 'users.id', '=', 'posts.user_id')
->select('users.name', 'posts.title')
->get();
// Multiple joins
$users = User::join('posts', 'users.id', '=', 'posts.user_id')
->join('categories', 'posts.category_id', '=', 'categories.id')
->select('users.name', 'posts.title', 'categories.name as category')
->get();
Model Relationships
One to One Relationship
Define one-to-one relationships:
<?php
// In Profile model
class Profile extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
// In User model
class User extends Model
{
public function profile()
{
return $this->hasOne(Profile::class);
}
}
// Usage
$user = User::find(1);
$profile = $user->profile;
// Or inverse
$profile = Profile::find(1);
$user = $profile->user;
// Eager loading
$user = User::with('profile')->find(1);
One to Many Relationship
Define one-to-many relationships:
<?php
// In Post model
class Post extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
public function comments()
{
return $this->hasMany(Comment::class);
}
}
// In User model
class User extends Model
{
public function posts()
{
return $this->hasMany(Post::class);
}
public function comments()
{
return $this->hasMany(Comment::class);
}
}
// Usage
$user = User::find(1);
$posts = $user->posts;
$comments = $user->comments;
// For a specific post
$post = Post::find(1);
$user = $post->user;
$postComments = $post->comments;
// With constraints
$recentPosts = $user->posts()->where('created_at', '>=', '2023-01-01')->get();
// Count relationships
$postCount = $user->posts()->count();
Many to Many Relationship
Define many-to-many relationships:
<?php
// In Post model
class Post extends Model
{
public function categories()
{
return $this->belongsToMany(Category::class);
}
public function tags()
{
return $this->belongsToMany(Tag::class);
}
}
// In Category model
class Category extends Model
{
public function posts()
{
return $this->belongsToMany(Post::class);
}
}
// In Tag model
class Tag extends Model
{
public function posts()
{
return $this->belongsToMany(Post::class);
}
}
// Usage
$post = Post::find(1);
$categories = $post->categories;
$tags = $post->tags;
// For a specific category
$category = Category::find(1);
$posts = $category->posts;
// Attach relationships
$post->categories()->attach([1, 2, 3]);
$post->tags()->sync([1, 2, 3]);
// Detach relationships
$post->categories()->detach([1, 2]);
$post->categories()->detach(); // Detach all
// Toggle relationships
$post->categories()->toggle([1, 2]);
// Update pivot table data
$post->categories()->sync([1 => ['featured' => true], 2 => ['featured' => false]]);
Accessors and Mutators
Accessors
Modify data when retrieving from the database:
<?php
class User extends Model
{
// Accessor for name
public function getFirstNameAttribute($value)
{
return ucfirst($value);
}
// Accessor for full name
public function getFullNameAttribute()
{
return $this->first_name . ' ' . $this->last_name;
}
// Accessor for age group
public function getAgeGroupAttribute()
{
if ($this->age < 18) {
return 'Minor';
} elseif ($this->age < 30) {
return 'Young Adult';
} elseif ($this->age < 50) {
return 'Adult';
} else {
return 'Senior';
}
}
// Accessor for password (hash automatically)
public function setPasswordAttribute($value)
{
$this->attributes['password'] = bcrypt($value);
}
// Accessor for email (lowercase)
public function setEmailAttribute($value)
{
$this->attributes['email'] = strtolower($value);
}
}
// Usage
$user = User::find(1);
echo $user->first_name; // Automatically capitalized
echo $user->full_name; // "John Doe"
echo $user->age_group; // "Young Adult"
Scopes
Global Scopes
Apply conditions to all queries:
<?php
class ActiveUserScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
$builder->where('active', true);
}
}
// Apply to model
class User extends Model
{
protected static function boot()
{
parent::boot();
static::addGlobalScope(new ActiveUserScope());
}
}
Local Scopes
Reusable query constraints:
<?php
class User extends Model
{
// Local scope for active users
public function scopeActive($query)
{
return $query->where('active', true);
}
// Local scope for admin users
public function scopeAdmin($query)
{
return $query->where('role', 'admin');
}
// Local scope for recent users
public function scopeRecent($query, $days = 7)
{
return $query->where('created_at', '>=', now()->subDays($days));
}
// Local scope for users with age
public function scopeWithAge($query, $minAge, $maxAge)
{
return $query->whereBetween('age', [$minAge, $maxAge]);
}
}
// Usage
$activeUsers = User::active()->get();
$adminUsers = User::admin()->get();
$recentUsers = User::recent(30)->get(); // 30 days
$usersByAge = User::withAge(18, 30)->get();
// Chain scopes
$activeAdmins = User::active()->admin()->get();
Collections and JSON
Working with Collections
Powerful collection methods:
// Map through collection
$names = User::all()->map(function($user) {
return $user->name;
});
// Filter collection
$activeUsers = User::all()->filter(function($user) {
return $user->active;
});
// Sort collection
$sortedUsers = User::all()->sortBy('name');
// Group collection
$usersByRole = User::all()->groupBy('role');
// Chunk collection
$chunks = User::all()->chunk(10);
// Find in collection
$user = User::all()->first(function($user) {
return $user->email === '[email protected]';
});
// Check conditions
$hasActiveUsers = User::all()->contains(function($user) {
return $user->active;
});
// Sum values
$totalAge = User::all()->sum('age');
// Average values
$averageAge = User::all->avg('age');
// Get unique values
$uniqueRoles = User::all->unique('role');
JSON Serialization
Convert models to JSON:
// Convert single model to JSON
$user = User::find(1);
$json = $user->toJson();
// Convert collection to JSON
$users = User::all();
$json = $users->toJson();
// Convert to array
$userArray = $user->toArray();
// Convert collection to array
$usersArray = $users->toArray();
// Custom JSON serialization
class User extends Model
{
protected $appends = ['full_name'];
public function getFullNameAttribute()
{
return $this->first_name . ' ' . $this->last_name;
}
}
// Usage
$user = User::find(1);
echo $user->full_name; // Available in JSON output
Conclusion
Eloquent ORM is one of Laravel's most powerful features. By mastering these concepts and techniques, you can build complex database-driven applications with clean, maintainable code. Remember to follow best practices like using proper relationships, accessors, mutators, and scopes to keep your code organized and efficient.
Key takeaways:
- Use models as the primary interface to your database
- Leverage relationships for complex data structures
- Use accessors and mutators for data transformation
- Apply scopes for reusable query constraints
- Work with collections for powerful data manipulation
- Follow naming conventions and best practices
With Eloquent ORM, you can build sophisticated applications while writing clean, expressive PHP code. Happy coding! 🚀