Stancl Tenancy v3 — 多租户 Laravel
Comprehensive agent skill for building multi-tenant SaaS applications with stancl/tenancy v3 (^3.9). Covers the full package API surface, real-world production patterns, and critical gotchas.
What this skill covers
- Package architecture — central vs tenant contexts, automatic/manual tenancy modes, tenant model patterns
- Tenant identification —
InitializeTenancyByDomain,InitializeTenancyBySubdomain,InitializeTenancyByPath,InitializeTenancyByRequestDatamiddleware with custom$onFailhandling - Data isolation — multi-database (per-tenant databases) and single-database (
BelongsToTenant,BelongsToPrimaryModel,HasScopedValidationRules, unique index scoping,withoutTenancy()) - Bootstrappers — database, cache, filesystem, queue, Redis bootstrappers + patterns for custom bootstrappers
- Features —
TenantConfig(per-tenant Laravel config),UserImpersonation,TelescopeTags,UniversalRoutes - Event system — full lifecycle events with
JobPipelinefor sequential job execution - Console commands —
tenants:migrate,tenants:seed,tenants:rollback,tenants:list,tenants:runwith--tenants=<id>filtering - Production patterns — queue isolation strategies (tenant-specific queues, priority queues, dedicated workers), idempotent provisioning, deployment-scale migrations, Horizon job tagging, Scout Meilisearch index scoping, Spatie package integration, Livewire tenant routing
- Testing — fast transaction-based testing (25x speed improvement),
Event::fake()caveats, event faking for isolation - Critical gotchas — queued events +
SerializesModelstrap (never pass tenant-scoped models),DatabaseBatchRepositorystale connection fix, bootstrapper prefix accumulation in long-running processes
Common patterns
// Tenant model with domains and databases
class Tenant extends BaseTenant implements TenantWithDatabase
{
use HasDatabase, HasDomains;
public function getCustomColumns(): array
{
return ['id', 'name', 'plan_id', 'status'];
}
}
// Event-driven tenant lifecycle
Event::listen(TenantCreated::class, JobPipeline::make([
CreateDatabase::class,
MigrateDatabase::class,
SeedDatabase::class,
])->send(fn ($event) => $event->tenant)->toListener());
// Manual tenancy control
$tenant->run(fn () => User::create([...]));
tenancy()->initialize($tenant);
tenancy()->end();
// Custom bootstrapper
class ScoutTenancyBootstrapper implements TenancyBootstrapper
{
public function bootstrap(Tenant $tenant): void { /* scope Meilisearch */ }
public function revert(): void { /* restore central config */ }
}
// Single-database scoping
class Post extends Model { use BelongsToTenant; }
class Comment extends Model { use BelongsToPrimaryModel; }
Critical Gotchas
Queued events + SerializesModels: Never pass tenant-scoped models in queued payloads. BelongsToTenant's global scope fires before QueueTenancyBootstrapper restores context. Pass scalars (tenantId, modelId) and call tenancy()->initialize() in handle().
DatabaseBatchRepository: DB::purge('tenant') between jobs nulls the PDO. Use houlokmah/tenancy-batch-fix for batched jobs across tenants.
Prefix accumulation: Cache/Redis/filesystem bootstrappers can accumulate prefixes across tenant switches in long-running processes (Horizon, Octane). Always test after multiple cycles.
For full patterns including queue isolation strategies, transaction-based testing (25x speed), and provisioning safety, see references/production-patterns.md in the skill source.
Installation
OpenClaw
clawhub install tenancy-development
Direct repo/manual install
git clone https://github.com/1991513ccie-png/skills
cp -R tenancy-development ~/.claude/skills/tenancy-development
Documentation Links
- Official docs: https://tenancyforlaravel.com/docs/v3/
- Upstream repo: https://github.com/1991513ccie-png/skills
- Skill source: https://github.com/1991513ccie-png/skills