Overview
When building features in your multi-tenant application, you’ll need to create tables that are automatically scoped to the current team. This guide shows you the pattern for creating tenant-aware tables.Basic Pattern
Every tenant-scoped table should include ateam_id foreign key that references the teams table.
Step 1: Create Migration
Key Elements
- Foreign Key:
$table->foreignId('team_id') - Constraint:
->constrained()creates the foreign key relationship - Cascade Delete:
->cascadeOnDelete()ensures data cleanup - Index:
$table->index(['team_id', 'created_at'])for performance
Step 2: Create Model with Global Scope
Understanding the Global Scope
The global scope does two important things:1. Automatic Filtering
All queries are automatically filtered by the current team:2. Automatic Assignment
New records getteam_id set automatically:
Common Patterns
Pattern 1: Simple Tenant-Scoped Table
For straightforward tables that belong to a team:Pattern 2: Nested Tenant-Scoped Table
For tables that belong to other tenant-scoped models:Pattern 3: Soft Deletes
For tables that need soft deletion:Pattern 4: Polymorphic Relationships
For tables with polymorphic relationships:Performance Optimization
Adding Composite Indexes
Always add indexes onteam_id combined with frequently queried columns:
Query Optimization
Use eager loading to avoid N+1 queries:Bypassing Global Scope
Sometimes you need to query without the team scope (e.g., admin panel):Conditional Scoping
Only apply scope in specific contexts:Testing
Checklist
When creating a tenant-scoped table, make sure you:- Add
team_idforeign key column - Use
constrained()->cascadeOnDelete() - Add indexes on
[team_id, ...]combinations - Include
team_idin model’s$fillable - Add global scope in
booted()method - Set
team_idautomatically increatingevent - Add
team()relationship method - Test the scoping behavior
- Test automatic
team_idassignment
Next Steps
- Working with Models - Model best practices
- Filament Resources - Creating UI for your tables

