1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2026-02-06 04:44:04 +00:00

Merge branch '2.x' into support-schema-postgres

This commit is contained in:
Nuradiyana 2020-02-27 00:30:53 +07:00
commit f5f03f8097
22 changed files with 221 additions and 55 deletions

View file

@ -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(

View file

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Contracts\Future;
/**
* This interface *might* be part of the TenantDatabaseManager interface in 3.x.
*/
interface CanSetConnection
{
public function setConnection(string $connection): void;
}

View file

@ -8,6 +8,7 @@ use Closure;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\DatabaseManager as BaseDatabaseManager;
use Illuminate\Foundation\Application;
use Stancl\Tenancy\Contracts\Future\CanSetConnection;
use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Exceptions\DatabaseManagerNotRegisteredException;
use Stancl\Tenancy\Exceptions\TenantDatabaseAlreadyExistsException;
@ -228,7 +229,7 @@ class DatabaseManager
*/
public function getTenantDatabaseManager(Tenant $tenant): TenantDatabaseManager
{
$driver = $this->getDriver($this->getBaseConnection($tenant->getConnectionName()));
$driver = $this->getDriver($this->getBaseConnection($connectionName = $tenant->getConnectionName()));
$databaseManagers = $this->app['config']['tenancy.database_managers'];
@ -236,7 +237,13 @@ class DatabaseManager
throw new DatabaseManagerNotRegisteredException($driver);
}
return $this->app[$databaseManagers[$driver]];
$databaseManager = $this->app[$databaseManagers[$driver]];
if ($connectionName !== 'tenant' && $databaseManager instanceof CanSetConnection) {
$databaseManager->setConnection($connectionName);
}
return $databaseManager;
}
/**

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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]);
}
}
}

View file

@ -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);
});
}
}

View file

@ -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;
}

View file

@ -5,34 +5,46 @@ declare(strict_types=1);
namespace Stancl\Tenancy\TenantDatabaseManagers;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Database\DatabaseManager as IlluminateDatabaseManager;
use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\Contracts\Future\CanSetConnection;
use Stancl\Tenancy\Contracts\TenantDatabaseManager;
class MySQLDatabaseManager implements TenantDatabaseManager
class MySQLDatabaseManager implements TenantDatabaseManager, CanSetConnection
{
/** @var \Illuminate\Database\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');
}
protected function 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'");
}
}

View file

@ -5,31 +5,43 @@ declare(strict_types=1);
namespace Stancl\Tenancy\TenantDatabaseManagers;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Database\DatabaseManager as IlluminateDatabaseManager;
use Illuminate\Database\Connection;
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 \Illuminate\Database\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');
}
protected function 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'");
}
}

View file

@ -73,16 +73,18 @@ class TenantManager
$this->storage->createTenant($tenant);
$tenant->persisted = true;
/** @var \Illuminate\Contracts\Queue\ShouldQueue[]|callable[] $afterCreating */
$afterCreating = [];
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());
};
}
@ -96,7 +98,9 @@ class TenantManager
};
}
$this->database->createDatabase($tenant, $afterCreating);
if ($this->shouldCreateDatabase($tenant)) {
$this->database->createDatabase($tenant, $afterCreating);
}
$this->event('tenant.created', $tenant);
@ -376,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;
@ -401,6 +414,11 @@ class TenantManager
return $this->app['config']['tenancy.seeder_parameters'] ?? [];
}
public function getMigrationParameters()
{
return $this->app['config']['tenancy.migration_parameters'] ?? [];
}
/**
* Add an event listener.
*

View file

@ -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')]);
}
}

View file

@ -18,14 +18,19 @@ if (! function_exists('tenancy')) {
}
if (! function_exists('tenant')) {
/** @return Tenant|mixed */
/**
* Get a key from the current tenant's storage.
*
* @param string|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);
return optional(app(Tenant::class))->get($key) ?? null;
}
}
@ -52,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();

View file

@ -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');
});