Mahamudul Hasan Rubel
HomeBlogCoursesAboutProjectsSkillsExperiencePhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • Blog
  • Courses
  • About
  • Projects
  • Skills
  • Experience
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

Subscribe to the newsletter

Get new articles and course lessons delivered to your inbox. No spam, unsubscribe anytime.

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
Lesson 7 of the Advanced Laravel: Architecture, Scaling & Performance course
LaravelJune 27, 20264 min read

Querying with Strict Eloquent: Preventing N+1 and Data Leaks

Stop N+1 issues and unauthorized data access in their tracks. Learn to configure strict Eloquent modes and global scopes for a rock-solid production architecture.

LaravelEloquentPerformanceN+1Securityphpbackend

Previously in this course, we discussed the Service Layer Pattern to decouple our business logic from the infrastructure. While that pattern protects our domain, the underlying data access layer remains vulnerable to runtime errors like lazy loading and cross-tenant data leakage. This lesson adds a layer of "defensive database programming" to your toolkit, ensuring that your application fails loudly during development rather than silently failing in production.

The Case for Strict Eloquent

In a high-traffic SaaS, performance is often lost in small, silent increments. An N+1 query might be undetectable during local development with a small dataset but can cripple your database under load. Similarly, forgetting a where('tenant_id', ...) clause in a complex query is a catastrophic security vulnerability.

Laravel’s "Strict" modes are designed to turn these silent killers into immediate exceptions. By enabling them, you shift the burden of catching these bugs from your QA team to your test suite.

Enabling Strict Loading

Strict loading prevents the "lazy loading" of relationships, which is the primary driver of the N+1 problem. When enabled, any attempt to access a relationship that hasn't been eager-loaded will throw an Illuminate\Database\LazyLoadingViolationException.

To enable this globally, you typically do this in the boot method of your AppServiceProvider:

PHP
use Illuminate\Database\Eloquent\Model;

public function boot(): void
{
    #6A9955">// Enable strict mode in local/testing environments
    Model::preventLazyLoading(!app()->isProduction());
    
    #6A9955">// Optional: Log violations in production instead of throwing
    #6A9955">// Model::preventLazyLoading(app()->isProduction());
}

By setting this to !app()->isProduction(), you ensure that your developers are forced to use with() or load() during the development lifecycle. If a developer writes $user->profile->avatar without eager loading profile, the test suite will fail immediately.

Enforcing Data Isolation with Global Scopes

In our SaaS platform project, data leakage between tenants is a non-negotiable failure. While you could manually add a where('tenant_id', ...) to every query, human error makes this approach risky. Global Scopes allow you to inject this constraint automatically into every Eloquent query for a specific model.

Here is how we implement a TenantScope for our Subscription model:

PHP
namespace App\Models\Scopes;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Support\Facades\Auth;

class TenantScope implements Scope
{
    public function apply(Builder $builder, Model $model): void
    {
        #6A9955">// Ensure we only retrieve records for the authenticated tenant
        if (Auth::check()) {
            $builder->where('tenant_id', Auth::user()->tenant_id);
        }
    }
}

Now, apply it to the model:

PHP
namespace App\Models;

use App\Models\Scopes\TenantScope;
use Illuminate\Database\Eloquent\Model;

class Subscription extends Model
{
    protected static function booted(): void
    {
        static::addGlobalScope(new TenantScope);
    }
}

With this in place, Subscription::all() now automatically executes SELECT * FROM subscriptions WHERE tenant_id = ?. You’ve effectively eliminated the possibility of accidentally leaking data from other tenants in your controllers or service classes.

Hands-on Exercise: Implementing the Guard

  1. Enable Strict Mode: Open your AppServiceProvider and add the Model::preventLazyLoading() call as shown above.
  2. Create a Scope: Generate a new scope class for your Project model in our SaaS project to ensure users only ever see their own projects.
  3. Verify: Write a Feature Test that attempts to access a relationship without eager loading. Assert that a LazyLoadingViolationException is thrown.
  4. Test Isolation: Write a test where you attempt to query a Project belonging to a different tenant. Assert that the record is not found, even without an explicit where clause.

Common Pitfalls

  • The "Production Exception" Trap: Do not simply call Model::preventLazyLoading(true) in production if you haven't audited your entire codebase. It will crash your site for users the moment they hit a page with a missing with() call. Use the environment check or a custom log handler.
  • Global Scope Over-restriction: Global scopes are powerful, but they can be problematic when you need to access cross-tenant data (e.g., in a background job that aggregates statistics across all tenants). Use Model::withoutGlobalScope(TenantScope::class) when you intentionally need to bypass these constraints.
  • Performance Overhead of Scopes: While minor, remember that global scopes are applied to every query, including exists() checks or count(). Ensure your tenant_id column has an index to prevent these automated queries from becoming slow.

Summary

By leveraging preventLazyLoading and GlobalScopes, you transform Eloquent from a helpful abstraction into a strict guardian of your data integrity. This approach prevents common N+1 performance bottlenecks and provides a declarative way to handle multi-tenancy. You are now essentially "baking in" security and performance at the framework level.

Up next: We will dive into Advanced Subqueries and Joins to handle complex reporting requirements that go beyond simple model relationships.

Previous lessonModular Monolith StructureNext lesson Advanced Subqueries and Joins
Back to Blog

Similar Posts

LaravelJune 28, 20264 min read

Mass Assignment Hardening: Securing Eloquent Models

Mass assignment is a critical security vulnerability where attackers inject unauthorized fields into your database. Learn to harden your Laravel models today.

Read more
LaravelJune 28, 20263 min read

Multi-Tenant Security: Eloquent Isolation and Tenant-Aware Scopes

Master multi-tenant security by implementing robust row-level isolation in Laravel. Learn to build tenant-aware Eloquent scopes that prevent cross-tenant leaks.

Part of the course

Advanced Laravel: Architecture, Scaling & Performance

advanced · Lesson 7 of 57

  1. 1

    Transitioning from MVC to DDD

    3 min
  2. 2

    Defining Bounded Contexts

    3 min
  3. 3

    Implementing Action Classes

    3 min
Read more
LaravelJune 27, 20263 min read

Eloquent Caching Strategies: Scaling Laravel Performance

Master Eloquent caching to minimize database hits. Learn how to wrap model lookups in Redis and automate cache invalidation using Laravel's model events.

Read more
4

Utilizing Data Transfer Objects (DTOs)

3 min
  • 5

    Service Layer Pattern

    4 min
  • 6

    Modular Monolith Structure

    3 min
  • 7

    Querying with Strict Eloquent

    4 min
  • 8

    Advanced Subqueries and Joins

    4 min
  • 9

    Raw Expressions for Performance

    4 min
  • 10

    Advanced Indexing Strategies

    4 min
  • 11

    Database Partitioning Techniques

    4 min
  • 12

    Read/Write Database Splitting

    4 min
  • 13

    Handling Multi-Database Connections

    3 min
  • 14

    Eloquent Caching Strategies

    3 min
  • 15

    Queue Worker Prioritization

    4 min
  • 16

    Unique Job Patterns

    4 min
  • 17

    Rate Limiting Background Jobs

    3 min
  • 18

    Event-Driven Architecture

    4 min
  • 19

    Integrating External Message Brokers

    4 min
  • 20

    Distributed Transactions and Sagas

    3 min
  • 21

    Eventual Consistency Patterns

    4 min
  • 22

    Multi-Layered Caching Strategy

    4 min
  • 23

    Cache Tagging and Invalidation

    4 min
  • 24

    Session Persistence in Clusters

    4 min
  • 25

    High-Availability Infrastructure

    4 min
  • 26

    Zero-Downtime Deployment Pipelines

    4 min
  • 27

    Advanced OAuth2 Implementation

    3 min
  • 28

    JWT and Stateless Security

    4 min
  • 29

    Multi-Tenant Security Isolation

    3 min
  • 30

    Defense Against SSRF

    3 min
  • 31

    Mass Assignment Hardening

    4 min
  • 32

    Automated Security Testing

    3 min
  • 33

    Custom Telemetry Design

    3 min
  • 34

    Distributed Tracing

    4 min
  • 35

    Profiling PHP Execution

    3 min
  • 36

    Memory Management in Long-Running Processes

    4 min
  • 37

    Testing DDD Components

    3 min
  • 38

    Contract Testing

    3 min
  • 39

    Handling Large File Uploads

    3 min
  • 40

    Optimizing Asset Pipelines

    4 min
  • 41

    Database Query Caching Layers

    3 min
  • 42

    Advanced Eloquent Scopes

    4 min
  • 43

    Distributed Locks

    3 min
  • 44

    API Versioning Strategies

    4 min
  • 45

    Database Migration Strategies

    4 min
  • 46

    Handling Webhooks Securely

    3 min
  • 47

    Advanced Logging Patterns

    3 min
  • 48

    Database Indexing for Joins

    4 min
  • 49

    Graceful Degradation

    3 min
  • 50

    Custom Middleware Development

    Coming soon
  • 51

    Database Connection Pooling

    Coming soon
  • 52

    Handling Large Data Exports

    Coming soon
  • 53

    Security Header Configuration

    Coming soon
  • 54

    Database Sharding Concepts

    Coming soon
  • 55

    Real-time Data Synchronization

    Coming soon
  • 56

    Database Deadlock Prevention

    Coming soon
  • 57

    Managing Third-Party API Integrations

    Coming soon
  • View full course