Certainly! In Episode 10, Jeremy demonstrates filtering within the controller or directly on the Eloquent model, often resulting in multiple if conditions to customize queries based on HTTP request input. While this works, extracting filtering logic into a dedicated service class (often called a filter or query class) leads to a more maintainable and reusable codebase—especially as filtering grows in complexity.
Why Use a Service Class?
- Separation of concerns: Keeps controllers slim.
- Testability: Business logic is easier to test in isolation.
- Reusability: Filtering logic can be reused elsewhere (web, API, jobs).
Example Approach
Let’s imagine a TicketFilter class which consolidates request-based filtering for Ticket queries:
// app/Filters/TicketFilter.php
namespace App\Filters;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
class TicketFilter
{
protected $request;
protected $builder;
protected $filters = ['status', 'user_id', 'priority'];
public function __construct(Request $request)
{
$this->request = $request;
}
public function apply(Builder $builder)
{
$this->builder = $builder;
foreach ($this->filters as $filter) {
if (method_exists($this, $filter) && $this->request->filled($filter)) {
$this->$filter($this->request->input($filter));
}
}
return $this->builder;
}
protected function status($value)
{
$this->builder->where('status', $value);
}
protected function user_id($value)
{
$this->builder->where('user_id', $value);
}
protected function priority($value)
{
$this->builder->where('priority', $value);
}
}
Usage in Controller:
use App\Filters\TicketFilter;
use App\Models\Ticket;
use Illuminate\Http\Request;
public function index(Request $request, TicketFilter $filter)
{
$tickets = $filter->apply(Ticket::query())->get();
return response()->json($tickets);
}
Or, using the when() method directly for simple cases:
public function index(Request $request)
{
$tickets = Ticket::query()
->when($request->filled('status'), fn($q) => $q->where('status', $request->status))
->when($request->filled('user_id'), fn($q) => $q->where('user_id', $request->user_id))
->get();
return response()->json($tickets);
}
Conclusion: Jeremy’s inline approach is more direct for small APIs, but if you need scalable, DRY, and testable code, using a dedicated service/filter class is an excellent idea—your instinct is correct!
Let me know if you want an even more advanced example, such as using Laravel query scopes or packages like spatie/laravel-query-builder to automate and streamline these patterns further.