From 0b248f937dd4799aa42ecdbd2fc73c05ddb943bb Mon Sep 17 00:00:00 2001 From: Massimo Simonini Date: Tue, 21 Nov 2023 02:25:32 +0100 Subject: [PATCH 1/8] Add step option to migrate-fresh command (#1164) --- src/Commands/MigrateFresh.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Commands/MigrateFresh.php b/src/Commands/MigrateFresh.php index 283d70b0..b626c775 100644 --- a/src/Commands/MigrateFresh.php +++ b/src/Commands/MigrateFresh.php @@ -25,6 +25,7 @@ final class MigrateFresh extends Command parent::__construct(); $this->addOption('--drop-views', null, InputOption::VALUE_NONE, 'Drop views along with tenant tables.', null); + $this->addOption('--step', null, InputOption::VALUE_NONE, 'Force the migrations to be run so they can be rolled back individually.'); $this->setName('tenants:migrate-fresh'); } @@ -47,6 +48,7 @@ final class MigrateFresh extends Command $this->info('Migrating.'); $this->callSilent('tenants:migrate', [ '--tenants' => [$tenant->getTenantKey()], + '--step' => $this->option('step'), '--force' => true, ]); }); From d268a06f5d8d98bbd0aa0c6fb7a4381c0bdc5c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Thu, 18 Jan 2024 14:30:40 +0100 Subject: [PATCH 2/8] tests: assert that tenants:run runs only for the specified tenants --- tests/CommandsTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/CommandsTest.php b/tests/CommandsTest.php index d7da0cab..78fc7c0b 100644 --- a/tests/CommandsTest.php +++ b/tests/CommandsTest.php @@ -196,10 +196,12 @@ class CommandsTest extends TestCase { $tenantId1 = Tenant::create()->getTenantKey(); $tenantId2 = Tenant::create()->getTenantKey(); + $tenantId3 = Tenant::create()->getTenantKey(); Artisan::call('tenants:migrate-fresh'); $this->artisan("tenants:run foo --tenants=$tenantId1 --tenants=$tenantId2 --argument='a=foo' --option='b=bar' --option='c=xyz'") ->expectsOutput('Tenant: ' . $tenantId1) - ->expectsOutput('Tenant: ' . $tenantId2); + ->expectsOutput('Tenant: ' . $tenantId2) + ->doesntExpectOutput('Tenant: ' . $tenantId3); } } From 5fe8825f13e2141c4388ae2afec01bd85f2f91ae Mon Sep 17 00:00:00 2001 From: chillbram Date: Thu, 25 Jan 2024 22:34:47 +0100 Subject: [PATCH 3/8] Make universal routes work for controller middleware (#1151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Make universal routes work for controller middleware * add a fallback --------- Co-authored-by: chillbram <7299762+chillbram@users.noreply.github.com> Co-authored-by: Samuel Ć tancl --- src/Features/UniversalRoutes.php | 2 +- tests/UniversalRouteTest.php | 42 ++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/Features/UniversalRoutes.php b/src/Features/UniversalRoutes.php index 6b729962..40acbeae 100644 --- a/src/Features/UniversalRoutes.php +++ b/src/Features/UniversalRoutes.php @@ -35,7 +35,7 @@ class UniversalRoutes implements Feature public static function routeHasMiddleware(Route $route, $middleware): bool { - if (in_array($middleware, $route->middleware(), true)) { + if (in_array($middleware, $route->computedMiddleware ?? $route->middleware(), true)) { return true; } diff --git a/tests/UniversalRouteTest.php b/tests/UniversalRouteTest.php index c0852545..fff7b9f6 100644 --- a/tests/UniversalRouteTest.php +++ b/tests/UniversalRouteTest.php @@ -63,4 +63,46 @@ class UniversalRouteTest extends TestCase ->assertSuccessful() ->assertSee('acme'); } + + /** @test */ + public function universal_route_works_when_middleware_is_inserted_via_controller_middleware() + { + Route::middlewareGroup('universal', []); + config(['tenancy.features' => [UniversalRoutes::class]]); + + Route::get('/foo', [UniversalRouteController::class, 'show']); + + $this->get('http://localhost/foo') + ->assertSuccessful() + ->assertSee('Tenancy is not initialized.'); + + $tenant = Tenant::create([ + 'id' => 'acme', + ]); + $tenant->domains()->create([ + 'domain' => 'acme.localhost', + ]); + + $this->get('http://acme.localhost/foo') + ->assertSuccessful() + ->assertSee('Tenancy is initialized.'); + } +} + +class UniversalRouteController +{ + public function getMiddleware() + { + return array_map(fn($middleware) => [ + 'middleware' => $middleware, + 'options' => [], + ], ['universal', InitializeTenancyByDomain::class]); + } + + public function show() + { + return tenancy()->initialized + ? 'Tenancy is initialized.' + : 'Tenancy is not initialized.'; + } } From 8db27a358ef71e01362dd388e5c0439fcda44944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Sat, 27 Jan 2024 22:55:59 +0100 Subject: [PATCH 4/8] Forget tenant parameter when a tenant is resolved from cache in PathTenantResolver (fix #1174) --- src/Resolvers/PathTenantResolver.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Resolvers/PathTenantResolver.php b/src/Resolvers/PathTenantResolver.php index 0b79626f..e3c32cc7 100644 --- a/src/Resolvers/PathTenantResolver.php +++ b/src/Resolvers/PathTenantResolver.php @@ -37,6 +37,14 @@ class PathTenantResolver extends Contracts\CachedTenantResolver throw new TenantCouldNotBeIdentifiedByPathException($id); } + public function resolved(Tenant $tenant, ...$args): void + { + /** @var Route $route */ + $route = $args[0]; + + $route->forgetParameter(static::$tenantParameterName); + } + public function getArgsForTenant(Tenant $tenant): array { return [ From 72b1b48edde3c4cdc5899b565476778bac462955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Tue, 12 Mar 2024 15:04:54 +0100 Subject: [PATCH 5/8] [3.x] Laravel 11 support (#1180) * Laravel 11 support * wip * trigger ci * fix ci file * try setting charset and collation on the default mysql connection * Set default cache driver to redis in tests * drop and recreate id column separately in autoincrement_ids_are_supported * set default redis client to predis in tests * revert fail-fast * try reverting TenantModelTest change * migrate phpunit configuration * add parent::tearDown() call --- .github/workflows/ci.yml | 2 ++ .gitignore | 1 + composer.json | 6 +++--- phpunit.xml | 18 ++++++++++-------- src/Concerns/TenantAwareCommand.php | 3 +-- tests/QueueTest.php | 4 +--- tests/TestCase.php | 4 ++++ 7 files changed, 22 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0272d86c..7caa8889 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,8 @@ jobs: php: "8.0" - laravel: 10 php: "8.1" + - laravel: 11 + php: "8.3" steps: - uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index c7cf933c..2a3062b7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ vendor/ .idea psysh .phpunit.result.cache +.phpunit.cache phpunit_var_*.xml coverage/ clover.xml diff --git a/composer.json b/composer.json index 66dceafe..21c0744e 100644 --- a/composer.json +++ b/composer.json @@ -12,15 +12,15 @@ "require": { "php": "^8.0", "ext-json": "*", - "illuminate/support": "^9.0|^10.0", + "illuminate/support": "^9.0|^10.0|^11.0", "facade/ignition-contracts": "^1.0.2", "ramsey/uuid": "^4.7.3", "stancl/jobpipeline": "^1.6.2", "stancl/virtualcolumn": "^1.3.1" }, "require-dev": { - "laravel/framework": "^9.0|^10.0", - "orchestra/testbench": "^7.0|^8.0", + "laravel/framework": "^9.0|^10.0|^11.0", + "orchestra/testbench": "^7.0|^8.0|^9.0", "league/flysystem-aws-s3-v3": "^3.12.2", "doctrine/dbal": "^3.6.0", "spatie/valuestore": "^1.3.2" diff --git a/phpunit.xml b/phpunit.xml index 19989e03..6d9f4ef7 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,13 +1,6 @@ - + - - ./src - - - ./src/routes.php - ./src/Vite.php - @@ -29,4 +22,13 @@ + + + ./src + + + ./src/routes.php + ./src/Vite.php + + diff --git a/src/Concerns/TenantAwareCommand.php b/src/Concerns/TenantAwareCommand.php index 5daa87f6..53774f83 100644 --- a/src/Concerns/TenantAwareCommand.php +++ b/src/Concerns/TenantAwareCommand.php @@ -10,8 +10,7 @@ use Symfony\Component\Console\Output\OutputInterface; trait TenantAwareCommand { - /** @return int */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $tenants = $this->getTenants(); $exitCode = 0; diff --git a/tests/QueueTest.php b/tests/QueueTest.php index 373e54a4..376257d2 100644 --- a/tests/QueueTest.php +++ b/tests/QueueTest.php @@ -4,9 +4,7 @@ declare(strict_types=1); namespace Stancl\Tenancy\Tests; -use Closure; use Exception; -use Illuminate\Support\Str; use Illuminate\Bus\Queueable; use Spatie\Valuestore\Valuestore; use Illuminate\Support\Facades\DB; @@ -25,7 +23,6 @@ use Illuminate\Queue\Events\JobProcessed; use Illuminate\Queue\Events\JobProcessing; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; -use PDO; use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\RevertToCentralContext; @@ -59,6 +56,7 @@ class QueueTest extends TestCase public function tearDown(): void { + parent::tearDown(); $this->valuestore->flush(); } diff --git a/tests/TestCase.php b/tests/TestCase.php index cea669a1..fe3fefb4 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -57,9 +57,11 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase $app['config']->set([ 'database.default' => 'central', + 'cache.default' => 'redis', 'database.redis.cache.host' => env('TENANCY_TEST_REDIS_HOST', '127.0.0.1'), 'database.redis.default.host' => env('TENANCY_TEST_REDIS_HOST', '127.0.0.1'), 'database.redis.options.prefix' => 'foo', + 'database.redis.client' => 'predis', 'database.connections.central' => [ 'driver' => 'mysql', 'url' => env('DATABASE_URL'), @@ -80,6 +82,8 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase ]) : [], ], 'database.connections.sqlite.database' => ':memory:', + 'database.connections.mysql.charset' => 'utf8mb4', + 'database.connections.mysql.collation' => 'utf8mb4_unicode_ci', 'database.connections.mysql.host' => env('TENANCY_TEST_MYSQL_HOST', '127.0.0.1'), 'database.connections.pgsql.host' => env('TENANCY_TEST_PGSQL_HOST', '127.0.0.1'), 'tenancy.filesystem.disks' => [ From d6d991ced6895599dcf8f1d2368d9301c61ecc10 Mon Sep 17 00:00:00 2001 From: Lucas Romano Date: Fri, 12 Apr 2024 15:05:55 -0300 Subject: [PATCH 6/8] Fixing RedisCluster deprecation of phpredis 6. (#1208) --- src/Bootstrappers/RedisTenancyBootstrapper.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Bootstrappers/RedisTenancyBootstrapper.php b/src/Bootstrappers/RedisTenancyBootstrapper.php index 7536984e..c230af33 100644 --- a/src/Bootstrappers/RedisTenancyBootstrapper.php +++ b/src/Bootstrappers/RedisTenancyBootstrapper.php @@ -28,8 +28,8 @@ class RedisTenancyBootstrapper implements TenancyBootstrapper $prefix = $this->config['tenancy.redis.prefix_base'] . $tenant->getTenantKey(); $client = Redis::connection($connection)->client(); - $this->originalPrefixes[$connection] = $client->getOption($client::OPT_PREFIX); - $client->setOption($client::OPT_PREFIX, $prefix); + $this->originalPrefixes[$connection] = $client->getOption(\Redis::OPT_PREFIX); + $client->setOption(\Redis::OPT_PREFIX, $prefix); } } @@ -38,7 +38,7 @@ class RedisTenancyBootstrapper implements TenancyBootstrapper foreach ($this->prefixedConnections() as $connection) { $client = Redis::connection($connection)->client(); - $client->setOption($client::OPT_PREFIX, $this->originalPrefixes[$connection]); + $client->setOption(\Redis::OPT_PREFIX, $this->originalPrefixes[$connection]); } $this->originalPrefixes = []; From 4dab0c1870ef5731230e08cdfb5386bcfa502209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Sun, 14 Apr 2024 10:12:18 +0200 Subject: [PATCH 7/8] defer tenant route registration in TSP stub --- assets/TenancyServiceProvider.stub.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/assets/TenancyServiceProvider.stub.php b/assets/TenancyServiceProvider.stub.php index 1d15f418..d94703cd 100644 --- a/assets/TenancyServiceProvider.stub.php +++ b/assets/TenancyServiceProvider.stub.php @@ -120,10 +120,12 @@ class TenancyServiceProvider extends ServiceProvider protected function mapRoutes() { - if (file_exists(base_path('routes/tenant.php'))) { - Route::namespace(static::$controllerNamespace) - ->group(base_path('routes/tenant.php')); - } + $this->app->booted(function () { + if (file_exists(base_path('routes/tenant.php'))) { + Route::namespace(static::$controllerNamespace) + ->group(base_path('routes/tenant.php')); + } + }); } protected function makeTenancyMiddlewareHighestPriority() From 120b8bc4aebfeebf6b3d70073bed7c0eeb073592 Mon Sep 17 00:00:00 2001 From: Bert Bredewold Date: Mon, 13 May 2024 17:15:26 +0200 Subject: [PATCH 8/8] Use assetPathResolver closure if set (#1221) The original Vite Facade uses the assetPathResolver closure (if set) to generate assets with a custom URL. This is needed when for example assets are cached on a CDN, or if you need assets from a central URL while in Tenant context. This updates the Tenancy version of the Vite-Facade to bring back the assetPathResolver logic. --- src/Vite.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Vite.php b/src/Vite.php index 1887361a..1464e3a3 100644 --- a/src/Vite.php +++ b/src/Vite.php @@ -15,6 +15,6 @@ class Vite extends BaseVite // todo move to a different directory in v4 */ protected function assetPath($path, $secure = null) { - return global_asset($path); + return $this->assetPathResolver ? ($this->assetPathResolver)($path, $secure) : global_asset($path); } }