Master advanced Eloquent scopes to encapsulate complex business logic, chain query filters, and maintain clean, expressive models in your Laravel SaaS platform.
Previously in this course, we explored database query caching layers to reduce load on our primary instances. In this lesson, we shift our focus from the database layer back to the application layer to address code maintainability. We will master Advanced Eloquent Scopes, enabling you to encapsulate complex business logic into reusable, chainable query constraints.
In a high-traffic SaaS platform, controllers often become cluttered with complex where clauses, orWhere logic, and date filtering. When this logic is repeated across different endpoints or services, it violates the DRY principle and makes cross-cutting changes (like updating a business rule) a nightmare.
Eloquent scopes allow us to move this logic directly into our models, transforming messy query builders into readable, intent-revealing method chains. As we've discussed when refactoring monolithic components, moving logic closer to the data is a cornerstone of clean architecture.
Local scopes are defined in your model and prefixed with scope. They receive the $query instance as their first argument, allowing you to manipulate the underlying Builder object.
For our SaaS project, let's look at an Invoice model that needs to filter by status, date ranges, and payment types.
PHPnamespace App\Models; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; class Invoice extends Model { #6A9955">/** * Scope a query to only include overdue invoices. */ public function scopeOverdue(Builder $query): void { $query->where('due_at', '<', now()) ->where('status', 'pending'); } #6A9955">/** * Scope a query to filter by amount range. */ public function scopeWithAmountBetween(Builder $query, float $min, float $max): void { $query->whereBetween('amount', [$min, $max]); } }
By defining these as scopes, your controller logic simplifies from raw SQL-like builders to expressive business language:
PHP#6A9955">// Usage in a Service or Controller $invoices = Invoice::overdue() ->withAmountBetween(100, 500) ->get();
The real power of Eloquent scopes lies in their composability. When building a reporting engine, you can chain multiple scopes to construct complex SQL dynamically based on user input.
However, be careful with state. Always ensure your scopes return void or the $query instance, and never execute the query inside the scope itself (e.g., don't call ->get() or ->first() inside a scope).
When dealing with complex filters, use a "Filter" class or a dedicated trait to avoid bloating your model. If you are interested in how this integrates with wider model cleanup, see mastering Laravel traits for cleaner Eloquent models.
While local scopes are for specific queries, global scopes apply to every query made on a model. As seen in our work on multi-tenant security, global scopes are essential for enforcing data isolation.
| Scope Type | Use Case | Implementation |
|---|---|---|
| Local | Ad-hoc queries, business-specific filters | scopeName($query, ...) |
| Global | Soft deletes, multi-tenancy, data isolation | implements Scope class |
Subscription model in the SaaS project.scopeActiveTrial that filters for users currently in their trial period (trial_ends_at is in the future).scopeExpiringInDays(Builder $query, int $days) that finds subscriptions expiring within a specific window.Builder instance.Advanced Eloquent scopes are a primary tool for maintaining readability in large Laravel applications. By encapsulating domain logic, you ensure that query constraints are consistent, testable, and reusable. Remember that while scopes make code cleaner, they must still be supported by proper database indexing and should not replace dedicated service-layer logic for complex orchestration.
Up next: We will tackle Distributed Locks to prevent race conditions when multiple workers attempt to process the same invoice or subscription update simultaneously.
Master Action Classes to remove business logic from your controllers. Learn to build testable, single-purpose classes for a scalable Laravel architecture.
Read moreMass assignment is a critical security vulnerability where attackers inject unauthorized fields into your database. Learn to harden your Laravel models today.
Advanced Eloquent Scopes
Custom Middleware Development
Database Connection Pooling
Handling Large Data Exports
Security Header Configuration
Database Sharding Concepts
Real-time Data Synchronization
Database Deadlock Prevention
Managing Third-Party API Integrations