Good Code
The good version keeps a repeated filter close to the model and lets callers compose it with eager loading, ordering, or pagination.
Lesson 05
Put repeated Eloquent filters into query scopes and keep collection filtering out of request paths.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
final class Review extends Model
{
public function scopePublished(Builder $query): Builder
{
// The database owns the repeated published-review filter.
return $query->where('status', 'published')
->whereNotNull('published_at');
}
}<?php
public function __invoke()
{
// Pulling every row first makes filtering and lazy loading happen in PHP.
return Review::all()
->filter(fn (Review $review) => $review->status === 'published')
->map(fn (Review $review) => [$review->title, $review->author->name]);
}The good version keeps a repeated filter close to the model and lets callers compose it with eager loading, ordering, or pagination.
The bad version fetches all rows and filters in memory. It also risks hidden N+1 queries through lazy-loaded relations.