php 38 lines · 5 steps

How a tenant global scope works in Laravel

A reusable Eloquent scope that silently filters every query by the current tenant and adds escape-hatch query macros.

Explained by highlit
1<?php
2 
3namespace App\Models\Scopes;
4 
5use Illuminate\Database\Eloquent\Builder;
6use Illuminate\Database\Eloquent\Model;
7use Illuminate\Database\Eloquent\Scope;
8 
9class TenantScope implements Scope
10{
11 public function apply(Builder $builder, Model $model): void
12 {
13 if ($tenantId = app('tenant')?->id) {
14 $builder->where($model->qualifyColumn('tenant_id'), $tenantId);
15 }
16 }
17 
18 public function extend(Builder $builder): void
19 {
20 $builder->macro('withoutTenant', function (Builder $builder) {
21 return $builder->withoutGlobalScope($this);
22 });
23 
24 $builder->macro('forTenant', function (Builder $builder, $tenant) {
25 $tenantId = $tenant instanceof Model ? $tenant->getKey() : $tenant;
26 
27 return $builder->withoutGlobalScope($this)
28 ->where($builder->getModel()->qualifyColumn('tenant_id'), $tenantId);
29 });
30 }
31 
32 public function creating(Model $model): void
33 {
34 if (! $model->getAttribute('tenant_id') && $tenantId = app('tenant')?->id) {
35 $model->setAttribute('tenant_id', $tenantId);
36 }
37 }
38}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Global scopes centralize a where-clause so every query on a model is filtered without touching call sites.
  2. 2Macros extend the query builder with named overrides, giving controlled escape hatches from an otherwise-automatic scope.
  3. 3Pairing a scope with a creating hook keeps reads and writes consistent — rows are both filtered and stamped with the tenant.

Related explainers

Share this explainer

Here's the card — post it anywhere.

How a tenant global scope works in Laravel — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code