| name | eloquent-best-practices |
| description | Best practices for Laravel Eloquent ORM including query optimization, relationship management, and avoiding common pitfalls like N+1 queries. |
Eloquent Best Practices
Query Optimization
Always Eager Load Relationships
$posts = Post::all();
foreach ($posts as $post) {
echo $post->user->name;
}
$posts = Post::with('user')->get();
foreach ($posts as $post) {
echo $post->user->name;
}
Select Only Needed Columns
$users = User::all();
$users = User::select(['id', 'name', 'email'])->get();
$posts = Post::with(['user:id,name'])->select(['id', 'title', 'user_id'])->get();
Use Query Scopes
class Post extends Model
{
public function scopePublished($query)
{
return $query->where('status', 'published')
->whereNotNull('published_at');
}
public function scopePopular($query, $threshold = 100)
{
return $query->where('views', '>', $threshold);
}
}
$posts = Post::published()->popular()->get();
Relationship Best Practices
Define Return Types
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Post extends Model
{
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function comments(): HasMany
{
return $this->hasMany(Comment::class);
}
}
Use withCount for Counts
foreach ($posts as $post) {
echo $post->comments()->count();
}
$posts = Post::withCount('comments')->get();
foreach ($posts as $post) {
echo $post->comments_count;
}
Mass Assignment Protection
class Post extends Model
{
protected $fillable = ['title', 'content', 'status'];
protected $guarded = ['id', 'user_id'];
}
Use Casts for Type Safety
class Post extends Model
{
protected $casts = [
'published_at' => 'datetime',
'metadata' => 'array',
'is_featured' => 'boolean',
'views' => 'integer',
];
}
Chunking for Large Datasets
Post::chunk(200, function ($posts) {
foreach ($posts as $post) {
}
});
Post::lazy()->each(function ($post) {
});
Database-Level Operations
$posts = Post::where('status', 'draft')->get();
foreach ($posts as $post) {
$post->update(['status' => 'archived']);
}
Post::where('status', 'draft')->update(['status' => 'archived']);
Post::where('id', $id)->increment('views');
Use Model Events Wisely
class Post extends Model
{
protected static function booted()
{
static::creating(function ($post) {
$post->slug = Str::slug($post->title);
});
static::deleting(function ($post) {
$post->comments()->delete();
});
}
}
Common Pitfalls to Avoid
Don't Query in Loops
foreach ($userIds as $id) {
$user = User::find($id);
}
$users = User::whereIn('id', $userIds)->get();
Don't Forget Indexes
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->index();
$table->string('slug')->unique();
$table->string('status')->index();
$table->timestamp('published_at')->nullable()->index();
$table->index(['status', 'published_at']);
});
Prevent Lazy Loading in Development
Model::preventLazyLoading(!app()->isProduction());
Checklist