diff --git a/CLAUDE.md b/CLAUDE.md index f74d37a2..b23187f3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,6 +8,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - `composer test` - Run tests without coverage using Docker - `./test tests/TestFile.php` - Run an entire test file - `./t 'test name'` - Run a specific test +- You can append `-v` to get a full stack trace if a test fails due to an exception ### Code Quality - `composer phpstan` - Run PHPStan static analysis (level 8) diff --git a/assets/config.php b/assets/config.php index 13dc416e..3b073c12 100644 --- a/assets/config.php +++ b/assets/config.php @@ -117,7 +117,7 @@ return [ 'cache_store' => null, // null = default ], Resolvers\PathTenantResolver::class => [ - 'tenant_parameter_name' => 'tenant', // todo0 test changing this + 'tenant_parameter_name' => 'tenant', 'tenant_model_column' => null, // null = tenant key 'tenant_route_name_prefix' => 'tenant.', 'allowed_extra_model_columns' => [], // used with binding route fields diff --git a/src/Bootstrappers/UrlGeneratorBootstrapper.php b/src/Bootstrappers/UrlGeneratorBootstrapper.php index 09259145..6c923d21 100644 --- a/src/Bootstrappers/UrlGeneratorBootstrapper.php +++ b/src/Bootstrappers/UrlGeneratorBootstrapper.php @@ -67,13 +67,14 @@ class UrlGeneratorBootstrapper implements TenancyBootstrapper $defaultParameters = $this->originalUrlGenerator->getDefaultParameters(); if (static::$addTenantParameterToDefaults) { + $tenantParameterName = PathTenantResolver::tenantParameterName(); + $defaultParameters = array_merge($defaultParameters, [ - PathTenantResolver::tenantParameterName() => PathTenantResolver::tenantParameterValue($tenant), + $tenantParameterName => PathTenantResolver::tenantParameterValue($tenant), ]); foreach (PathTenantResolver::allowedExtraModelColumns() as $column) { - // todo0 should this be tenantParameterName() concatenated to :$column? - $defaultParameters["tenant:$column"] = $tenant->getAttribute($column); + $defaultParameters["$tenantParameterName:$column"] = $tenant->getAttribute($column); } } diff --git a/tests/Bootstrappers/UrlGeneratorBootstrapperTest.php b/tests/Bootstrappers/UrlGeneratorBootstrapperTest.php index b4b9d40e..0f41dd86 100644 --- a/tests/Bootstrappers/UrlGeneratorBootstrapperTest.php +++ b/tests/Bootstrappers/UrlGeneratorBootstrapperTest.php @@ -143,8 +143,6 @@ test('setting extra model columns sets additional URL defaults', function () { $table->string('slug')->unique(); }); - $this->artisan('tenants:migrate'); - Route::get('/{tenant}/foo/{user}', function (string $user) { return tenant()->getTenantKey() . " $user"; })->middleware([InitializeTenancyByPath::class, 'web'])->name('foo'); @@ -185,8 +183,6 @@ test('changing the tenant model column changes the default value for the tenant $table->string('slug')->unique(); }); - $this->artisan('tenants:migrate'); - Route::get('/{tenant}/foo/{user}', function (string $user) { return tenant()->getTenantKey() . " $user"; })->middleware([InitializeTenancyByPath::class, 'web'])->name('foo'); @@ -204,6 +200,49 @@ test('changing the tenant model column changes the default value for the tenant pest()->get(route('foo', ['bar']))->assertSee(tenant()->getTenantKey() . ' bar'); }); +test('changing the tenant parameter name is respected by the url generator', function () { + Tenant::$extraCustomColumns = ['slug', 'slug2']; + TenancyUrlGenerator::$passTenantParameterToRoutes = false; + UrlGeneratorBootstrapper::$addTenantParameterToDefaults = true; + + config(['tenancy.bootstrappers' => [UrlGeneratorBootstrapper::class]]); + config(['tenancy.identification.resolvers.' . PathTenantResolver::class . '.tenant_parameter_name' => 'team']); + config(['tenancy.identification.resolvers.' . PathTenantResolver::class . '.tenant_model_column' => 'slug']); + config(['tenancy.identification.resolvers.' . PathTenantResolver::class . '.allowed_extra_model_columns' => ['slug2']]); + + Schema::table('tenants', function (Blueprint $table) { + $table->string('slug')->unique(); + $table->string('slug2')->unique(); + }); + + Route::get('/{team}/foo/{user}', function (string $user) { + return tenant()->getTenantKey() . " $user"; + })->middleware([InitializeTenancyByPath::class, 'web'])->name('foo'); + + Route::get('/{team:slug2}/fooslug2/{user}', function (string $user) { + return tenant()->getTenantKey() . " $user"; + })->middleware([InitializeTenancyByPath::class, 'web'])->name('fooslug2'); + + $tenant = Tenant::create(['slug' => 'acme', 'slug2' => 'acme2']); + + // In central context, no URL defaults are applied + expect(route('foo', ['acme', 'bar']))->toBe("http://localhost/acme/foo/bar"); + pest()->get(route('foo', ['acme', 'bar']))->assertSee(tenant()->getTenantKey() . ' bar'); + tenancy()->end(); + + expect(route('fooslug2', ['acme2', 'bar']))->toBe("http://localhost/acme2/fooslug2/bar"); + pest()->get(route('fooslug2', ['acme2', 'bar']))->assertSee(tenant()->getTenantKey() . ' bar'); + tenancy()->end(); + + // In tenant context, URL defaults are applied + tenancy()->initialize($tenant); + expect(route('foo', ['bar']))->toBe("http://localhost/acme/foo/bar"); + pest()->get(route('foo', ['bar']))->assertSee(tenant()->getTenantKey() . ' bar'); + + expect(route('fooslug2', ['bar']))->toBe("http://localhost/acme2/fooslug2/bar"); + pest()->get(route('fooslug2', ['bar']))->assertSee(tenant()->getTenantKey() . ' bar'); +}); + test('url generator can override specific route names', function() { config(['tenancy.bootstrappers' => [UrlGeneratorBootstrapper::class]]);