From a8f84d280860e003ca00a8e67ce78ec8962bbd24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Mon, 2 Dec 2019 17:57:16 +0100 Subject: [PATCH 01/16] Accept closures in tenant() helper --- src/helpers.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/helpers.php b/src/helpers.php index 101d43df..3f7f98ab 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -18,14 +18,23 @@ if (! function_exists('tenancy')) { } if (! function_exists('tenant')) { - /** @return Tenant|mixed */ + /** + * Get a key from the current tenant's storage. + * + * @param string|Closure|null $key + * @return Tenant|mixed + */ function tenant($key = null) { - if (! is_null($key)) { - return optional(app(Tenant::class))->get($key) ?? null; + if (is_null($key)) { + return app(Tenant::class); } - return app(Tenant::class); + if ($key instanceof Closure) { + return app(Tenant::class)->run($key); + } + + return optional(app(Tenant::class))->get($key) ?? null; } } From 9851f088ea1ba38adcb44c1546afd639d5544424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Tue, 3 Dec 2019 22:49:20 +0100 Subject: [PATCH 02/16] Make route parameter optional, fix #240 --- src/routes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes.php b/src/routes.php index 7f1a5310..093f5de4 100644 --- a/src/routes.php +++ b/src/routes.php @@ -3,7 +3,7 @@ declare(strict_types=1); Route::middleware(['tenancy'])->group(function () { - Route::get('/tenancy/assets/{path}', 'Stancl\Tenancy\Controllers\TenantAssetsController@asset') + Route::get('/tenancy/assets/{path?}', 'Stancl\Tenancy\Controllers\TenantAssetsController@asset') ->where('path', '(.*)') ->name('stancl.tenancy.asset'); }); From f99ecb72c70f7d83bd4084666eb53fcbe0dc763d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Tue, 3 Dec 2019 22:52:30 +0100 Subject: [PATCH 03/16] Don't accept closures in tenant() helper --- src/helpers.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/helpers.php b/src/helpers.php index 3f7f98ab..67acd2fb 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -21,7 +21,7 @@ if (! function_exists('tenant')) { /** * Get a key from the current tenant's storage. * - * @param string|Closure|null $key + * @param string|null $key * @return Tenant|mixed */ function tenant($key = null) @@ -30,10 +30,6 @@ if (! function_exists('tenant')) { return app(Tenant::class); } - if ($key instanceof Closure) { - return app(Tenant::class)->run($key); - } - return optional(app(Tenant::class))->get($key) ?? null; } } From 609b6f12cf7842e4e6cbb6166ce461e6420dda9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Fri, 6 Dec 2019 22:34:02 +0100 Subject: [PATCH 04/16] Remove typehints passed to route() --- src/helpers.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers.php b/src/helpers.php index 67acd2fb..58aa8ccb 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -57,7 +57,7 @@ if (! function_exists('global_cache')) { } if (! function_exists('tenant_route')) { - function tenant_route(string $route, array $parameters = [], string $domain = null): string + function tenant_route($route, $parameters = [], string $domain = null) { $domain = $domain ?? request()->getHost(); From 5145b1f13eaad9a3097860a247fb814a752f0824 Mon Sep 17 00:00:00 2001 From: Jess Johannessen Date: Tue, 10 Dec 2019 16:56:51 +0100 Subject: [PATCH 05/16] Added support for migration parameters (#243) --- assets/config.php | 4 ++++ src/Jobs/QueuedTenantDatabaseMigrator.php | 8 ++++++-- src/TenantManager.php | 9 +++++++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/assets/config.php b/assets/config.php index c08fa0a6..2657dfb6 100644 --- a/assets/config.php +++ b/assets/config.php @@ -93,9 +93,13 @@ return [ 'home_url' => '/app', 'queue_database_creation' => false, 'migrate_after_creation' => false, // run migrations after creating a tenant + 'migration_parameters' => [ + // '--force' => true, // force database migrations + ], 'seed_after_migration' => false, // should the seeder run after automatic migration 'seeder_parameters' => [ '--class' => 'DatabaseSeeder', // root seeder class to run after automatic migrations, e.g.: 'DatabaseSeeder' + // '--force' => true, // force database seeder ], 'queue_database_deletion' => false, 'delete_database_after_tenant_deletion' => false, // delete the tenant's database after deleting the tenant diff --git a/src/Jobs/QueuedTenantDatabaseMigrator.php b/src/Jobs/QueuedTenantDatabaseMigrator.php index 5ea12656..c71696cc 100644 --- a/src/Jobs/QueuedTenantDatabaseMigrator.php +++ b/src/Jobs/QueuedTenantDatabaseMigrator.php @@ -19,9 +19,13 @@ class QueuedTenantDatabaseMigrator implements ShouldQueue /** @var string */ protected $tenantId; - public function __construct(Tenant $tenant) + /** @var array */ + protected $migrationParameters = []; + + public function __construct(Tenant $tenant, $migrationParameters = []) { $this->tenantId = $tenant->id; + $this->migrationParameters = $migrationParameters; } /** @@ -33,6 +37,6 @@ class QueuedTenantDatabaseMigrator implements ShouldQueue { Artisan::call('tenants:migrate', [ '--tenants' => [$this->tenantId], - ]); + ] + $this->migrationParameters); } } diff --git a/src/TenantManager.php b/src/TenantManager.php index abf3b766..9e3b20ea 100644 --- a/src/TenantManager.php +++ b/src/TenantManager.php @@ -78,11 +78,11 @@ class TenantManager if ($this->shouldMigrateAfterCreation()) { $afterCreating[] = $this->databaseCreationQueued() - ? new QueuedTenantDatabaseMigrator($tenant) + ? new QueuedTenantDatabaseMigrator($tenant, $this->getMigrationParameters()) : function () use ($tenant) { $this->artisan->call('tenants:migrate', [ '--tenants' => [$tenant['id']], - ]); + ] + $this->getMigrationParameters()); }; } @@ -401,6 +401,11 @@ class TenantManager return $this->app['config']['tenancy.seeder_parameters'] ?? []; } + public function getMigrationParameters() + { + return $this->app['config']['tenancy.migration_parameters'] ?? []; + } + /** * Add an event listener. * From fed8c0f9d11c26c03c66809e815b4b04e6b257f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Wed, 11 Dec 2019 22:16:25 +0100 Subject: [PATCH 06/16] [2.2.4] [WIP] Respect custom connections when creating database (#244) * Add TenantDatabaseManager::setConnection() * Apply fixes from StyleCI --- src/Contracts/Future/CanSetConnection.php | 15 +++++++++++++++ src/DatabaseManager.php | 11 +++++++++-- .../MySQLDatabaseManager.php | 11 +++++++++-- .../PostgreSQLDatabaseManager.php | 8 +++++++- 4 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 src/Contracts/Future/CanSetConnection.php diff --git a/src/Contracts/Future/CanSetConnection.php b/src/Contracts/Future/CanSetConnection.php new file mode 100644 index 00000000..4fde6cb4 --- /dev/null +++ b/src/Contracts/Future/CanSetConnection.php @@ -0,0 +1,15 @@ +getDriver($this->getBaseConnection($tenant->getConnectionName())); + $driver = $this->getDriver($this->getBaseConnection($connectionName = $tenant->getConnectionName())); $databaseManagers = $this->app['config']['tenancy.database_managers']; @@ -234,6 +235,12 @@ class DatabaseManager throw new DatabaseManagerNotRegisteredException($driver); } - return $this->app[$databaseManagers[$driver]]; + $databaseManager = $this->app[$databaseManagers[$driver]]; + + if ($connectionName !== 'tenant' && $databaseManager instanceof CanSetConnection) { + $databaseManager->setConnection($this->database->connection($connectionName)); + } + + return $databaseManager; } } diff --git a/src/TenantDatabaseManagers/MySQLDatabaseManager.php b/src/TenantDatabaseManagers/MySQLDatabaseManager.php index 548f628d..9ed211bc 100644 --- a/src/TenantDatabaseManagers/MySQLDatabaseManager.php +++ b/src/TenantDatabaseManagers/MySQLDatabaseManager.php @@ -5,12 +5,14 @@ declare(strict_types=1); namespace Stancl\Tenancy\TenantDatabaseManagers; use Illuminate\Contracts\Config\Repository; +use Illuminate\Database\Connection; use Illuminate\Database\DatabaseManager as IlluminateDatabaseManager; +use Stancl\Tenancy\Contracts\Future\CanSetConnection; use Stancl\Tenancy\Contracts\TenantDatabaseManager; -class MySQLDatabaseManager implements TenantDatabaseManager +class MySQLDatabaseManager implements TenantDatabaseManager, CanSetConnection { - /** @var \Illuminate\Database\Connection */ + /** @var Connection */ protected $database; public function __construct(Repository $config, IlluminateDatabaseManager $databaseManager) @@ -18,6 +20,11 @@ class MySQLDatabaseManager implements TenantDatabaseManager $this->database = $databaseManager->connection($config['tenancy.database_manager_connections.mysql']); } + public function setConnection(Connection $connection) + { + $this->database = $connection; + } + public function createDatabase(string $name): bool { $charset = $this->database->getConfig('charset'); diff --git a/src/TenantDatabaseManagers/PostgreSQLDatabaseManager.php b/src/TenantDatabaseManagers/PostgreSQLDatabaseManager.php index 9fe67297..688b357e 100644 --- a/src/TenantDatabaseManagers/PostgreSQLDatabaseManager.php +++ b/src/TenantDatabaseManagers/PostgreSQLDatabaseManager.php @@ -5,12 +5,13 @@ declare(strict_types=1); namespace Stancl\Tenancy\TenantDatabaseManagers; use Illuminate\Contracts\Config\Repository; +use Illuminate\Database\Connection; use Illuminate\Database\DatabaseManager as IlluminateDatabaseManager; use Stancl\Tenancy\Contracts\TenantDatabaseManager; class PostgreSQLDatabaseManager implements TenantDatabaseManager { - /** @var \Illuminate\Database\Connection */ + /** @var Connection */ protected $database; public function __construct(Repository $config, IlluminateDatabaseManager $databaseManager) @@ -18,6 +19,11 @@ class PostgreSQLDatabaseManager implements TenantDatabaseManager $this->database = $databaseManager->connection($config['tenancy.database_manager_connections.pgsql']); } + public function setConnection(Connection $connection) + { + $this->database = $connection; + } + public function createDatabase(string $name): bool { return $this->database->statement("CREATE DATABASE \"$name\" WITH TEMPLATE=template0"); From 5ebb68e860119b4508d86f2150d2dd7ce4696bd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Thu, 26 Dec 2019 20:28:29 +0100 Subject: [PATCH 07/16] Fix issue related to closure middleware --- src/Middleware/PreventAccessFromTenantDomains.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Middleware/PreventAccessFromTenantDomains.php b/src/Middleware/PreventAccessFromTenantDomains.php index 92fda549..654babd5 100644 --- a/src/Middleware/PreventAccessFromTenantDomains.php +++ b/src/Middleware/PreventAccessFromTenantDomains.php @@ -55,7 +55,7 @@ class PreventAccessFromTenantDomains // groups have a `tenancy` middleware group inside them $middlewareGroups = Router::getMiddlewareGroups(); foreach ($route->gatherMiddleware() as $inner) { - if (isset($middlewareGroups[$inner]) && in_array($middleware, $middlewareGroups[$inner], true)) { + if (! $inner instanceof Closure && isset($middlewareGroups[$inner]) && in_array($middleware, $middlewareGroups[$inner], true)) { return true; } } From 6ad1a6639f674e6b0c5808d3990319a0f41adc98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Sun, 19 Jan 2020 12:08:16 +0100 Subject: [PATCH 08/16] Set $persisted in TenantManager::createTenant() (#272) * Set $persisted in TenantManager::createTenant() * Apply fixes from StyleCI --- src/Commands/Install.php | 2 +- src/Tenant.php | 4 +--- src/TenantManager.php | 2 ++ 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Commands/Install.php b/src/Commands/Install.php index c1012d94..426bf1d3 100644 --- a/src/Commands/Install.php +++ b/src/Commands/Install.php @@ -33,7 +33,7 @@ class Install extends Command $this->callSilent('vendor:publish', [ '--provider' => 'Stancl\Tenancy\TenancyServiceProvider', '--tag' => 'config', - ]); + ]); $this->info('✔️ Created config/tenancy.php'); $newKernel = str_replace( diff --git a/src/Tenant.php b/src/Tenant.php index 8e6a900b..38ac7261 100644 --- a/src/Tenant.php +++ b/src/Tenant.php @@ -41,7 +41,7 @@ class Tenant implements ArrayAccess /** @var Repository */ protected $config; - /** @var StorageDriver */ + /** @var StorageDriver|CanDeleteKeys */ protected $storage; /** @var TenantManager */ @@ -233,8 +233,6 @@ class Tenant implements ArrayAccess $this->manager->createTenant($this); } - $this->persisted = true; - return $this; } diff --git a/src/TenantManager.php b/src/TenantManager.php index 9e3b20ea..2128da5a 100644 --- a/src/TenantManager.php +++ b/src/TenantManager.php @@ -73,6 +73,8 @@ class TenantManager $this->storage->createTenant($tenant); + $tenant->persisted = true; + /** @var \Illuminate\Contracts\Queue\ShouldQueue[]|callable[] $afterCreating */ $afterCreating = []; From bb467672575b3a224ad9edb332e6e63d804cd7d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Sun, 19 Jan 2020 12:15:15 +0100 Subject: [PATCH 09/16] Add regression test for #271 --- tests/TenantManagerEventsTest.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/TenantManagerEventsTest.php b/tests/TenantManagerEventsTest.php index eca5077f..4dde735c 100644 --- a/tests/TenantManagerEventsTest.php +++ b/tests/TenantManagerEventsTest.php @@ -121,4 +121,18 @@ class TenantManagerEventsTest extends TestCase tenancy()->init('abc.localhost'); $this->assertSame('tenant', \DB::connection()->getConfig()['name']); } + + /** @test */ + public function tenant_is_persisted_before_the_created_hook_is_called() + { + $was_persisted = false; + + Tenancy::eventListener('tenant.created', function ($tenancy, $tenant) use (&$was_persisted) { + $was_persisted = $tenant->persisted; + }); + + Tenant::new()->save(); + + $this->assertTrue($was_persisted); + } } From b4cf658ad8ab98089971dc90275bcb3c20f26e2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Mon, 27 Jan 2020 18:21:08 +0100 Subject: [PATCH 10/16] Create SUPPORT.md --- SUPPORT.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 SUPPORT.md diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 00000000..8f782e51 --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,7 @@ +# Get Support + +If you need help with implementing the package, you can: +- Open an [issue here on GitHub](https://github.com/stancl/tenancy/issues/new?assignees=stancl&labels=support&template=support-question.md&title=) +- Message me (`@stancl`) on the [Unofficial Laravel Discord](https://discord.gg/zGVGFAd), in the `#stancl_tenancy` channel +- Contact me on Telegram: [@samuelstancl](https://t.me/samuelstancl) +- Send me an email: [samuel.stancl@gmail.com](mailto:samuel.stancl@gmail.com) From 01f080450d652d0e970064de9951daac3d678dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Mon, 27 Jan 2020 18:22:10 +0100 Subject: [PATCH 11/16] Add link to support --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7b7faab6..da359983 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ Documentation can be found here: https://tenancy.samuelstancl.me/docs/v2/ The repository with the documentation source code can be found here: [stancl/tenancy-docs](https://github.com/stancl/tenancy-docs). +### [Need help?](https://github.com/stancl/tenancy/blob/2.x/SUPPORT.md) + ### Credits - Created by [Samuel Štancl](https://github.com/stancl) From 95ee295da1527e280dea94d50054de90c754f7c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Mon, 10 Feb 2020 16:04:40 +0100 Subject: [PATCH 12/16] Fix PDO serialization exception (#289) * Fix PDO serialization exception when queueing tenant creation * Apply fixes from StyleCI * Fix $this->database() calls --- src/Contracts/Future/CanSetConnection.php | 4 +-- src/DatabaseManager.php | 2 +- .../MySQLDatabaseManager.php | 29 +++++++++++-------- .../PostgreSQLDatabaseManager.php | 28 +++++++++++------- 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/Contracts/Future/CanSetConnection.php b/src/Contracts/Future/CanSetConnection.php index 4fde6cb4..3c4d527b 100644 --- a/src/Contracts/Future/CanSetConnection.php +++ b/src/Contracts/Future/CanSetConnection.php @@ -4,12 +4,10 @@ declare(strict_types=1); namespace Stancl\Tenancy\Contracts\Future; -use Illuminate\Database\Connection; - /** * This interface *might* be part of the TenantDatabaseManager interface in 3.x. */ interface CanSetConnection { - public function setConnection(Connection $connection); + public function setConnection(string $connection): void; } diff --git a/src/DatabaseManager.php b/src/DatabaseManager.php index 7d653d86..9890e588 100644 --- a/src/DatabaseManager.php +++ b/src/DatabaseManager.php @@ -238,7 +238,7 @@ class DatabaseManager $databaseManager = $this->app[$databaseManagers[$driver]]; if ($connectionName !== 'tenant' && $databaseManager instanceof CanSetConnection) { - $databaseManager->setConnection($this->database->connection($connectionName)); + $databaseManager->setConnection($connectionName); } return $databaseManager; diff --git a/src/TenantDatabaseManagers/MySQLDatabaseManager.php b/src/TenantDatabaseManagers/MySQLDatabaseManager.php index 9ed211bc..f6c4ef96 100644 --- a/src/TenantDatabaseManagers/MySQLDatabaseManager.php +++ b/src/TenantDatabaseManagers/MySQLDatabaseManager.php @@ -6,40 +6,45 @@ namespace Stancl\Tenancy\TenantDatabaseManagers; use Illuminate\Contracts\Config\Repository; use Illuminate\Database\Connection; -use Illuminate\Database\DatabaseManager as IlluminateDatabaseManager; +use Illuminate\Support\Facades\DB; use Stancl\Tenancy\Contracts\Future\CanSetConnection; use Stancl\Tenancy\Contracts\TenantDatabaseManager; class MySQLDatabaseManager implements TenantDatabaseManager, CanSetConnection { - /** @var Connection */ - protected $database; + /** @var string */ + protected $connection; - public function __construct(Repository $config, IlluminateDatabaseManager $databaseManager) + public function __construct(Repository $config) { - $this->database = $databaseManager->connection($config['tenancy.database_manager_connections.mysql']); + $this->connection = $config->get('tenancy.database_manager_connections.mysql'); } - public function setConnection(Connection $connection) + protected function database(): Connection { - $this->database = $connection; + return DB::connection($this->connection); + } + + public function setConnection(string $connection): void + { + $this->connection = $connection; } public function createDatabase(string $name): bool { - $charset = $this->database->getConfig('charset'); - $collation = $this->database->getConfig('collation'); + $charset = $this->database()->getConfig('charset'); + $collation = $this->database()->getConfig('collation'); - return $this->database->statement("CREATE DATABASE `$name` CHARACTER SET `$charset` COLLATE `$collation`"); + return $this->database()->statement("CREATE DATABASE `$name` CHARACTER SET `$charset` COLLATE `$collation`"); } public function deleteDatabase(string $name): bool { - return $this->database->statement("DROP DATABASE `$name`"); + return $this->database()->statement("DROP DATABASE `$name`"); } public function databaseExists(string $name): bool { - return (bool) $this->database->select("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '$name'"); + return (bool) $this->database()->select("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '$name'"); } } diff --git a/src/TenantDatabaseManagers/PostgreSQLDatabaseManager.php b/src/TenantDatabaseManagers/PostgreSQLDatabaseManager.php index 688b357e..fc21668e 100644 --- a/src/TenantDatabaseManagers/PostgreSQLDatabaseManager.php +++ b/src/TenantDatabaseManagers/PostgreSQLDatabaseManager.php @@ -6,36 +6,42 @@ namespace Stancl\Tenancy\TenantDatabaseManagers; use Illuminate\Contracts\Config\Repository; use Illuminate\Database\Connection; -use Illuminate\Database\DatabaseManager as IlluminateDatabaseManager; +use Illuminate\Support\Facades\DB; +use Stancl\Tenancy\Contracts\Future\CanSetConnection; use Stancl\Tenancy\Contracts\TenantDatabaseManager; -class PostgreSQLDatabaseManager implements TenantDatabaseManager +class PostgreSQLDatabaseManager implements TenantDatabaseManager, CanSetConnection { - /** @var Connection */ - protected $database; + /** @var string */ + protected $connection; - public function __construct(Repository $config, IlluminateDatabaseManager $databaseManager) + public function __construct(Repository $config) { - $this->database = $databaseManager->connection($config['tenancy.database_manager_connections.pgsql']); + $this->connection = $config->get('tenancy.database_manager_connections.pgsql'); } - public function setConnection(Connection $connection) + protected function database(): Connection { - $this->database = $connection; + return DB::connection($this->connection); + } + + public function setConnection(string $connection): void + { + $this->connection = $connection; } public function createDatabase(string $name): bool { - return $this->database->statement("CREATE DATABASE \"$name\" WITH TEMPLATE=template0"); + return $this->database()->statement("CREATE DATABASE \"$name\" WITH TEMPLATE=template0"); } public function deleteDatabase(string $name): bool { - return $this->database->statement("DROP DATABASE \"$name\""); + return $this->database()->statement("DROP DATABASE \"$name\""); } public function databaseExists(string $name): bool { - return (bool) $this->database->select("SELECT datname FROM pg_database WHERE datname = '$name'"); + return (bool) $this->database()->select("SELECT datname FROM pg_database WHERE datname = '$name'"); } } From 06ee1ff0e28f131bf8a4e064cdd3e8a1c1b11293 Mon Sep 17 00:00:00 2001 From: curious-jim Date: Sun, 16 Feb 2020 19:10:50 +0800 Subject: [PATCH 13/16] Migration paths (#268) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * allow multiple paths for tenant migrations * tests: allow multiple paths for tenant migrations * Update tests/CommandsTest.php Co-Authored-By: Samuel Štancl * Update src/Traits/DealsWithMigrations.php Co-Authored-By: Samuel Štancl * Update tests/CommandsTest.php Co-Authored-By: Samuel Štancl * Update src/Traits/DealsWithMigrations.php Co-Authored-By: Samuel Štancl * Update tests/TestCase.php Co-Authored-By: Samuel Štancl Co-authored-by: Samuel Štancl --- src/Traits/DealsWithMigrations.php | 2 +- tests/CommandsTest.php | 2 +- tests/TestCase.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Traits/DealsWithMigrations.php b/src/Traits/DealsWithMigrations.php index a51bfa57..f730cf07 100644 --- a/src/Traits/DealsWithMigrations.php +++ b/src/Traits/DealsWithMigrations.php @@ -12,6 +12,6 @@ trait DealsWithMigrations return parent::getMigrationPaths(); } - return [config('tenancy.migrations_directory', database_path('migrations/tenant'))]; + return config('tenancy.migration_paths', [config('tenancy.migrations_directory') ?? database_path('migrations/tenant')]); } } diff --git a/tests/CommandsTest.php b/tests/CommandsTest.php index 071f929c..6bcf6fd9 100644 --- a/tests/CommandsTest.php +++ b/tests/CommandsTest.php @@ -18,7 +18,7 @@ class CommandsTest extends TestCase { parent::setUp(); - config(['tenancy.migrations_directory' => database_path('../migrations')]); + config(['tenancy.migration_paths', [database_path('../migrations')]]); } /** @test */ diff --git a/tests/TestCase.php b/tests/TestCase.php index 8df82561..336bff07 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -96,7 +96,7 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase 'tenancy.redis.tenancy' => env('TENANCY_TEST_REDIS_TENANCY', true), 'database.redis.client' => env('TENANCY_TEST_REDIS_CLIENT', 'phpredis'), 'tenancy.redis.prefixed_connections' => ['default'], - 'tenancy.migrations_directory' => database_path('../migrations'), + 'tenancy.migration_paths' => [database_path('../migrations')], 'tenancy.storage_drivers.db.connection' => 'central', 'tenancy.bootstrappers.redis' => \Stancl\Tenancy\TenancyBootstrappers\RedisTenancyBootstrapper::class, 'queue.connections.central' => [ From 5bb743f73dc5e7cc0d439bd44b21845d0cc72e5b Mon Sep 17 00:00:00 2001 From: Sean Taylor Date: Sat, 22 Feb 2020 12:58:30 +0000 Subject: [PATCH 14/16] Reinitialize tenancy for queued jobs if tenant id has changed (#276) * Reinitialize tenancy for queued jobs if tenant id has changed * Refactor condition logic for better readability --- src/TenancyServiceProvider.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/TenancyServiceProvider.php b/src/TenancyServiceProvider.php index 604d8a09..b5c41c73 100644 --- a/src/TenancyServiceProvider.php +++ b/src/TenancyServiceProvider.php @@ -109,11 +109,21 @@ class TenancyServiceProvider extends ServiceProvider // Queue tenancy $this->app['events']->listen(\Illuminate\Queue\Events\JobProcessing::class, function ($event) { - if (array_key_exists('tenant_id', $event->job->payload())) { - if (! tenancy()->initialized) { // dispatchNow - tenancy()->initialize(tenancy()->find($event->job->payload()['tenant_id'])); - } + $tenantId = $event->job->payload()['tenant_id'] ?? null; + + // The job is not tenant-aware + if (! $tenantId) { + return; } + + // Tenancy is already initialized for the tenant (e.g. dispatchNow was used) + if (tenancy()->initialized && tenant('id') === $tenantId) { + return; + } + + // Tenancy was either not initialized, or initialized for a different tenant. + // Therefore, we initialize it for the correct tenant. + tenancy()->initById($tenantId); }); } } From 98ce0ee29468e0b6c0519393ad096fe8a8aa267c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Wed, 26 Feb 2020 08:49:08 +0100 Subject: [PATCH 15/16] Make DB creation optional (#299) --- assets/config.php | 1 + src/TenantManager.php | 13 ++++++++++++- tests/TenantManagerTest.php | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/assets/config.php b/assets/config.php index 2657dfb6..9ff317ed 100644 --- a/assets/config.php +++ b/assets/config.php @@ -91,6 +91,7 @@ return [ // 'paypal_api_key' => 'services.paypal.api_key', ], 'home_url' => '/app', + 'create_database' => true, 'queue_database_creation' => false, 'migrate_after_creation' => false, // run migrations after creating a tenant 'migration_parameters' => [ diff --git a/src/TenantManager.php b/src/TenantManager.php index 2128da5a..d0d8b428 100644 --- a/src/TenantManager.php +++ b/src/TenantManager.php @@ -98,7 +98,9 @@ class TenantManager }; } - $this->database->createDatabase($tenant, $afterCreating); + if ($this->shouldCreateDatabase($tenant)) { + $this->database->createDatabase($tenant, $afterCreating); + } $this->event('tenant.created', $tenant); @@ -378,6 +380,15 @@ class TenantManager return array_diff_key($this->app['config']['tenancy.bootstrappers'], array_flip($except)); } + public function shouldCreateDatabase(Tenant $tenant): bool + { + if (array_key_exists('_tenancy_create_database', $tenant->data)) { + return $tenant->data['_tenancy_create_database']; + } + + return $this->app['config']['tenancy.create_database'] ?? true; + } + public function shouldMigrateAfterCreation(): bool { return $this->app['config']['tenancy.migrate_after_creation'] ?? false; diff --git a/tests/TenantManagerTest.php b/tests/TenantManagerTest.php index c3df859c..8e78a607 100644 --- a/tests/TenantManagerTest.php +++ b/tests/TenantManagerTest.php @@ -345,4 +345,38 @@ class TenantManagerTest extends TestCase $this->assertArrayHasKey('foo', $tenant->data); $this->assertArrayHasKey('abc123', $tenant->data); } + + /** @test */ + public function database_creation_can_be_disabled() + { + config(['tenancy.create_database' => false]); + + tenancy()->hook('database.creating', function () { + $this->fail(); + }); + + $tenant = Tenant::new()->save(); + + $this->assertTrue(true); + } + + /** @test */ + public function database_creation_can_be_disabled_for_specific_tenants() + { + config(['tenancy.create_database' => true]); + + tenancy()->hook('database.creating', function () { + $this->assertTrue(true); + }); + + $tenant = Tenant::new()->save(); + + tenancy()->hook('database.creating', function () { + $this->fail(); + }); + + $tenant2 = Tenant::new()->withData([ + '_tenancy_create_database' => false, + ])->save(); + } } From 292b7acd161675cee40b7f68a327d8f1b5f0092a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Wed, 26 Feb 2020 09:14:40 +0100 Subject: [PATCH 16/16] Make asset tenancy optional (#300) --- assets/config.php | 1 + .../FilesystemTenancyBootstrapper.php | 28 ++++++++++++------- tests/TenantAssetTest.php | 16 +++++++++++ 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/assets/config.php b/assets/config.php index 9ff317ed..d15fb08d 100644 --- a/assets/config.php +++ b/assets/config.php @@ -43,6 +43,7 @@ return [ 'filesystem' => [ // https://tenancy.samuelstancl.me/docs/v2/filesystem-tenancy/ 'suffix_base' => 'tenant', 'suffix_storage_path' => true, // Note: Disabling this will likely break local disk tenancy. Only disable this if you're using an external file storage service like S3. + 'asset_helper_tenancy' => true, // should asset() be automatically tenant-aware. You may want to disable this if you use tools like Horizon. // Disks which should be suffixed with the suffix_base + tenant id. 'disks' => [ 'local', diff --git a/src/TenancyBootstrappers/FilesystemTenancyBootstrapper.php b/src/TenancyBootstrappers/FilesystemTenancyBootstrapper.php index 5d417f8b..35ea5b2b 100644 --- a/src/TenancyBootstrappers/FilesystemTenancyBootstrapper.php +++ b/src/TenancyBootstrappers/FilesystemTenancyBootstrapper.php @@ -4,7 +4,8 @@ declare(strict_types=1); namespace Stancl\Tenancy\TenancyBootstrappers; -use Illuminate\Contracts\Foundation\Application; +use Illuminate\Filesystem\FilesystemAdapter; +use Illuminate\Foundation\Application; use Illuminate\Support\Facades\Storage; use Stancl\Tenancy\Contracts\TenancyBootstrapper; use Stancl\Tenancy\Tenant; @@ -43,23 +44,27 @@ class FilesystemTenancyBootstrapper implements TenancyBootstrapper } // asset() - if ($this->originalPaths['asset_url']) { - $this->app['config']['app.asset_url'] = ($this->originalPaths['asset_url'] ?? $this->app['config']['app.url']) . "/$suffix"; - $this->app['url']->setAssetRoot($this->app['config']['app.asset_url']); - } else { - $this->app['url']->setAssetRoot($this->app['url']->route('stancl.tenancy.asset', ['path' => ''])); + if ($this->app['config']['tenancy.filesystem.asset_helper_tenancy'] ?? true) { + if ($this->originalPaths['asset_url']) { + $this->app['config']['app.asset_url'] = ($this->originalPaths['asset_url'] ?? $this->app['config']['app.url']) . "/$suffix"; + $this->app['url']->setAssetRoot($this->app['config']['app.asset_url']); + } else { + $this->app['url']->setAssetRoot($this->app['url']->route('stancl.tenancy.asset', ['path' => ''])); + } } // Storage facade foreach ($this->app['config']['tenancy.filesystem.disks'] as $disk) { - $this->originalPaths['disks'][$disk] = Storage::disk($disk)->getAdapter()->getPathPrefix(); + /** @var FilesystemAdapter $filesystemDisk */ + $filesystemDisk = Storage::disk($disk); + $this->originalPaths['disks'][$disk] = $filesystemDisk->getAdapter()->getPathPrefix(); if ($root = str_replace('%storage_path%', storage_path(), $this->app['config']["tenancy.filesystem.root_override.{$disk}"])) { - Storage::disk($disk)->getAdapter()->setPathPrefix($root); + $filesystemDisk->getAdapter()->setPathPrefix($root); } else { $root = $this->app['config']["filesystems.disks.{$disk}.root"]; - Storage::disk($disk)->getAdapter()->setPathPrefix($root . "/{$suffix}"); + $filesystemDisk->getAdapter()->setPathPrefix($root . "/{$suffix}"); } } } @@ -75,7 +80,10 @@ class FilesystemTenancyBootstrapper implements TenancyBootstrapper // Storage facade foreach ($this->app['config']['tenancy.filesystem.disks'] as $disk) { - Storage::disk($disk)->getAdapter()->setPathPrefix($this->originalPaths['disks'][$disk]); + /** @var FilesystemAdapter $filesystemDisk */ + $filesystemDisk = Storage::disk($disk); + + $filesystemDisk->getAdapter()->setPathPrefix($this->originalPaths['disks'][$disk]); } } } diff --git a/tests/TenantAssetTest.php b/tests/TenantAssetTest.php index 248e7703..b9bbabad 100644 --- a/tests/TenantAssetTest.php +++ b/tests/TenantAssetTest.php @@ -68,4 +68,20 @@ class TenantAssetTest extends TestCase $this->assertSame($original, global_asset('foobar')); } + + /** @test */ + public function asset_helper_tenancy_can_be_disabled() + { + $original = asset('foo'); + + config([ + 'app.asset_url' => null, + 'tenancy.filesystem.asset_helper_tenancy' => false, + ]); + + Tenant::create('foo.localhost'); + tenancy()->init('foo.localhost'); + + $this->assertSame($original, asset('foo')); + } }