From 9155e32ba85a954481b7a000a251a13f006a56e5 Mon Sep 17 00:00:00 2001 From: Leandro Guindani Gehlen Date: Mon, 4 Jul 2022 16:03:03 -0300 Subject: [PATCH 01/37] Add feature that allow to determine when model is syncable --- src/Contracts/Syncable.php | 2 + src/Database/Concerns/ResourceSyncing.php | 18 ++++--- src/Database/Models/TenantPivot.php | 2 +- tests/ResourceSyncingTest.php | 65 +++++++++++++++++++++++ 4 files changed, 80 insertions(+), 7 deletions(-) diff --git a/src/Contracts/Syncable.php b/src/Contracts/Syncable.php index 1f6e36e7..5c35f317 100644 --- a/src/Contracts/Syncable.php +++ b/src/Contracts/Syncable.php @@ -15,4 +15,6 @@ interface Syncable public function getSyncedAttributeNames(): array; public function triggerSyncEvent(); + + public function isSyncEnabled(); } diff --git a/src/Database/Concerns/ResourceSyncing.php b/src/Database/Concerns/ResourceSyncing.php index f1026f7a..4708faba 100644 --- a/src/Database/Concerns/ResourceSyncing.php +++ b/src/Database/Concerns/ResourceSyncing.php @@ -14,15 +14,16 @@ trait ResourceSyncing { static::saved(function (Syncable $model) { /** @var ResourceSyncing $model */ - $model->triggerSyncEvent(); + if ($model->isSyncEnabled()) { + $model->triggerSyncEvent(); + } }); static::creating(function (self $model) { - if (! $model->getAttribute($model->getGlobalIdentifierKeyName()) && app()->bound(UniqueIdentifierGenerator::class)) { - $model->setAttribute( - $model->getGlobalIdentifierKeyName(), - app(UniqueIdentifierGenerator::class)->generate($model) - ); + $key = $model->getGlobalIdentifierKeyName(); + + if (! $model->getAttribute($key) && app()->bound(UniqueIdentifierGenerator::class) && $model->isSyncEnabled()) { + $model->setAttribute($key, app(UniqueIdentifierGenerator::class)->generate($model)); } }); } @@ -32,4 +33,9 @@ trait ResourceSyncing /** @var Syncable $this */ event(new SyncedResourceSaved($this, tenant())); } + + public function isSyncEnabled() + { + return true; + } } diff --git a/src/Database/Models/TenantPivot.php b/src/Database/Models/TenantPivot.php index f745b45a..877d9466 100644 --- a/src/Database/Models/TenantPivot.php +++ b/src/Database/Models/TenantPivot.php @@ -16,7 +16,7 @@ class TenantPivot extends Pivot static::saved(function (self $pivot) { $parent = $pivot->pivotParent; - if ($parent instanceof Syncable) { + if ($parent instanceof Syncable && $parent->isSyncEnabled()) { $parent->triggerSyncEvent(); } }); diff --git a/tests/ResourceSyncingTest.php b/tests/ResourceSyncingTest.php index 570448d1..ea71ead4 100644 --- a/tests/ResourceSyncingTest.php +++ b/tests/ResourceSyncingTest.php @@ -89,6 +89,61 @@ class ResourceSyncingTest extends TestCase }); } + /** @test */ + public function resources_are_synced_only_sync_is_enabled() + { + CentralUser::create([ + 'global_id' => 'acme', + 'name' => 'John Doe', + 'email' => 'john@localhost', + 'password' => 'secret', + 'role' => 'commenter', // synced + ]); + + $tenant = ResourceTenant::create(); + $this->migrateTenants(); + + $tenant->run(function() { + ResourceUser::create([ + 'name' => 'Foo', + 'email' => 'foo@email.com', + 'password' => 'secret', + 'global_id' => 'acme', + 'role' => 'not_sync', + ]); + }); + + $centralUser = CentralUser::first(); + $this->assertSame('John Doe', $centralUser->name); // not sync + $this->assertSame('john@localhost', $centralUser->email); // not sync + $this->assertSame('secret', $centralUser->password); // not sync + } + + /** @test */ + public function central_users_are_synced_only_sync_is_enabled() + { + $centralUser = CentralUser::create([ + 'global_id' => 'acme', + 'name' => 'John Doe', + 'email' => 'john@localhost', + 'password' => 'secret', + 'role' => 'not_sync', // unsynced + ]); + + $t1 = ResourceTenant::create([ + 'id' => 't1', + ]); + $this->migrateTenants(); + + $centralUser->tenants()->attach('t1'); + + $t1->run(function () { + // assert user not exsits + $this->assertCount(0, ResourceUser::all()); + }); + + } + /** @test */ public function only_the_synced_columns_are_updated_in_the_central_db() { @@ -626,6 +681,11 @@ class CentralUser extends Model implements SyncMaster 'email', ]; } + + public function isSyncEnabled() + { + return $this->role !== 'not_sync'; + } } class ResourceUser extends Model implements Syncable @@ -659,4 +719,9 @@ class ResourceUser extends Model implements Syncable 'email', ]; } + + public function isSyncEnabled() + { + return $this->role !== 'not_sync'; + } } From ba928100e6431ea0e5b770ba781d3eabbf240c9c Mon Sep 17 00:00:00 2001 From: lukinovec Date: Tue, 19 Jul 2022 16:11:47 +0200 Subject: [PATCH 02/37] Add space after 'up' in 'docker-compose up-d' (#900) --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a5a6ec3f..b4c862c1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,4 +21,4 @@ services: platform: linux/amd64 ``` -to `docker-compose.override.yml` to make `docker-compose up-d` work on M1. +to `docker-compose.override.yml` to make `docker-compose up -d` work on M1. From 747c192979aa03907824e5481edb6b152edc3462 Mon Sep 17 00:00:00 2001 From: Bram Wubs Date: Wed, 20 Jul 2022 21:35:33 +0200 Subject: [PATCH 03/37] Fix ArgumentCountError on the TenantAssetsController (#894) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix ArgumentCount exception on the TenantAssetsController when no `$path` is provided * CS * CS * Handle null case explicitly * code style Co-authored-by: Bram Wubs Co-authored-by: Samuel Štancl --- src/Controllers/TenantAssetsController.php | 7 +++++-- tests/TenantAssetTest.php | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Controllers/TenantAssetsController.php b/src/Controllers/TenantAssetsController.php index 67478cf9..5549da0d 100644 --- a/src/Controllers/TenantAssetsController.php +++ b/src/Controllers/TenantAssetsController.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Stancl\Tenancy\Controllers; use Illuminate\Routing\Controller; +use Throwable; class TenantAssetsController extends Controller { @@ -15,11 +16,13 @@ class TenantAssetsController extends Controller $this->middleware(static::$tenancyMiddleware); } - public function asset($path) + public function asset($path = null) { + abort_if($path === null, 404); + try { return response()->file(storage_path("app/public/$path")); - } catch (\Throwable $th) { + } catch (Throwable $th) { abort(404); } } diff --git a/tests/TenantAssetTest.php b/tests/TenantAssetTest.php index 77a130b4..703ac65e 100644 --- a/tests/TenantAssetTest.php +++ b/tests/TenantAssetTest.php @@ -126,4 +126,19 @@ class TenantAssetTest extends TestCase $this->assertSame($original, asset('foo')); } + + public function test_asset_controller_returns_a_404_when_no_path_is_provided() + { + TenantAssetsController::$tenancyMiddleware = InitializeTenancyByRequestData::class; + + $tenant = Tenant::create(); + + tenancy()->initialize($tenant); + $response = $this->get(tenant_asset(null), [ + 'X-Tenant' => $tenant->id, + ]); + + $response->assertNotFound(); + } + } From 1ea3cefa1d1499211e8659a0b2e050b05ff0c4fe Mon Sep 17 00:00:00 2001 From: George Bishop Date: Sun, 21 Aug 2022 15:43:01 +0100 Subject: [PATCH 04/37] Add support for nested tenant config override (#920) * feat: add support for nested tenant config override * test: ensure nested tenant values are mapped --- src/Features/TenantConfig.php | 3 ++- tests/Features/TenantConfigTest.php | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/Features/TenantConfig.php b/src/Features/TenantConfig.php index fb09a871..95691f0d 100644 --- a/src/Features/TenantConfig.php +++ b/src/Features/TenantConfig.php @@ -6,6 +6,7 @@ namespace Stancl\Tenancy\Features; use Illuminate\Contracts\Config\Repository; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Arr; use Illuminate\Support\Facades\Event; use Stancl\Tenancy\Contracts\Feature; use Stancl\Tenancy\Contracts\Tenant; @@ -45,7 +46,7 @@ class TenantConfig implements Feature { /** @var Tenant|Model $tenant */ foreach (static::$storageToConfigMap as $storageKey => $configKey) { - $override = $tenant->getAttribute($storageKey); + $override = Arr::get($tenant, $storageKey); if (! is_null($override)) { if (is_array($configKey)) { diff --git a/tests/Features/TenantConfigTest.php b/tests/Features/TenantConfigTest.php index 37e26198..904e420b 100644 --- a/tests/Features/TenantConfigTest.php +++ b/tests/Features/TenantConfigTest.php @@ -22,6 +22,30 @@ class TenantConfigTest extends TestCase parent::tearDown(); } + /** @test */ + public function nested_tenant_values_are_merged() + { + $this->assertSame(null, config('whitelabel.theme')); + config([ + 'tenancy.features' => [TenantConfig::class], + 'tenancy.bootstrappers' => [], + ]); + Event::listen(TenancyInitialized::class, BootstrapTenancy::class); + Event::listen(TenancyEnded::class, RevertToCentralContext::class); + + TenantConfig::$storageToConfigMap = [ + 'whitelabel.config.theme' => 'whitelabel.theme', + ]; + + $tenant = Tenant::create([ + 'whitelabel' => ['config' => ['theme' => 'dark']], + ]); + + tenancy()->initialize($tenant); + $this->assertSame('dark', config('whitelabel.theme')); + tenancy()->end(); + } + /** @test */ public function config_is_merged_and_removed() { From d7305952e07d541622b31de41fbab16704de095c Mon Sep 17 00:00:00 2001 From: Leandro Guindani Gehlen Date: Thu, 25 Aug 2022 17:21:24 -0300 Subject: [PATCH 05/37] Remove unnecessary check --- src/Database/Concerns/ResourceSyncing.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Database/Concerns/ResourceSyncing.php b/src/Database/Concerns/ResourceSyncing.php index 4708faba..3d6c4cc2 100644 --- a/src/Database/Concerns/ResourceSyncing.php +++ b/src/Database/Concerns/ResourceSyncing.php @@ -20,10 +20,10 @@ trait ResourceSyncing }); static::creating(function (self $model) { - $key = $model->getGlobalIdentifierKeyName(); + $keyName = $model->getGlobalIdentifierKeyName(); - if (! $model->getAttribute($key) && app()->bound(UniqueIdentifierGenerator::class) && $model->isSyncEnabled()) { - $model->setAttribute($key, app(UniqueIdentifierGenerator::class)->generate($model)); + if (! $model->getAttribute($keyName) && app()->bound(UniqueIdentifierGenerator::class)) { + $model->setAttribute($keyName, app(UniqueIdentifierGenerator::class)->generate($model)); } }); } From 38fc706c97291bccaa4619a8480ab69173f13c37 Mon Sep 17 00:00:00 2001 From: Anbuselvan Rocky <15264938+anburocky3@users.noreply.github.com> Date: Thu, 22 Sep 2022 21:54:01 +0530 Subject: [PATCH 06/37] fix: typo mistake (#954) --- assets/config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/config.php b/assets/config.php index 85592d14..891bc64c 100644 --- a/assets/config.php +++ b/assets/config.php @@ -139,7 +139,7 @@ return [ ], /** - * Redis tenancy config. Used by RedisTenancyBoostrapper. + * Redis tenancy config. Used by RedisTenancyBootstrapper. * * Note: You need phpredis to use Redis tenancy. * From d093c1387d5d189ebae1d6a68e88108bbc0110e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wilsen=20Hern=C3=A1ndez?= <13445515+wilsenhc@users.noreply.github.com> Date: Tue, 27 Sep 2022 22:28:30 -0400 Subject: [PATCH 07/37] [3.x] Add Vite helper for tenancy (#956) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Vite helper for tenancy * Move Vite bundler to an Optional Feature * Rename to foundation vite * Add ViteBundlerTest * Add missing end of file * Update tests * remove unnecessary end() call Co-authored-by: Samuel Štancl --- assets/config.php | 1 + src/Features/ViteBundler.php | 21 ++++++++++++++++++++ src/TenancyServiceProvider.php | 1 + src/Vite.php | 20 +++++++++++++++++++ tests/Features/ViteBundlerTest.php | 32 ++++++++++++++++++++++++++++++ 5 files changed, 75 insertions(+) create mode 100644 src/Features/ViteBundler.php create mode 100644 src/Vite.php create mode 100644 tests/Features/ViteBundlerTest.php diff --git a/assets/config.php b/assets/config.php index 891bc64c..b12cd6c4 100644 --- a/assets/config.php +++ b/assets/config.php @@ -168,6 +168,7 @@ return [ // Stancl\Tenancy\Features\UniversalRoutes::class, // Stancl\Tenancy\Features\TenantConfig::class, // https://tenancyforlaravel.com/docs/v3/features/tenant-config // Stancl\Tenancy\Features\CrossDomainRedirect::class, // https://tenancyforlaravel.com/docs/v3/features/cross-domain-redirect + // Stancl\Tenancy\Features\ViteBundler::class, ], /** diff --git a/src/Features/ViteBundler.php b/src/Features/ViteBundler.php new file mode 100644 index 00000000..76b96a48 --- /dev/null +++ b/src/Features/ViteBundler.php @@ -0,0 +1,21 @@ +app->singleton(FoundationVite::class, StanclVite::class); + } +} diff --git a/src/TenancyServiceProvider.php b/src/TenancyServiceProvider.php index 4faaccf3..dec8c72e 100644 --- a/src/TenancyServiceProvider.php +++ b/src/TenancyServiceProvider.php @@ -65,6 +65,7 @@ class TenancyServiceProvider extends ServiceProvider $this->app->singleton(Commands\Rollback::class, function ($app) { return new Commands\Rollback($app['migrator']); }); + $this->app->singleton(Commands\Seed::class, function ($app) { return new Commands\Seed($app['db']); }); diff --git a/src/Vite.php b/src/Vite.php new file mode 100644 index 00000000..2598a2e4 --- /dev/null +++ b/src/Vite.php @@ -0,0 +1,20 @@ +toBeInstanceOf(FoundationVite::class); + + expect($vite)->not->toBeInstanceOf(StanclVite::class); + + config([ + 'tenancy.features' => [ViteBundler::class], + ]); + + $tenant = Tenant::create(); + + tenancy()->initialize($tenant); + + app()->forgetInstance(\Illuminate\Foundation\Vite::class); + + $vite = app(\Illuminate\Foundation\Vite::class); + + expect($vite)->toBeInstanceOf(StanclVite::class); +}); From ab2af5b38847de3ed79f16b2165d5e48512309ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Wed, 28 Sep 2022 16:43:20 +0200 Subject: [PATCH 08/37] rewrite ViteBundlerTest to phpunit syntax --- tests/Features/ViteBundlerTest.php | 35 ++++++++++++++++-------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/tests/Features/ViteBundlerTest.php b/tests/Features/ViteBundlerTest.php index 8d9ba61d..6b9208be 100644 --- a/tests/Features/ViteBundlerTest.php +++ b/tests/Features/ViteBundlerTest.php @@ -3,30 +3,33 @@ declare(strict_types=1); use Illuminate\Foundation\Vite as FoundationVite; -use Illuminate\Support\Facades\App; use Stancl\Tenancy\Features\ViteBundler; use Stancl\Tenancy\Tests\Etc\Tenant; +use Stancl\Tenancy\Tests\TestCase; use Stancl\Tenancy\Vite as StanclVite; +class ViteBundlerTest extends TestCase +{ + /** @test */ + public function the_vite_helper_uses_our_custom_class() + { + $vite = app(\Illuminate\Foundation\Vite::class); -test('replaces the vite helper instance with custom class', function () { - $vite = app(\Illuminate\Foundation\Vite::class); + $this->assertInstanceOf(FoundationVite::class, $vite); + $this->assertNotInstanceOf(StanclVite::class, $vite); - expect($vite)->toBeInstanceOf(FoundationVite::class); + config([ + 'tenancy.features' => [ViteBundler::class], + ]); - expect($vite)->not->toBeInstanceOf(StanclVite::class); + $tenant = Tenant::create(); - config([ - 'tenancy.features' => [ViteBundler::class], - ]); + tenancy()->initialize($tenant); - $tenant = Tenant::create(); + app()->forgetInstance(\Illuminate\Foundation\Vite::class); - tenancy()->initialize($tenant); + $vite = app(\Illuminate\Foundation\Vite::class); - app()->forgetInstance(\Illuminate\Foundation\Vite::class); - - $vite = app(\Illuminate\Foundation\Vite::class); - - expect($vite)->toBeInstanceOf(StanclVite::class); -}); + $this->assertInstanceOf(StanclVite::class, $vite); + } +} From 8ecdb49531741e831bb34aa4305b81cd3de0d414 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Wed, 28 Sep 2022 16:49:41 +0200 Subject: [PATCH 09/37] skip vite test in Laravel < 9 --- tests/Features/ViteBundlerTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/Features/ViteBundlerTest.php b/tests/Features/ViteBundlerTest.php index 6b9208be..90e06d78 100644 --- a/tests/Features/ViteBundlerTest.php +++ b/tests/Features/ViteBundlerTest.php @@ -13,6 +13,10 @@ class ViteBundlerTest extends TestCase /** @test */ public function the_vite_helper_uses_our_custom_class() { + if (version_compare(app()->version(), '9.0', '<')) { + $this->markTestSkipped('Vite is only used in Laravel 9+'); + } + $vite = app(\Illuminate\Foundation\Vite::class); $this->assertInstanceOf(FoundationVite::class, $vite); From 6d599de0672a1bab6ad739111ac286abb0b1b15b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Wed, 28 Sep 2022 17:06:05 +0200 Subject: [PATCH 10/37] convert ViteBundler to PHP 7 syntax --- src/Features/ViteBundler.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Features/ViteBundler.php b/src/Features/ViteBundler.php index 76b96a48..928de8f5 100644 --- a/src/Features/ViteBundler.php +++ b/src/Features/ViteBundler.php @@ -12,7 +12,12 @@ use Stancl\Tenancy\Vite as StanclVite; class ViteBundler implements Feature { - public function __construct(protected Application $app) { } + protected Application $app; + + public function __construct(Application $app) + { + $this->app = $app; + } public function bootstrap(Tenancy $tenancy): void { From d31a319cc7fc29fc1c92e5beccb951c3ae8ce51a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Wed, 28 Sep 2022 17:11:35 +0200 Subject: [PATCH 11/37] remove import of nonexistent class in older Laravel versions --- src/Features/ViteBundler.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Features/ViteBundler.php b/src/Features/ViteBundler.php index 928de8f5..f50510e4 100644 --- a/src/Features/ViteBundler.php +++ b/src/Features/ViteBundler.php @@ -5,10 +5,9 @@ declare(strict_types=1); namespace Stancl\Tenancy\Features; use Illuminate\Foundation\Application; -use Illuminate\Foundation\Vite as FoundationVite; use Stancl\Tenancy\Contracts\Feature; use Stancl\Tenancy\Tenancy; -use Stancl\Tenancy\Vite as StanclVite; +use Stancl\Tenancy\Vite; class ViteBundler implements Feature { @@ -21,6 +20,6 @@ class ViteBundler implements Feature public function bootstrap(Tenancy $tenancy): void { - $this->app->singleton(FoundationVite::class, StanclVite::class); + $this->app->singleton(\Illuminate\Foundation\Vite::class, Vite::class); } } From 596c47329a14533ff48a84afbed16d98fcf7296f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Wed, 28 Sep 2022 17:17:21 +0200 Subject: [PATCH 12/37] remove import of Foundation\Vite in tests --- tests/Features/ViteBundlerTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/Features/ViteBundlerTest.php b/tests/Features/ViteBundlerTest.php index 90e06d78..b544bcac 100644 --- a/tests/Features/ViteBundlerTest.php +++ b/tests/Features/ViteBundlerTest.php @@ -2,7 +2,6 @@ declare(strict_types=1); -use Illuminate\Foundation\Vite as FoundationVite; use Stancl\Tenancy\Features\ViteBundler; use Stancl\Tenancy\Tests\Etc\Tenant; use Stancl\Tenancy\Tests\TestCase; @@ -19,7 +18,7 @@ class ViteBundlerTest extends TestCase $vite = app(\Illuminate\Foundation\Vite::class); - $this->assertInstanceOf(FoundationVite::class, $vite); + $this->assertInstanceOf(\Illuminate\Foundation\Vite::class, $vite); $this->assertNotInstanceOf(StanclVite::class, $vite); config([ From a2c53b58ba8ed15b6710f85f8c57d010da462257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Wed, 28 Sep 2022 18:43:39 +0200 Subject: [PATCH 13/37] try to exclude Vite.php from coverage report --- phpunit.xml | 3 ++- src/Vite.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index 28fc8a08..934fd431 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -18,6 +18,7 @@ ./src ./src/routes.php + ./src/Vite.php @@ -35,4 +36,4 @@ - \ No newline at end of file + diff --git a/src/Vite.php b/src/Vite.php index 2598a2e4..1887361a 100644 --- a/src/Vite.php +++ b/src/Vite.php @@ -4,7 +4,7 @@ namespace Stancl\Tenancy; use Illuminate\Foundation\Vite as BaseVite; -class Vite extends BaseVite +class Vite extends BaseVite // todo move to a different directory in v4 { /** * Generate an asset path for the application. From 437a8ed05ceeb00f886202f1b04dd8f9757175ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Wed, 28 Sep 2022 18:50:09 +0200 Subject: [PATCH 14/37] remove typehint --- src/Features/ViteBundler.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Features/ViteBundler.php b/src/Features/ViteBundler.php index f50510e4..e3fee2fa 100644 --- a/src/Features/ViteBundler.php +++ b/src/Features/ViteBundler.php @@ -11,7 +11,8 @@ use Stancl\Tenancy\Vite; class ViteBundler implements Feature { - protected Application $app; + /** @var Application */ + protected $app; public function __construct(Application $app) { From 84a32877994bfd2ed303240f9e24709c74c809e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Thu, 29 Sep 2022 16:49:12 +0200 Subject: [PATCH 15/37] update channel name --- SUPPORT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SUPPORT.md b/SUPPORT.md index b7caaa5c..f85a21a9 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -1,5 +1,5 @@ # Get Support -If you need help with implementing the package, you can join our community [Discord server](https://discord.gg/7cpgPxv) and ask in `#help`. +If you need help with implementing the package, you can join our community [Discord server](https://archte.ch/discord) and ask in `#tenancy-help`. If you're interested in paid consulting from the maintainer, see the [contact page](https://tenancyforlaravel.com/contact/) on our website. From a7a8a5f5e3cb4601bd3c984e47d8dc8e7bc57862 Mon Sep 17 00:00:00 2001 From: Leandro Guindani Gehlen Date: Wed, 21 Dec 2022 10:07:19 -0300 Subject: [PATCH 16/37] Renamed method from `isSyncEnabled` to `shouldSync` --- src/Contracts/Syncable.php | 2 +- src/Database/Concerns/ResourceSyncing.php | 4 ++-- src/Database/Models/TenantPivot.php | 2 +- tests/ResourceSyncingTest.php | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Contracts/Syncable.php b/src/Contracts/Syncable.php index 5c35f317..0fe05695 100644 --- a/src/Contracts/Syncable.php +++ b/src/Contracts/Syncable.php @@ -16,5 +16,5 @@ interface Syncable public function triggerSyncEvent(); - public function isSyncEnabled(); + public function shouldSync(): bool; } diff --git a/src/Database/Concerns/ResourceSyncing.php b/src/Database/Concerns/ResourceSyncing.php index 3d6c4cc2..6db79dd2 100644 --- a/src/Database/Concerns/ResourceSyncing.php +++ b/src/Database/Concerns/ResourceSyncing.php @@ -14,7 +14,7 @@ trait ResourceSyncing { static::saved(function (Syncable $model) { /** @var ResourceSyncing $model */ - if ($model->isSyncEnabled()) { + if ($model->shouldSync()) { $model->triggerSyncEvent(); } }); @@ -34,7 +34,7 @@ trait ResourceSyncing event(new SyncedResourceSaved($this, tenant())); } - public function isSyncEnabled() + public function shouldSync(): bool { return true; } diff --git a/src/Database/Models/TenantPivot.php b/src/Database/Models/TenantPivot.php index 877d9466..3fb0118f 100644 --- a/src/Database/Models/TenantPivot.php +++ b/src/Database/Models/TenantPivot.php @@ -16,7 +16,7 @@ class TenantPivot extends Pivot static::saved(function (self $pivot) { $parent = $pivot->pivotParent; - if ($parent instanceof Syncable && $parent->isSyncEnabled()) { + if ($parent instanceof Syncable && $parent->shouldSync()) { $parent->triggerSyncEvent(); } }); diff --git a/tests/ResourceSyncingTest.php b/tests/ResourceSyncingTest.php index ea71ead4..bd71dad1 100644 --- a/tests/ResourceSyncingTest.php +++ b/tests/ResourceSyncingTest.php @@ -682,7 +682,7 @@ class CentralUser extends Model implements SyncMaster ]; } - public function isSyncEnabled() + public function shouldSync() { return $this->role !== 'not_sync'; } @@ -720,7 +720,7 @@ class ResourceUser extends Model implements Syncable ]; } - public function isSyncEnabled() + public function shouldSync() { return $this->role !== 'not_sync'; } From 26b856976132ecbeff8b2d84c90f8acba7a874c0 Mon Sep 17 00:00:00 2001 From: lukinovec Date: Fri, 27 Jan 2023 08:17:14 +0100 Subject: [PATCH 17/37] Cache crash fix (#1048) --- src/Resolvers/Contracts/CachedTenantResolver.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Resolvers/Contracts/CachedTenantResolver.php b/src/Resolvers/Contracts/CachedTenantResolver.php index 968ac794..feb9609c 100644 --- a/src/Resolvers/Contracts/CachedTenantResolver.php +++ b/src/Resolvers/Contracts/CachedTenantResolver.php @@ -36,9 +36,7 @@ abstract class CachedTenantResolver implements TenantResolver $key = $this->getCacheKey(...$args); - if ($this->cache->has($key)) { - $tenant = $this->cache->get($key); - + if ($tenant = $this->cache->get($key)) { $this->resolved($tenant, ...$args); return $tenant; From 7d59ff180fbac386774829f3f2d887889250a4f6 Mon Sep 17 00:00:00 2001 From: Joel Stein Date: Tue, 31 Jan 2023 23:24:53 -0600 Subject: [PATCH 18/37] Don't prevent accessing missing Tenant attributes. (#1045) --- src/Database/Models/Tenant.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Database/Models/Tenant.php b/src/Database/Models/Tenant.php index 4ec685b7..ecc5dbc4 100644 --- a/src/Database/Models/Tenant.php +++ b/src/Database/Models/Tenant.php @@ -28,6 +28,8 @@ class Tenant extends Model implements Contracts\Tenant Concerns\TenantRun, Concerns\InvalidatesResolverCache; + protected static $modelsShouldPreventAccessingMissingAttributes = false; + protected $table = 'tenants'; protected $primaryKey = 'id'; protected $guarded = []; From d4a99011e602cde65e708b67046d4c4cb13749b6 Mon Sep 17 00:00:00 2001 From: Guilherme Saade Date: Thu, 16 Feb 2023 13:21:06 -0300 Subject: [PATCH 19/37] [3.x] L10 compatibility (#1065) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Bump dependencies for Laravel 10 * Update GitHub Actions for Laravel 10 * ci: do not test L10 using PHP 7.3 * drop < L9 support * use `dispatch_sync` instead of `dispatch_now` * migrate phpunit configuration * Update ci.yml * drop laravel < 9 support * misc L10 fixes, new docker image * specify odbc version * wip * properly list php versions as strings * minor changes * Add `getValue($queryGrammar)` to raw query * Clean up `isVersion8` code * rewrite hasFailed assertion * phpunit schema update * Upgrade `doctrine/dbal` --------- Co-authored-by: Samuel Štancl Co-authored-by: Samuel Štancl Co-authored-by: lukinovec --- .github/workflows/ci.yml | 16 ++--- Dockerfile | 68 ++++++++----------- composer.json | 28 +++++--- docker-compose-m1.override.yml | 7 ++ phpunit.xml | 67 ++++++++---------- .../QueueTenancyBootstrapper.php | 11 ++- src/Database/Models/ImpersonationToken.php | 4 +- ...rmissionControlledMySQLDatabaseManager.php | 3 +- .../PostgreSQLSchemaManager.php | 6 +- test | 1 + tests/BootstrapperTest.php | 4 -- tests/EventListenerTest.php | 7 +- tests/Features/ViteBundlerTest.php | 4 -- tests/QueueTest.php | 4 -- tests/TenantDatabaseManagerTest.php | 6 +- 15 files changed, 108 insertions(+), 128 deletions(-) create mode 100644 docker-compose-m1.override.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ed8ff9ac..0272d86c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,9 +5,9 @@ env: on: push: - branches: [ 3.x, 2.x ] + branches: [ 3.x ] pull_request: - branches: [ 3.x, 2.x ] + branches: [ 3.x ] jobs: tests: @@ -15,17 +15,17 @@ jobs: strategy: matrix: - php: ["7.3", "8.0"] - laravel: ["^6.0", "^8.0", "^9.0"] - exclude: - - laravel: "^9.0" - php: "7.3" + include: + - laravel: 9 + php: "8.0" + - laravel: 10 + php: "8.1" steps: - uses: actions/checkout@v2 - name: Start docker containers run: PHP_VERSION=${{ matrix.php }} docker-compose up -d - name: Install dependencies - run: docker-compose exec -T test composer require --no-interaction "laravel/framework:${{ matrix.laravel }}" + run: docker-compose exec -T test composer require --no-interaction "laravel/framework:^${{ matrix.laravel }}.0" - name: Run tests run: ./test diff --git a/Dockerfile b/Dockerfile index 36f52d6a..6a66bde2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,50 +1,42 @@ -ARG PHP_VERSION=7.4 -ARG PHP_TARGET=php:${PHP_VERSION}-cli +# add amd64 platform to support Mac M1 +FROM --platform=linux/amd64 shivammathur/node:latest-amd64 -FROM ${PHP_TARGET} - -ARG COMPOSER_TARGET=2.0.3 +ARG PHP_VERSION=8.1 WORKDIR /var/www/html -LABEL org.opencontainers.image.source=https://github.com/stancl/tenancy \ - org.opencontainers.image.vendor="Samuel Štancl" \ - org.opencontainers.image.licenses="MIT" \ - org.opencontainers.image.title="PHP ${PHP_VERSION} with modules for laravel support" \ - org.opencontainers.image.description="PHP ${PHP_VERSION} with a set of php/os packages suitable for running Laravel apps" - # our default timezone and langauge ENV TZ=Europe/London ENV LANG=en_GB.UTF-8 -# Note: we only install reliable/core 1st-party php extensions here. -# If your app needs custom ones install them in the apps own -# Dockerfile _and pin the versions_! Eg: -# RUN pecl install memcached-2.2.0 && docker-php-ext-enable memcached +# install MYSSQL ODBC Driver +RUN apt-get update \ + && apt-get install -y gnupg2 \ + && curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - \ + && curl https://packages.microsoft.com/config/ubuntu/20.04/prod.list > /etc/apt/sources.list.d/mssql-release.list \ + && apt-get update \ + && ACCEPT_EULA=Y apt-get install -y unixodbc-dev=2.3.7 unixodbc=2.3.7 odbcinst1debian2=2.3.7 odbcinst=2.3.7 msodbcsql17 + +# set PHP version +RUN update-alternatives --set php /usr/bin/php$PHP_VERSION \ + && update-alternatives --set phar /usr/bin/phar$PHP_VERSION \ + && update-alternatives --set phar.phar /usr/bin/phar.phar$PHP_VERSION \ + && update-alternatives --set phpize /usr/bin/phpize$PHP_VERSION \ + && update-alternatives --set php-config /usr/bin/php-config$PHP_VERSION + +RUN apt-get update \ + && apt-get install -y --no-install-recommends libhiredis0.14 libjemalloc2 liblua5.1-0 lua-bitop lua-cjson redis redis-server redis-tools + +RUN pecl install redis-5.3.7 sqlsrv pdo_sqlsrv pcov \ + && printf "; priority=20\nextension=redis.so\n" > /etc/php/$PHP_VERSION/mods-available/redis.ini \ + && printf "; priority=20\nextension=sqlsrv.so\n" > /etc/php/$PHP_VERSION/mods-available/sqlsrv.ini \ + && printf "; priority=30\nextension=pdo_sqlsrv.so\n" > /etc/php/$PHP_VERSION/mods-available/pdo_sqlsrv.ini \ + && printf "; priority=40\nextension=pcov.so\n" > /etc/php/$PHP_VERSION/mods-available/pcov.ini \ + && phpenmod -v $PHP_VERSION redis sqlsrv pdo_sqlsrv pcov + +# install composer +COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer -# install some OS packages we need -RUN apt-get update -RUN apt-get install -y --no-install-recommends libfreetype6-dev libjpeg62-turbo-dev libpng-dev libgmp-dev libldap2-dev netcat curl sqlite3 libsqlite3-dev libpq-dev libzip-dev unzip vim-tiny gosu git - # install php extensions -RUN docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql \ - # && if [ "${PHP_VERSION}" = "7.4" ]; then docker-php-ext-configure gd --with-freetype --with-jpeg; else docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/; fi \ - && docker-php-ext-install -j$(nproc) gd pdo pdo_mysql pdo_pgsql pdo_sqlite pgsql zip gmp bcmath pcntl ldap sysvmsg exif \ - # install the redis php extension - && pecl install redis-5.3.7 \ - && docker-php-ext-enable redis \ - # install the pcov extention - && pecl install pcov \ - && docker-php-ext-enable pcov \ - && echo "pcov.enabled = 1" > /usr/local/etc/php/conf.d/pcov.ini -# clear the apt cache -RUN rm -rf /var/lib/apt/lists/* \ - && rm -rf /var/lib/apt/lists/* \ - # install composer - && curl -o /tmp/composer-setup.php https://getcomposer.org/installer \ - && curl -o /tmp/composer-setup.sig https://composer.github.io/installer.sig \ - && php -r "if (hash('SHA384', file_get_contents('/tmp/composer-setup.php')) !== trim(file_get_contents('/tmp/composer-setup.sig'))) { unlink('/tmp/composer-setup.php'); echo 'Invalid installer' . PHP_EOL; exit(1); }" \ - && php /tmp/composer-setup.php --version=${COMPOSER_TARGET} --no-ansi --install-dir=/usr/local/bin --filename=composer --snapshot \ - && rm -f /tmp/composer-setup.* # set the system timezone RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \ && echo $TZ > /etc/timezone diff --git a/composer.json b/composer.json index 88bfea29..66dceafe 100644 --- a/composer.json +++ b/composer.json @@ -10,19 +10,20 @@ } ], "require": { + "php": "^8.0", "ext-json": "*", - "illuminate/support": "^6.0|^7.0|^8.0|^9.0", - "facade/ignition-contracts": "^1.0", - "ramsey/uuid": "^3.7|^4.0", - "stancl/jobpipeline": "^1.0", - "stancl/virtualcolumn": "^1.0" + "illuminate/support": "^9.0|^10.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": "^6.0|^7.0|^8.0|^9.0", - "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0", - "league/flysystem-aws-s3-v3": "^1.0|^3.0", - "doctrine/dbal": "^2.10", - "spatie/valuestore": "^1.2.5" + "laravel/framework": "^9.0|^10.0", + "orchestra/testbench": "^7.0|^8.0", + "league/flysystem-aws-s3-v3": "^3.12.2", + "doctrine/dbal": "^3.6.0", + "spatie/valuestore": "^1.3.2" }, "autoload": { "psr-4": { @@ -49,5 +50,10 @@ } }, "minimum-stability": "dev", - "prefer-stable": true + "prefer-stable": true, + "config": { + "allow-plugins": { + "pestphp/pest-plugin": true + } + } } diff --git a/docker-compose-m1.override.yml b/docker-compose-m1.override.yml new file mode 100644 index 00000000..32e163e6 --- /dev/null +++ b/docker-compose-m1.override.yml @@ -0,0 +1,7 @@ +services: + mysql: + platform: linux/amd64 + mysql2: + platform: linux/amd64 + mssql: + image: mcr.microsoft.com/azure-sql-edge diff --git a/phpunit.xml b/phpunit.xml index 934fd431..19989e03 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,39 +1,32 @@ - - - - ./tests - - - - - ./src - - ./src/routes.php - ./src/Vite.php - - - - - - - - - - - - - - - - - + + + + ./src + + + ./src/routes.php + ./src/Vite.php + + + + + + + + + ./tests + + + + + + + + + + + + + diff --git a/src/Bootstrappers/QueueTenancyBootstrapper.php b/src/Bootstrappers/QueueTenancyBootstrapper.php index 790e1344..f747faea 100644 --- a/src/Bootstrappers/QueueTenancyBootstrapper.php +++ b/src/Bootstrappers/QueueTenancyBootstrapper.php @@ -63,14 +63,11 @@ class QueueTenancyBootstrapper implements TenancyBootstrapper static::initializeTenancyForQueue($event->job->payload()['tenant_id'] ?? null); }); - if (version_compare(app()->version(), '8.64', '>=')) { - // JobRetryRequested only exists since Laravel 8.64 - $dispatcher->listen(JobRetryRequested::class, function ($event) use (&$previousTenant) { - $previousTenant = tenant(); + $dispatcher->listen(JobRetryRequested::class, function ($event) use (&$previousTenant) { + $previousTenant = tenant(); - static::initializeTenancyForQueue($event->payload()['tenant_id'] ?? null); - }); - } + static::initializeTenancyForQueue($event->payload()['tenant_id'] ?? null); + }); // If we're running tests, we make sure to clean up after any artisan('queue:work') calls $revertToPreviousState = function ($event) use (&$previousTenant, $runningTests) { diff --git a/src/Database/Models/ImpersonationToken.php b/src/Database/Models/ImpersonationToken.php index 4aa63252..0de48060 100644 --- a/src/Database/Models/ImpersonationToken.php +++ b/src/Database/Models/ImpersonationToken.php @@ -26,8 +26,8 @@ class ImpersonationToken extends Model protected $primaryKey = 'token'; public $incrementing = false; protected $table = 'tenant_user_impersonation_tokens'; - protected $dates = [ - 'created_at', + protected $casts = [ + 'created_at' => 'datetime', ]; public static function boot() diff --git a/src/TenantDatabaseManagers/PermissionControlledMySQLDatabaseManager.php b/src/TenantDatabaseManagers/PermissionControlledMySQLDatabaseManager.php index 918601a8..df23ed1f 100644 --- a/src/TenantDatabaseManagers/PermissionControlledMySQLDatabaseManager.php +++ b/src/TenantDatabaseManagers/PermissionControlledMySQLDatabaseManager.php @@ -40,7 +40,8 @@ class PermissionControlledMySQLDatabaseManager extends MySQLDatabaseManager impl protected function isVersion8(): bool { - $version = $this->database()->select($this->database()->raw('select version()'))[0]->{'version()'}; + $versionSelect = $this->database()->raw('select version()')->getValue($this->database()->getQueryGrammar()); + $version = $this->database()->select($versionSelect)[0]->{'version()'}; return version_compare($version, '8.0.0') >= 0; } diff --git a/src/TenantDatabaseManagers/PostgreSQLSchemaManager.php b/src/TenantDatabaseManagers/PostgreSQLSchemaManager.php index 55f049d0..53607bdb 100644 --- a/src/TenantDatabaseManagers/PostgreSQLSchemaManager.php +++ b/src/TenantDatabaseManagers/PostgreSQLSchemaManager.php @@ -46,11 +46,7 @@ class PostgreSQLSchemaManager implements TenantDatabaseManager public function makeConnectionConfig(array $baseConfig, string $databaseName): array { - if (version_compare(app()->version(), '9.0', '>=')) { - $baseConfig['search_path'] = $databaseName; - } else { - $baseConfig['schema'] = $databaseName; - } + $baseConfig['search_path'] = $databaseName; return $baseConfig; } diff --git a/test b/test index 49535a7a..e064bbfe 100755 --- a/test +++ b/test @@ -1,3 +1,4 @@ #!/bin/bash +cat vendor/laravel/framework/src/Illuminate/Foundation/Application.php | grep 'const VERSION' docker-compose exec -T test vendor/bin/phpunit "$@" diff --git a/tests/BootstrapperTest.php b/tests/BootstrapperTest.php index 588fadd8..6f9cc493 100644 --- a/tests/BootstrapperTest.php +++ b/tests/BootstrapperTest.php @@ -207,10 +207,6 @@ class BootstrapperTest extends TestCase $disk = Storage::disk($disk); $adapter = $disk->getAdapter(); - if (! Str::startsWith(app()->version(), '9.')) { - return $adapter->getPathPrefix(); - } - $prefixer = (new ReflectionObject($adapter))->getProperty('prefixer'); $prefixer->setAccessible(true); diff --git a/tests/EventListenerTest.php b/tests/EventListenerTest.php index 4a45205c..759e330c 100644 --- a/tests/EventListenerTest.php +++ b/tests/EventListenerTest.php @@ -94,7 +94,7 @@ class EventListenerTest extends TestCase }); $tenant = Tenant::create(); - dispatch_now(new CreateDatabase($tenant)); + dispatch_sync(new CreateDatabase($tenant)); $this->assertFalse($tenant->database()->manager()->databaseExists( $tenant->database()->getName() @@ -192,12 +192,13 @@ class EventListenerTest extends TestCase })->toListener() ); - Tenant::create([ + $tenant = Tenant::create([ 'tenancy_create_database' => false, 'tenancy_db_name' => 'already_created', ]); - $this->assertFalse($this->hasFailed()); + // assert test didn't fail + $this->assertTrue($tenant->exists()); } } diff --git a/tests/Features/ViteBundlerTest.php b/tests/Features/ViteBundlerTest.php index b544bcac..23cf164d 100644 --- a/tests/Features/ViteBundlerTest.php +++ b/tests/Features/ViteBundlerTest.php @@ -12,10 +12,6 @@ class ViteBundlerTest extends TestCase /** @test */ public function the_vite_helper_uses_our_custom_class() { - if (version_compare(app()->version(), '9.0', '<')) { - $this->markTestSkipped('Vite is only used in Laravel 9+'); - } - $vite = app(\Illuminate\Foundation\Vite::class); $this->assertInstanceOf(\Illuminate\Foundation\Vite::class, $vite); diff --git a/tests/QueueTest.php b/tests/QueueTest.php index a3df9cd7..373e54a4 100644 --- a/tests/QueueTest.php +++ b/tests/QueueTest.php @@ -197,10 +197,6 @@ class QueueTest extends TestCase */ public function tenancy_is_initialized_when_retrying_jobs(bool $shouldEndTenancy) { - if (! Str::startsWith(app()->version(), '8')) { - $this->markTestSkipped('queue:retry tenancy is only supported in Laravel 8'); - } - $this->withFailedJobs(); $this->withTenantDatabases(); diff --git a/tests/TenantDatabaseManagerTest.php b/tests/TenantDatabaseManagerTest.php index 3d45d96f..6051ee12 100644 --- a/tests/TenantDatabaseManagerTest.php +++ b/tests/TenantDatabaseManagerTest.php @@ -95,7 +95,7 @@ class TenantDatabaseManagerTest extends TestCase $this->assertTrue($postgresManager->databaseExists($database)); } - public function database_manager_provider() + public static function database_manager_provider() { return [ ['mysql', MySQLDatabaseManager::class], @@ -194,9 +194,7 @@ class TenantDatabaseManagerTest extends TestCase ]); tenancy()->initialize($tenant); - $schemaConfig = version_compare(app()->version(), '9.0', '>=') ? - config('database.connections.' . config('database.default') . '.search_path') : - config('database.connections.' . config('database.default') . '.schema'); + $schemaConfig = config('database.connections.' . config('database.default') . '.search_path'); $this->assertSame($tenant->database()->getName(), $schemaConfig); $this->assertSame($originalDatabaseName, config(['database.connections.pgsql.database'])); From bcdd607cd4ce974fa85d7fcfe5302b8b94fc7fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Mon, 24 Apr 2023 22:02:32 +0200 Subject: [PATCH 20/37] update laravel version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 95fb7c60..8ce25809 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@

- Laravel 9.x + Laravel 10.x Latest Stable Version GitHub Actions CI status Donate From d9b7107900f938dc404810c59c092a1b8325366b Mon Sep 17 00:00:00 2001 From: Chris Thompson Date: Thu, 27 Apr 2023 20:40:27 +0700 Subject: [PATCH 21/37] Typo in PHPdoc (#1106) --- assets/config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/config.php b/assets/config.php index b12cd6c4..a10ad33c 100644 --- a/assets/config.php +++ b/assets/config.php @@ -112,7 +112,7 @@ return [ * See https://tenancyforlaravel.com/docs/v3/tenancy-bootstrappers/#filesystem-tenancy-boostrapper */ 'root_override' => [ - // Disks whose roots should be overriden after storage_path() is suffixed. + // Disks whose roots should be overridden after storage_path() is suffixed. 'local' => '%storage_path%/app/', 'public' => '%storage_path%/app/public/', ], From e070d137458c26f65bd5c4f1bf1a03ea34fb7fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Thu, 27 Jul 2023 05:15:28 +0200 Subject: [PATCH 22/37] update support link --- .github/ISSUE_TEMPLATE/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 6870a17c..2aab2732 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,7 +1,7 @@ blank_issues_enabled: false contact_links: - name: Support Questions & Other - url: https://github.com/stancl/tenancy/blob/3.x/SUPPORT.md + url: https://archte.ch/discord about: 'If you have a question or need help using the package.' - name: Documentation Issue url: https://github.com/stancl/tenancy-docs/issues From 395192442d4000fe901888482c7d9b98ead9bff8 Mon Sep 17 00:00:00 2001 From: tamiroh Date: Fri, 18 Aug 2023 14:40:21 +0900 Subject: [PATCH 23/37] Add use (#1103) --- src/Middleware/InitializeTenancyByRequestData.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Middleware/InitializeTenancyByRequestData.php b/src/Middleware/InitializeTenancyByRequestData.php index de75d8c5..ef2132e8 100644 --- a/src/Middleware/InitializeTenancyByRequestData.php +++ b/src/Middleware/InitializeTenancyByRequestData.php @@ -6,6 +6,7 @@ namespace Stancl\Tenancy\Middleware; use Closure; use Illuminate\Http\Request; +use Stancl\Tenancy\Contracts\TenantResolver; use Stancl\Tenancy\Resolvers\RequestDataTenantResolver; use Stancl\Tenancy\Tenancy; From 4af70d302ffcf19306cc16009bd94162f3743ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Thu, 24 Aug 2023 18:21:23 +0200 Subject: [PATCH 24/37] add extra $path validation to TenantAssetsController --- .gitignore | 1 + src/Controllers/TenantAssetsController.php | 18 +++++++++++++++++- tests/TenantAssetTest.php | 13 +++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f470ba75..c7cf933c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ coverage/ clover.xml tests/Etc/tmp/queuetest.json docker-compose.override.yml +.DS_Store diff --git a/src/Controllers/TenantAssetsController.php b/src/Controllers/TenantAssetsController.php index 5549da0d..1e2014a7 100644 --- a/src/Controllers/TenantAssetsController.php +++ b/src/Controllers/TenantAssetsController.php @@ -18,7 +18,7 @@ class TenantAssetsController extends Controller public function asset($path = null) { - abort_if($path === null, 404); + $this->validatePath($path); try { return response()->file(storage_path("app/public/$path")); @@ -26,4 +26,20 @@ class TenantAssetsController extends Controller abort(404); } } + + /** + * @throws \Symfony\Component\HttpKernel\Exception\HttpException + */ + protected function validatePath(string|null $path): void + { + abort_if($path === null, 404); + + $allowedRoot = storage_path('app/public'); + + // Prevent path traversal attacks. This is generally a non-issue on modern + // webservers but it's still worth handling on the application level as well. + if (! str(realpath("{$allowedRoot}/{$path}"))->startsWith($allowedRoot)) { + abort(403); + } + } } diff --git a/tests/TenantAssetTest.php b/tests/TenantAssetTest.php index 703ac65e..c24767cc 100644 --- a/tests/TenantAssetTest.php +++ b/tests/TenantAssetTest.php @@ -141,4 +141,17 @@ class TenantAssetTest extends TestCase $response->assertNotFound(); } + public function test_asset_controller_returns_a_403_when_an_invalid_path_is_provided() + { + TenantAssetsController::$tenancyMiddleware = InitializeTenancyByRequestData::class; + + $tenant = Tenant::create(); + + tenancy()->initialize($tenant); + $response = $this->get(tenant_asset('../foo.txt'), [ + 'X-Tenant' => $tenant->id, + ]); + + $response->assertForbidden(); + } } From caf2267a085dd8ff0c566b3540bbe94f7a89a4cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Sat, 2 Sep 2023 03:19:37 +0200 Subject: [PATCH 25/37] reimplement TenantAssetsController::validatePath() (fixes #1143) --- src/Controllers/TenantAssetsController.php | 36 ++++++++++-- tests/TenantAssetTest.php | 68 ++++++++++++++++++++-- 2 files changed, 92 insertions(+), 12 deletions(-) diff --git a/src/Controllers/TenantAssetsController.php b/src/Controllers/TenantAssetsController.php index 1e2014a7..356c25dd 100644 --- a/src/Controllers/TenantAssetsController.php +++ b/src/Controllers/TenantAssetsController.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Stancl\Tenancy\Controllers; +use Exception; use Illuminate\Routing\Controller; use Throwable; @@ -28,18 +29,41 @@ class TenantAssetsController extends Controller } /** + * Prevent path traversal attacks. This is generally a non-issue on modern + * webservers but it's still worth handling on the application level as well. + * * @throws \Symfony\Component\HttpKernel\Exception\HttpException */ protected function validatePath(string|null $path): void { - abort_if($path === null, 404); + $this->abortIf($path === null, 'Empty path'); - $allowedRoot = storage_path('app/public'); + $allowedRoot = realpath(storage_path('app/public')); - // Prevent path traversal attacks. This is generally a non-issue on modern - // webservers but it's still worth handling on the application level as well. - if (! str(realpath("{$allowedRoot}/{$path}"))->startsWith($allowedRoot)) { - abort(403); + // `storage_path('app/public')` doesn't exist, so it cannot contain files + $this->abortIf($allowedRoot === false, "Storage root doesn't exist"); + + $attemptedPath = realpath("{$allowedRoot}/{$path}"); + + // User is attempting to access a nonexistent file + $this->abortIf($attemptedPath === false, 'Accessing a nonexistent file'); + + // User is attempting to access a file outside the $allowedRoot folder + $this->abortIf(! str($attemptedPath)->startsWith($allowedRoot), 'Accessing a file outside the storage root'); + } + + protected function abortIf($condition, $exceptionMessage): void + { + if ($condition) { + if (app()->runningUnitTests()) { + // Makes testing the cause of the failure in validatePath() easier + throw new Exception($exceptionMessage); + } else { + // We always use 404 to avoid leaking information about the cause of the error + // e.g. when someone is trying to access a nonexistent file outside of the allowed + // root folder, we don't want to let the user know whether such a file exists or not. + abort(404); + } } } } diff --git a/tests/TenantAssetTest.php b/tests/TenantAssetTest.php index c24767cc..8aabc0e2 100644 --- a/tests/TenantAssetTest.php +++ b/tests/TenantAssetTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Stancl\Tenancy\Tests; +use Exception; use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Storage; @@ -134,24 +135,79 @@ class TenantAssetTest extends TestCase $tenant = Tenant::create(); tenancy()->initialize($tenant); - $response = $this->get(tenant_asset(null), [ + + $this->withoutExceptionHandling(); + $this->expectExceptionMessage('Empty path'); // outside tests this is a 404 + + $this->get(tenant_asset(null), [ 'X-Tenant' => $tenant->id, ]); - - $response->assertNotFound(); } - public function test_asset_controller_returns_a_403_when_an_invalid_path_is_provided() + public function test_asset_controller_returns_a_404_when_the_storage_root_doesnt_exist() { TenantAssetsController::$tenancyMiddleware = InitializeTenancyByRequestData::class; $tenant = Tenant::create(); tenancy()->initialize($tenant); - $response = $this->get(tenant_asset('../foo.txt'), [ + + $storageRoot = storage_path("app/public"); + + if (is_dir($storageRoot)) { + rmdir(storage_path("app/public")); + } + + $this->withoutExceptionHandling(); + $this->expectExceptionMessage("Storage root doesn't exist"); // outside tests this is a 404 + + $this->get(tenant_asset('foo.txt'), [ 'X-Tenant' => $tenant->id, ]); + } - $response->assertForbidden(); + public function test_asset_controller_returns_a_404_when_accessing_a_nonexistent_file() + { + TenantAssetsController::$tenancyMiddleware = InitializeTenancyByRequestData::class; + + $tenant = Tenant::create(); + + tenancy()->initialize($tenant); + + $storageRoot = storage_path("app/public"); + + if (! is_dir($storageRoot)) { + mkdir(storage_path("app/public"), recursive: true); + } + + $this->withoutExceptionHandling(); + $this->expectExceptionMessage("Accessing a nonexistent file"); // outside tests this is a 404 + + $this->get(tenant_asset('foo.txt'), [ + 'X-Tenant' => $tenant->id, + ]); + } + + public function test_asset_controller_returns_a_404_when_accessing_a_file_outside_the_storage_root() + { + TenantAssetsController::$tenancyMiddleware = InitializeTenancyByRequestData::class; + + $tenant = Tenant::create(); + + tenancy()->initialize($tenant); + + $storageRoot = storage_path("app/public"); + + if (! is_dir($storageRoot)) { + mkdir(storage_path("app/public"), recursive: true); + file_put_contents(storage_path('app/foo.txt'), 'bar'); + } + + $this->withoutExceptionHandling(); + $this->expectExceptionMessage('Accessing a file outside the storage root'); // outside tests this is a 404 + + $this->get(tenant_asset('../foo.txt'), [ + 'X-Tenant' => $tenant->id, + ]); } } From 85c7465aca2fb017545a5304dea0ee0006bee5f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Sat, 2 Sep 2023 03:20:42 +0200 Subject: [PATCH 26/37] remove unnecessary import --- tests/TenantAssetTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/TenantAssetTest.php b/tests/TenantAssetTest.php index 8aabc0e2..2852eae2 100644 --- a/tests/TenantAssetTest.php +++ b/tests/TenantAssetTest.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace Stancl\Tenancy\Tests; -use Exception; use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Storage; From 0b248f937dd4799aa42ecdbd2fc73c05ddb943bb Mon Sep 17 00:00:00 2001 From: Massimo Simonini Date: Tue, 21 Nov 2023 02:25:32 +0100 Subject: [PATCH 27/37] 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 28/37] 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 29/37] 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 30/37] 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 31/37] [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 32/37] 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 33/37] 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 34/37] 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); } } From 885179bf7482ff0063ef0ba78aa863fcdd792cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Wed, 22 May 2024 19:42:20 +0200 Subject: [PATCH 35/37] Fix #1223 - Vite regression introduced on Laravel 9 in v3.8.3 --- src/Vite.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Vite.php b/src/Vite.php index 1464e3a3..91ab9aad 100644 --- a/src/Vite.php +++ b/src/Vite.php @@ -15,6 +15,9 @@ class Vite extends BaseVite // todo move to a different directory in v4 */ protected function assetPath($path, $secure = null) { - return $this->assetPathResolver ? ($this->assetPathResolver)($path, $secure) : global_asset($path); + // In Laravel 9, the property doesn't exist. + return (isset($this->assetPathResolver) && $this->assetPathResolver) + ? ($this->assetPathResolver)($path, $secure) + : global_asset($path); } } From 8f9c7efa4584007f41048448d0a11f572a1d3239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Wed, 22 May 2024 19:57:48 +0200 Subject: [PATCH 36/37] pin dockerfile to ubuntu 22 --- Dockerfile | 2 +- docker-compose.yml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6a66bde2..407b8a92 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # add amd64 platform to support Mac M1 -FROM --platform=linux/amd64 shivammathur/node:latest-amd64 +FROM --platform=linux/amd64 shivammathur/node:jammy-amd64 ARG PHP_VERSION=8.1 diff --git a/docker-compose.yml b/docker-compose.yml index e8e8d418..e1403dd6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: '2.4' services: test: build: From a70312cb48ccff0e61873263088bdba0eff9f3aa Mon Sep 17 00:00:00 2001 From: Leandro Guindani Gehlen Date: Fri, 28 Jun 2024 15:10:13 -0300 Subject: [PATCH 37/37] Add tenant key resolver to define filesystem path suffix --- src/Bootstrappers/FilesystemTenancyBootstrapper.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Bootstrappers/FilesystemTenancyBootstrapper.php b/src/Bootstrappers/FilesystemTenancyBootstrapper.php index 346892b3..8674df04 100644 --- a/src/Bootstrappers/FilesystemTenancyBootstrapper.php +++ b/src/Bootstrappers/FilesystemTenancyBootstrapper.php @@ -17,6 +17,9 @@ class FilesystemTenancyBootstrapper implements TenancyBootstrapper /** @var array */ public $originalPaths = []; + /** @var callable */ + protected static $tenantKeyResolver; + public function __construct(Application $app) { $this->app = $app; @@ -33,9 +36,16 @@ class FilesystemTenancyBootstrapper implements TenancyBootstrapper }); } + public static function resolveTenantKeyUsing(callable $tenantKeyResolver): void + { + static::$tenantKeyResolver = $tenantKeyResolver; + } + public function bootstrap(Tenant $tenant) { - $suffix = $this->app['config']['tenancy.filesystem.suffix_base'] . $tenant->getTenantKey(); + $tenantKey = static::$tenantKeyResolver ? (static::$tenantKeyResolver)($tenant) : $tenant->getTenantKey(); + + $suffix = $this->app['config']['tenancy.filesystem.suffix_base'] . $tenantKey; // storage_path() if ($this->app['config']['tenancy.filesystem.suffix_storage_path'] ?? true) {