From e51678b21c1698bae45e6d952787f01778b4910d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Mon, 2 Jun 2025 02:52:42 +0200 Subject: [PATCH] minor refactor in PathIdentificationTest, expand CLAUDE.md to include early identification section --- CLAUDE.md | 30 ++++++++++++++++++++++++++++++ tests/PathIdentificationTest.php | 12 ++++++------ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index b23187f3..bb6dedac 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -77,6 +77,36 @@ All of these work as flags, i.e. middleware groups that are empty arrays with a - `universal` - Routes working in both contexts - `clone` - Tells route cloning logic to clone the route +### Early Identification + +**Early identification** ensures tenancy is initialized before controller instantiation, which is critical for certain scenarios. + +**When needed:** +- Controllers using constructor dependency injection +- Integration with packages that inject dependencies in constructors + +**The Problem:** +Laravel executes controller constructors and route model binding before route-level middleware runs, causing services to use central context instead of tenant context. + +**Solutions:** +1. **Avoid Constructor Injection** - Use method injection instead +2. **Laravel's Native Solution** - Use controllers that implement `HasMiddleware` interface +3. **Kernel Identification** - Add middleware to HTTP Kernel's global stack: + +```php +// In HttpKernel.php +protected $middleware = [ + \Stancl\Tenancy\Middleware\InitializeTenancyByDomain::class, + // other middleware... +]; +``` + +Note you also need to flag the route with the `'tenant'` middleware if default route mode (set in config) isn't set to TENANT. + +**Benefits:** +- Constructor dependency injection receives tenant-aware services +- Seamless integration with existing Laravel applications + ### Testing Environment Tests use Docker with MySQL/PostgreSQL/Redis. The `./test` script runs Pest tests inside containers with proper database isolation. diff --git a/tests/PathIdentificationTest.php b/tests/PathIdentificationTest.php index 1df74092..79cd3816 100644 --- a/tests/PathIdentificationTest.php +++ b/tests/PathIdentificationTest.php @@ -35,6 +35,11 @@ beforeEach(function () { }); }); +afterEach(function () { + InitializeTenancyByPath::$onFail = null; + Tenant::$extraCustomColumns = []; +}); + test('tenant can be identified by path', function () { Tenant::create([ 'id' => 'acme', @@ -150,6 +155,7 @@ test('central route can have a parameter with the same name as the tenant parame config(['tenancy.identification.resolvers.' . PathTenantResolver::class . '.tenant_parameter_name' => 'team']); $tenantKey = Tenant::create()->getTenantKey(); + // The route is flagged as central (while using kernel identification) so the {team} parameter should not be used for tenancy initialization Route::get('/central/route/{team}/{a}/{b}', function ($team, $a, $b) { return "$a + $b + $team"; })->middleware('central')->name('central-route'); @@ -185,8 +191,6 @@ test('the tenant model column can be customized in the config', function () { $this->withoutExceptionHandling(); pest()->get('/acme/foo')->assertSee($tenant->getTenantKey()); expect(fn () => pest()->get($tenant->id . '/foo'))->toThrow(TenantCouldNotBeIdentifiedByPathException::class); - - Tenant::$extraCustomColumns = []; // static property reset }); test('the tenant model column can be customized in the route definition', function () { @@ -218,8 +222,6 @@ test('the tenant model column can be customized in the route definition', functi // Binding field defined pest()->get('/acme/bar')->assertSee($tenant->getTenantKey()); expect(fn () => pest()->get($tenant->id . '/bar'))->toThrow(TenantCouldNotBeIdentifiedByPathException::class); - - Tenant::$extraCustomColumns = []; // static property reset }); test('any extra model column needs to be whitelisted', function () { @@ -243,6 +245,4 @@ test('any extra model column needs to be whitelisted', function () { // After whitelisting the column it works config(['tenancy.identification.resolvers.' . PathTenantResolver::class . '.allowed_extra_model_columns' => ['slug']]); pest()->get('/acme/foo')->assertSee($tenant->getTenantKey()); - - Tenant::$extraCustomColumns = []; // static property reset });