mirror of
https://github.com/archtechx/tenancy.git
synced 2025-12-12 21:34:04 +00:00
Bootstrapper tests
This commit is contained in:
parent
73fc525126
commit
6f4b9f486c
20 changed files with 266 additions and 79 deletions
|
|
@ -8,7 +8,7 @@ use Stancl\Tenancy\Database\Models\Tenant;
|
||||||
return [
|
return [
|
||||||
'tenant_model' => Tenant::class,
|
'tenant_model' => Tenant::class,
|
||||||
'domain_model' => Domain::class,
|
'domain_model' => Domain::class,
|
||||||
'internal_prefix' => 'tenancy_',
|
'internal_column_prefix' => 'tenancy_',
|
||||||
|
|
||||||
'central_connection' => 'central',
|
'central_connection' => 'central',
|
||||||
'template_tenant_connection' => null,
|
'template_tenant_connection' => null,
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@ services:
|
||||||
DB_PASSWORD: password
|
DB_PASSWORD: password
|
||||||
DB_USERNAME: root
|
DB_USERNAME: root
|
||||||
DB_DATABASE: main
|
DB_DATABASE: main
|
||||||
|
TENANCY_TEST_REDIS_HOST: redis
|
||||||
|
TENANCY_TEST_MYSQL_HOST: mysql
|
||||||
|
TENANCY_TEST_PGSQL_HOST: postgres
|
||||||
stdin_open: true
|
stdin_open: true
|
||||||
tty: true
|
tty: true
|
||||||
mysql:
|
mysql:
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ namespace Stancl\Tenancy\Commands;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Database\Console\Migrations\MigrateCommand;
|
use Illuminate\Database\Console\Migrations\MigrateCommand;
|
||||||
use Illuminate\Database\Migrations\Migrator;
|
use Illuminate\Database\Migrations\Migrator;
|
||||||
|
use Stancl\Tenancy\Contracts\TenantWithDatabase;
|
||||||
use Stancl\Tenancy\DatabaseManager;
|
use Stancl\Tenancy\DatabaseManager;
|
||||||
use Stancl\Tenancy\Events\DatabaseMigrated;
|
use Stancl\Tenancy\Events\DatabaseMigrated;
|
||||||
use Stancl\Tenancy\Traits\DealsWithMigrations;
|
use Stancl\Tenancy\Traits\DealsWithMigrations;
|
||||||
|
|
@ -56,15 +57,20 @@ class Migrate extends MigrateCommand
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tenancy()->all($this->option('tenants'))->each(function ($tenant) {
|
tenancy()
|
||||||
$this->line("Tenant: {$tenant['id']}");
|
->query()
|
||||||
|
->when($this->option('tenants'), function ($query) {
|
||||||
|
$query->whereIn(tenancy()->model()->getTenantKeyName(), $this->option('tenants'));
|
||||||
|
})
|
||||||
|
->each(function (TenantWithDatabase $tenant) {
|
||||||
|
$this->line("Tenant: {$tenant['id']}");
|
||||||
|
|
||||||
$tenant->run(function () {
|
$tenant->run(function () {
|
||||||
// Migrate
|
// Migrate
|
||||||
parent::handle();
|
parent::handle();
|
||||||
|
});
|
||||||
|
|
||||||
|
event(new DatabaseMigrated($tenant));
|
||||||
});
|
});
|
||||||
|
|
||||||
event(new DatabaseMigrated($tenant));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,12 @@
|
||||||
|
|
||||||
namespace Stancl\Tenancy\Contracts;
|
namespace Stancl\Tenancy\Contracts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see \Stancl\Tenancy\Database\Models\Tenant
|
||||||
|
*/
|
||||||
interface Tenant
|
interface Tenant
|
||||||
{
|
{
|
||||||
public function getTenantKeyName(): string;
|
public function getTenantKeyName(): string;
|
||||||
public function getTenantKey(): string;
|
public function getTenantKey(): string;
|
||||||
|
public function run(callable $callback);
|
||||||
}
|
}
|
||||||
|
|
@ -4,8 +4,6 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Stancl\Tenancy\Contracts;
|
namespace Stancl\Tenancy\Contracts;
|
||||||
|
|
||||||
use Stancl\Tenancy\Tenant;
|
|
||||||
|
|
||||||
interface TenantDatabaseManager
|
interface TenantDatabaseManager
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
@ -18,12 +16,12 @@ interface TenantDatabaseManager
|
||||||
/**
|
/**
|
||||||
* Create a database.
|
* Create a database.
|
||||||
*/
|
*/
|
||||||
public function createDatabase(Tenant $tenant): bool;
|
public function createDatabase(TenantWithDatabase $tenant): bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a database.
|
* Delete a database.
|
||||||
*/
|
*/
|
||||||
public function deleteDatabase(Tenant $tenant): bool;
|
public function deleteDatabase(TenantWithDatabase $tenant): bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does a database exist.
|
* Does a database exist.
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use Stancl\Tenancy\Events;
|
||||||
use Stancl\Tenancy\Contracts;
|
use Stancl\Tenancy\Contracts;
|
||||||
|
|
||||||
// todo @property
|
// todo @property
|
||||||
class Tenant extends Model implements Contracts\Tenant
|
class Tenant extends Model implements Contracts\TenantWithDatabase
|
||||||
{
|
{
|
||||||
use Concerns\CentralConnection, Concerns\HasADataColumn, Concerns\GeneratesIds, Concerns\HasADataColumn {
|
use Concerns\CentralConnection, Concerns\HasADataColumn, Concerns\GeneratesIds, Concerns\HasADataColumn {
|
||||||
Concerns\HasADataColumn::getCasts as dataColumnCasts;
|
Concerns\HasADataColumn::getCasts as dataColumnCasts;
|
||||||
|
|
@ -41,7 +41,7 @@ class Tenant extends Model implements Contracts\Tenant
|
||||||
|
|
||||||
public static function internalPrefix(): string
|
public static function internalPrefix(): string
|
||||||
{
|
{
|
||||||
return config('tenancy.database_prefix');
|
return config('tenancy.internal_column_prefix');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -76,15 +76,15 @@ class Tenant extends Model implements Contracts\Tenant
|
||||||
|
|
||||||
public function run(callable $callback)
|
public function run(callable $callback)
|
||||||
{
|
{
|
||||||
// todo new logic with the manager
|
$originalTenant = tenant();
|
||||||
$originalTenant = $this->manager->getTenant();
|
|
||||||
|
|
||||||
$this->manager->initializeTenancy($this);
|
tenancy()->initialize($this);
|
||||||
$result = $callback($this);
|
$result = $callback($this);
|
||||||
$this->manager->endTenancy($this);
|
|
||||||
|
|
||||||
if ($originalTenant) {
|
if ($originalTenant) {
|
||||||
$this->manager->initializeTenancy($originalTenant);
|
tenancy()->initialize($originalTenant);
|
||||||
|
} else {
|
||||||
|
tenancy()->end();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ class DatabaseConfig
|
||||||
{
|
{
|
||||||
return $this->tenant->getInternal('db_connection')
|
return $this->tenant->getInternal('db_connection')
|
||||||
?? config('tenancy.template_tenant_connection')
|
?? config('tenancy.template_tenant_connection')
|
||||||
?? DatabaseManager::$originalDefaultConnectionName;
|
?? config('tenancy.central_connection');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -105,6 +105,7 @@ class DatabaseConfig
|
||||||
|
|
||||||
$templateConnection = config("database.connections.{$template}");
|
$templateConnection = config("database.connections.{$template}");
|
||||||
|
|
||||||
|
// todo move a lot of this logic to the tenant DB manager so that we dont have to deal with the separators & modifying DB names here
|
||||||
$databaseName = $this->getName();
|
$databaseName = $this->getName();
|
||||||
if (($manager = $this->manager()) instanceof ModifiesDatabaseNameForConnection) {
|
if (($manager = $this->manager()) instanceof ModifiesDatabaseNameForConnection) {
|
||||||
/** @var ModifiesDatabaseNameForConnection $manager */
|
/** @var ModifiesDatabaseNameForConnection $manager */
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||||
namespace Stancl\Tenancy;
|
namespace Stancl\Tenancy;
|
||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
|
use Illuminate\Config\Repository;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Database\DatabaseManager as BaseDatabaseManager;
|
use Illuminate\Database\DatabaseManager as BaseDatabaseManager;
|
||||||
use Illuminate\Foundation\Application;
|
use Illuminate\Foundation\Application;
|
||||||
|
|
@ -18,58 +19,43 @@ use Stancl\Tenancy\Jobs\QueuedTenantDatabaseDeleter;
|
||||||
/**
|
/**
|
||||||
* @internal Class is subject to breaking changes in minor and patch versions.
|
* @internal Class is subject to breaking changes in minor and patch versions.
|
||||||
*/
|
*/
|
||||||
// todo rewrite everything
|
|
||||||
class DatabaseManager
|
class DatabaseManager
|
||||||
{
|
{
|
||||||
/** @var string */
|
|
||||||
public static $originalDefaultConnectionName;
|
|
||||||
|
|
||||||
/** @var Application */
|
/** @var Application */
|
||||||
protected $app;
|
protected $app;
|
||||||
|
|
||||||
/** @var BaseDatabaseManager */
|
/** @var BaseDatabaseManager */
|
||||||
protected $database;
|
protected $database;
|
||||||
|
|
||||||
/** @var TenantManager */
|
/** @var Repository */
|
||||||
protected $tenancy;
|
protected $config;
|
||||||
|
|
||||||
public function __construct(Application $app, BaseDatabaseManager $database)
|
public function __construct(Application $app, BaseDatabaseManager $database, Repository $config)
|
||||||
{
|
{
|
||||||
$this->app = $app;
|
$this->app = $app;
|
||||||
$this->database = $database;
|
$this->database = $database;
|
||||||
static::$originalDefaultConnectionName = $app['config']['database.default'];
|
$this->config = $config;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the TenantManager instance, used to dispatch tenancy events.
|
|
||||||
*/
|
|
||||||
public function withTenantManager(Tenancy $tenantManager): self
|
|
||||||
{
|
|
||||||
$this->tenancy = $tenantManager;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect to a tenant's database.
|
* Connect to a tenant's database.
|
||||||
*/
|
*/
|
||||||
public function connect(TenantWithDatabase $tenant)
|
public function connectToTenant(TenantWithDatabase $tenant)
|
||||||
{
|
{
|
||||||
$this->createTenantConnection($tenant);
|
$this->createTenantConnection($tenant);
|
||||||
$this->setDefaultConnection('tenant');
|
$this->setDefaultConnection('tenant');
|
||||||
$this->switchConnection('tenant');
|
$this->database->purge('tenant');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reconnect to the default non-tenant connection.
|
* Reconnect to the default non-tenant connection.
|
||||||
*/
|
*/
|
||||||
public function reconnect()
|
public function reconnectToCentral()
|
||||||
{
|
{
|
||||||
if ($this->tenancy->initialized) {
|
if (tenancy()->initialized) {
|
||||||
$this->database->purge('tenant');
|
$this->database->purge('tenant');
|
||||||
}
|
}
|
||||||
$this->setDefaultConnection(static::$originalDefaultConnectionName);
|
$this->setDefaultConnection($this->config->get('tenancy.central_connection'));
|
||||||
$this->switchConnection(static::$originalDefaultConnectionName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -78,25 +64,17 @@ class DatabaseManager
|
||||||
public function setDefaultConnection(string $connection)
|
public function setDefaultConnection(string $connection)
|
||||||
{
|
{
|
||||||
$this->app['config']['database.default'] = $connection;
|
$this->app['config']['database.default'] = $connection;
|
||||||
|
$this->database->setDefaultConnection($connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the tenant database connection.
|
* Create the tenant database connection.
|
||||||
*/
|
*/
|
||||||
public function createTenantConnection(Tenant $tenant)
|
public function createTenantConnection(TenantWithDatabase $tenant)
|
||||||
{
|
{
|
||||||
$this->app['config']['database.connections.tenant'] = $tenant->database()->connection();
|
$this->app['config']['database.connections.tenant'] = $tenant->database()->connection();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Switch the application's connection.
|
|
||||||
*/
|
|
||||||
public function switchConnection(string $connection)
|
|
||||||
{
|
|
||||||
$this->database->reconnect($connection);
|
|
||||||
$this->database->setDefaultConnection($connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a tenant can be created.
|
* Check if a tenant can be created.
|
||||||
*
|
*
|
||||||
|
|
@ -104,7 +82,7 @@ class DatabaseManager
|
||||||
* @throws DatabaseManagerNotRegisteredException
|
* @throws DatabaseManagerNotRegisteredException
|
||||||
* @throws TenantDatabaseAlreadyExistsException
|
* @throws TenantDatabaseAlreadyExistsException
|
||||||
*/
|
*/
|
||||||
public function ensureTenantCanBeCreated(Tenant $tenant): void
|
public function ensureTenantCanBeCreated(TenantWithDatabase $tenant): void
|
||||||
{
|
{
|
||||||
if ($tenant->database()->manager()->databaseExists($database = $tenant->database()->getName())) {
|
if ($tenant->database()->manager()->databaseExists($database = $tenant->database()->getName())) {
|
||||||
throw new TenantDatabaseAlreadyExistsException($database);
|
throw new TenantDatabaseAlreadyExistsException($database);
|
||||||
|
|
@ -119,8 +97,9 @@ class DatabaseManager
|
||||||
* @return void
|
* @return void
|
||||||
* @throws DatabaseManagerNotRegisteredException
|
* @throws DatabaseManagerNotRegisteredException
|
||||||
*/
|
*/
|
||||||
public function createDatabase(Tenant $tenant, array $afterCreating = [])
|
public function createDatabase(TenantWithDatabase $tenant, array $afterCreating = [])
|
||||||
{
|
{
|
||||||
|
// todo get rid of aftercreating logic
|
||||||
$afterCreating = array_merge(
|
$afterCreating = array_merge(
|
||||||
$afterCreating,
|
$afterCreating,
|
||||||
$this->tenancy->event('database.creating', $tenant->database()->getName(), $tenant)
|
$this->tenancy->event('database.creating', $tenant->database()->getName(), $tenant)
|
||||||
|
|
@ -168,7 +147,7 @@ class DatabaseManager
|
||||||
*
|
*
|
||||||
* @throws DatabaseManagerNotRegisteredException
|
* @throws DatabaseManagerNotRegisteredException
|
||||||
*/
|
*/
|
||||||
public function deleteDatabase(Tenant $tenant)
|
public function deleteDatabase(TenantWithDatabase $tenant)
|
||||||
{
|
{
|
||||||
$database = $tenant->database()->getName();
|
$database = $tenant->database()->getName();
|
||||||
$manager = $tenant->database()->manager();
|
$manager = $tenant->database()->manager();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
namespace Stancl\Tenancy\Events\Contracts;
|
namespace Stancl\Tenancy\Events\Contracts;
|
||||||
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Stancl\Tenancy\Database\Models\Domain;
|
use Stancl\Tenancy\Contracts\Domain;
|
||||||
|
|
||||||
abstract class DomainEvent
|
abstract class DomainEvent
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
namespace Stancl\Tenancy\Events\Contracts;
|
namespace Stancl\Tenancy\Events\Contracts;
|
||||||
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Stancl\Tenancy\Database\Models\Tenant;
|
use Stancl\Tenancy\Contracts\Tenant;
|
||||||
|
|
||||||
abstract class TenantEvent
|
abstract class TenantEvent
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ class JobPipeline implements ShouldQueue
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
foreach ($this->jobs as $job) {
|
foreach ($this->jobs as $job) {
|
||||||
app($job)->handle($this->passable);
|
app()->call([new $job(...$this->passable), 'handle']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,7 +82,10 @@ class JobPipeline implements ShouldQueue
|
||||||
{
|
{
|
||||||
$clone = clone $this;
|
$clone = clone $this;
|
||||||
|
|
||||||
$clone->passable = ($clone->send)(...$listenerArgs);
|
$passable = ($clone->send)(...$listenerArgs);
|
||||||
|
$passable = is_array($passable) ? $passable : [$passable];
|
||||||
|
|
||||||
|
$clone->passable = $passable;
|
||||||
unset($clone->send);
|
unset($clone->send);
|
||||||
|
|
||||||
return $clone;
|
return $clone;
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,18 @@ namespace Stancl\Tenancy\Jobs;
|
||||||
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Stancl\Tenancy\Database\Models\Tenant;
|
use Stancl\Tenancy\Contracts\TenantWithDatabase;
|
||||||
|
use Stancl\Tenancy\Contracts\Tenant;
|
||||||
|
|
||||||
class CreateDatabase implements ShouldQueue
|
class CreateDatabase implements ShouldQueue
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
/** @var Tenant */
|
/** @var TenantWithDatabase|Model */
|
||||||
protected $tenant;
|
protected $tenant;
|
||||||
|
|
||||||
public function __construct(Tenant $tenant)
|
public function __construct(Tenant $tenant)
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,14 @@
|
||||||
|
|
||||||
namespace Stancl\Tenancy;
|
namespace Stancl\Tenancy;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
||||||
use Stancl\Tenancy\Contracts\Tenant;
|
use Stancl\Tenancy\Contracts\Tenant;
|
||||||
|
|
||||||
class Tenancy
|
class Tenancy
|
||||||
{
|
{
|
||||||
/** @var Tenant|null */
|
/** @var Tenant|Model|null */
|
||||||
public $tenant;
|
public $tenant;
|
||||||
|
|
||||||
/** @var callable|null */
|
/** @var callable|null */
|
||||||
|
|
@ -48,4 +50,17 @@ class Tenancy
|
||||||
|
|
||||||
return $resolve($this->tenant);
|
return $resolve($this->tenant);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function query(): Builder
|
||||||
|
{
|
||||||
|
return $this->model()->query();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return Tenant|Model */
|
||||||
|
public function model()
|
||||||
|
{
|
||||||
|
$class = config('tenancy.tenant_model');
|
||||||
|
|
||||||
|
return new $class;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ namespace Stancl\Tenancy\TenancyBootstrappers;
|
||||||
|
|
||||||
use Illuminate\Cache\CacheManager;
|
use Illuminate\Cache\CacheManager;
|
||||||
use Illuminate\Contracts\Foundation\Application;
|
use Illuminate\Contracts\Foundation\Application;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Stancl\Tenancy\CacheManager as TenantCacheManager;
|
use Stancl\Tenancy\CacheManager as TenantCacheManager;
|
||||||
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
||||||
use Stancl\Tenancy\Contracts\Tenant;
|
use Stancl\Tenancy\Contracts\Tenant;
|
||||||
|
|
@ -25,6 +26,8 @@ class CacheTenancyBootstrapper implements TenancyBootstrapper
|
||||||
|
|
||||||
public function start(Tenant $tenant)
|
public function start(Tenant $tenant)
|
||||||
{
|
{
|
||||||
|
$this->resetFacadeCache();
|
||||||
|
|
||||||
$this->originalCache = $this->originalCache ?? $this->app['cache'];
|
$this->originalCache = $this->originalCache ?? $this->app['cache'];
|
||||||
$this->app->extend('cache', function () {
|
$this->app->extend('cache', function () {
|
||||||
return new TenantCacheManager($this->app);
|
return new TenantCacheManager($this->app);
|
||||||
|
|
@ -33,10 +36,22 @@ class CacheTenancyBootstrapper implements TenancyBootstrapper
|
||||||
|
|
||||||
public function end()
|
public function end()
|
||||||
{
|
{
|
||||||
|
$this->resetFacadeCache();
|
||||||
|
|
||||||
$this->app->extend('cache', function () {
|
$this->app->extend('cache', function () {
|
||||||
return $this->originalCache;
|
return $this->originalCache;
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->originalCache = null;
|
$this->originalCache = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This wouldn't be necessary, but is needed when a call to the
|
||||||
|
* facade has been made prior to bootstrapping tenancy. The
|
||||||
|
* facade has its own cache, separate from the container.
|
||||||
|
*/
|
||||||
|
public function resetFacadeCache()
|
||||||
|
{
|
||||||
|
Cache::clearResolvedInstances();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
||||||
use Stancl\Tenancy\DatabaseManager;
|
use Stancl\Tenancy\DatabaseManager;
|
||||||
use Stancl\Tenancy\Exceptions\TenantDatabaseDoesNotExistException;
|
use Stancl\Tenancy\Exceptions\TenantDatabaseDoesNotExistException;
|
||||||
use Stancl\Tenancy\Contracts\TenantWithDatabase;
|
use Stancl\Tenancy\Contracts\TenantWithDatabase;
|
||||||
|
use Stancl\Tenancy\Contracts\Tenant;
|
||||||
|
|
||||||
class DatabaseTenancyBootstrapper implements TenancyBootstrapper
|
class DatabaseTenancyBootstrapper implements TenancyBootstrapper
|
||||||
{
|
{
|
||||||
|
|
@ -19,8 +20,10 @@ class DatabaseTenancyBootstrapper implements TenancyBootstrapper
|
||||||
$this->database = $database;
|
$this->database = $database;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function start(TenantWithDatabase $tenant)
|
public function start(Tenant $tenant)
|
||||||
{
|
{
|
||||||
|
/** @var TenantWithDatabase $tenant */
|
||||||
|
|
||||||
$database = $tenant->database()->getName();
|
$database = $tenant->database()->getName();
|
||||||
if (! $tenant->database()->manager()->databaseExists($database)) {
|
if (! $tenant->database()->manager()->databaseExists($database)) {
|
||||||
throw new TenantDatabaseDoesNotExistException($database);
|
throw new TenantDatabaseDoesNotExistException($database);
|
||||||
|
|
@ -31,6 +34,6 @@ class DatabaseTenancyBootstrapper implements TenancyBootstrapper
|
||||||
|
|
||||||
public function end()
|
public function end()
|
||||||
{
|
{
|
||||||
$this->database->revertToCentral();
|
$this->database->reconnectToCentral();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,9 @@ use Illuminate\Cache\CacheManager;
|
||||||
use Illuminate\Contracts\Http\Kernel;
|
use Illuminate\Contracts\Http\Kernel;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Stancl\Tenancy\Database\Models\Tenant;
|
|
||||||
use Stancl\Tenancy\Database\TenantObserver;
|
|
||||||
use Stancl\Tenancy\StorageDrivers\Database\DatabaseStorageDriver;
|
use Stancl\Tenancy\StorageDrivers\Database\DatabaseStorageDriver;
|
||||||
use Stancl\Tenancy\TenancyBootstrappers\FilesystemTenancyBootstrapper;
|
use Stancl\Tenancy\TenancyBootstrappers\FilesystemTenancyBootstrapper;
|
||||||
|
use Stancl\Tenancy\Contracts\Tenant;
|
||||||
|
|
||||||
class TenancyServiceProvider extends ServiceProvider
|
class TenancyServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ namespace Stancl\Tenancy\TenantDatabaseManagers;
|
||||||
|
|
||||||
use Stancl\Tenancy\Contracts\ModifiesDatabaseNameForConnection;
|
use Stancl\Tenancy\Contracts\ModifiesDatabaseNameForConnection;
|
||||||
use Stancl\Tenancy\Contracts\TenantDatabaseManager;
|
use Stancl\Tenancy\Contracts\TenantDatabaseManager;
|
||||||
use Stancl\Tenancy\Tenant;
|
use Stancl\Tenancy\Contracts\TenantWithDatabase;
|
||||||
|
|
||||||
class SQLiteDatabaseManager implements TenantDatabaseManager, ModifiesDatabaseNameForConnection
|
class SQLiteDatabaseManager implements TenantDatabaseManager, ModifiesDatabaseNameForConnection
|
||||||
{
|
{
|
||||||
|
|
@ -15,7 +15,7 @@ class SQLiteDatabaseManager implements TenantDatabaseManager, ModifiesDatabaseNa
|
||||||
return 'database';
|
return 'database';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createDatabase(Tenant $tenant): bool
|
public function createDatabase(TenantWithDatabase $tenant): bool
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return fclose(fopen(database_path($tenant->database()->getName()), 'w'));
|
return fclose(fopen(database_path($tenant->database()->getName()), 'w'));
|
||||||
|
|
@ -24,7 +24,7 @@ class SQLiteDatabaseManager implements TenantDatabaseManager, ModifiesDatabaseNa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deleteDatabase(Tenant $tenant): bool
|
public function deleteDatabase(TenantWithDatabase $tenant): bool
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return unlink(database_path($tenant->database()->getName()));
|
return unlink(database_path($tenant->database()->getName()));
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Stancl\Tenancy\Database\Models\Tenant;
|
|
||||||
use Stancl\Tenancy\Tenancy;
|
use Stancl\Tenancy\Tenancy;
|
||||||
|
use Stancl\Tenancy\Contracts\Tenant;
|
||||||
|
|
||||||
if (! function_exists('tenancy')) {
|
if (! function_exists('tenancy')) {
|
||||||
/** @return Tenancy */
|
/** @return Tenancy */
|
||||||
|
|
@ -18,10 +18,14 @@ if (! function_exists('tenant')) {
|
||||||
* Get a key from the current tenant's storage.
|
* Get a key from the current tenant's storage.
|
||||||
*
|
*
|
||||||
* @param string|null $key
|
* @param string|null $key
|
||||||
* @return Tenant|mixed
|
* @return Tenant|null|mixed
|
||||||
*/
|
*/
|
||||||
function tenant($key = null)
|
function tenant($key = null)
|
||||||
{
|
{
|
||||||
|
if (! app()->bound(Tenant::class)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (is_null($key)) {
|
if (is_null($key)) {
|
||||||
return app(Tenant::class);
|
return app(Tenant::class);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,37 +2,185 @@
|
||||||
|
|
||||||
namespace Stancl\Tenancy\Tests\v3;
|
namespace Stancl\Tenancy\Tests\v3;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Event;
|
||||||
|
use Illuminate\Support\Facades\Redis;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Stancl\Tenancy\Database\Models\Tenant;
|
||||||
|
use Stancl\Tenancy\Events\Listeners\BootstrapTenancy;
|
||||||
|
use Stancl\Tenancy\Events\Listeners\JobPipeline;
|
||||||
|
use Stancl\Tenancy\Events\Listeners\RevertToCentralContext;
|
||||||
|
use Stancl\Tenancy\Events\TenancyEnded;
|
||||||
|
use Stancl\Tenancy\Events\TenancyInitialized;
|
||||||
|
use Stancl\Tenancy\Events\TenantCreated;
|
||||||
|
use Stancl\Tenancy\Jobs\CreateDatabase;
|
||||||
|
use Stancl\Tenancy\TenancyBootstrappers\CacheTenancyBootstrapper;
|
||||||
|
use Stancl\Tenancy\TenancyBootstrappers\DatabaseTenancyBootstrapper;
|
||||||
|
use Stancl\Tenancy\TenancyBootstrappers\FilesystemTenancyBootstrapper;
|
||||||
|
use Stancl\Tenancy\TenancyBootstrappers\RedisTenancyBootstrapper;
|
||||||
use Stancl\Tenancy\Tests\TestCase;
|
use Stancl\Tenancy\Tests\TestCase;
|
||||||
|
|
||||||
class BootstrapperTest extends TestCase
|
class BootstrapperTest extends TestCase
|
||||||
{
|
{
|
||||||
|
public $mockConsoleOutput = false;
|
||||||
|
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
Event::listen(
|
||||||
|
TenantCreated::class,
|
||||||
|
JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
|
||||||
|
return $event->tenant;
|
||||||
|
})->toListener()
|
||||||
|
);
|
||||||
|
|
||||||
|
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||||
|
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
|
||||||
|
}
|
||||||
|
|
||||||
/** @test */
|
/** @test */
|
||||||
public function database_data_is_separated()
|
public function database_data_is_separated()
|
||||||
{
|
{
|
||||||
|
config(['tenancy.bootstrappers' => [
|
||||||
|
DatabaseTenancyBootstrapper::class
|
||||||
|
]]);
|
||||||
|
|
||||||
|
$tenant1 = Tenant::create();
|
||||||
|
$tenant2 = Tenant::create();
|
||||||
|
|
||||||
|
$this->artisan('tenants:migrate');
|
||||||
|
|
||||||
|
tenancy()->initialize($tenant1);
|
||||||
|
|
||||||
|
// Create Foo user
|
||||||
|
DB::table('users')->insert(['name' => 'Foo', 'email' => 'foo@bar.com', 'password' => 'secret']);
|
||||||
|
$this->assertCount(1, DB::table('users')->get());
|
||||||
|
|
||||||
|
tenancy()->initialize($tenant2);
|
||||||
|
|
||||||
|
// Assert Foo user is not in this DB
|
||||||
|
$this->assertCount(0, DB::table('users')->get());
|
||||||
|
// Create Bar user
|
||||||
|
DB::table('users')->insert(['name' => 'Bar', 'email' => 'bar@bar.com', 'password' => 'secret']);
|
||||||
|
$this->assertCount(1, DB::table('users')->get());
|
||||||
|
|
||||||
|
tenancy()->initialize($tenant1);
|
||||||
|
|
||||||
|
// Assert Bar user is not in this DB
|
||||||
|
$this->assertCount(1, DB::table('users')->get());
|
||||||
|
$this->assertSame('Foo', DB::table('users')->first()->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @test */
|
/** @test */
|
||||||
public function cache_data_is_separated()
|
public function cache_data_is_separated()
|
||||||
{
|
{
|
||||||
|
config([
|
||||||
|
'tenancy.bootstrappers' => [
|
||||||
|
CacheTenancyBootstrapper::class
|
||||||
|
],
|
||||||
|
'cache.default' => 'redis',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$tenant1 = Tenant::create();
|
||||||
|
$tenant2 = Tenant::create();
|
||||||
|
|
||||||
|
cache()->set('foo', 'central');
|
||||||
|
$this->assertSame('central', Cache::get('foo'));
|
||||||
|
|
||||||
|
tenancy()->initialize($tenant1);
|
||||||
|
|
||||||
|
// Assert central cache doesn't leak to tenant context
|
||||||
|
$this->assertFalse(Cache::has('foo'));
|
||||||
|
|
||||||
|
cache()->set('foo', 'bar');
|
||||||
|
$this->assertSame('bar', Cache::get('foo'));
|
||||||
|
|
||||||
|
tenancy()->initialize($tenant2);
|
||||||
|
|
||||||
|
// Assert one tenant's data doesn't leak to another tenant
|
||||||
|
$this->assertFalse(Cache::has('foo'));
|
||||||
|
|
||||||
|
cache()->set('foo', 'xyz');
|
||||||
|
$this->assertSame('xyz', Cache::get('foo'));
|
||||||
|
|
||||||
|
tenancy()->initialize($tenant1);
|
||||||
|
|
||||||
|
// Asset data didn't leak to original tenant
|
||||||
|
$this->assertSame('bar', Cache::get('foo'));
|
||||||
|
|
||||||
|
tenancy()->end();
|
||||||
|
|
||||||
|
// Asset central is still the same
|
||||||
|
$this->assertSame('central', Cache::get('foo'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @test */
|
/** @test */
|
||||||
public function redis_data_is_separated()
|
public function redis_data_is_separated()
|
||||||
{
|
{
|
||||||
|
config(['tenancy.bootstrappers' => [
|
||||||
|
RedisTenancyBootstrapper::class
|
||||||
|
]]);
|
||||||
|
|
||||||
|
$tenant1 = Tenant::create();
|
||||||
|
$tenant2 = Tenant::create();
|
||||||
|
|
||||||
|
tenancy()->initialize($tenant1);
|
||||||
|
Redis::set('foo', 'bar');
|
||||||
|
$this->assertSame('bar', Redis::get('foo'));
|
||||||
|
|
||||||
|
tenancy()->initialize($tenant2);
|
||||||
|
$this->assertSame(null, Redis::get('foo'));
|
||||||
|
Redis::set('foo', 'xyz');
|
||||||
|
Redis::set('abc', 'def');
|
||||||
|
$this->assertSame('xyz', Redis::get('foo'));
|
||||||
|
$this->assertSame('def', Redis::get('abc'));
|
||||||
|
|
||||||
|
tenancy()->initialize($tenant1);
|
||||||
|
$this->assertSame('bar', Redis::get('foo'));
|
||||||
|
$this->assertSame(null, Redis::get('abc'));
|
||||||
|
|
||||||
|
$tenant3 = Tenant::create();
|
||||||
|
tenancy()->initialize($tenant3);
|
||||||
|
$this->assertSame(null, Redis::get('foo'));
|
||||||
|
$this->assertSame(null, Redis::get('abc'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @test */
|
/** @test */
|
||||||
public function filesystem_data_is_separated()
|
public function filesystem_data_is_separated()
|
||||||
{
|
{
|
||||||
|
config(['tenancy.bootstrappers' => [
|
||||||
|
FilesystemTenancyBootstrapper::class
|
||||||
|
]]);
|
||||||
|
|
||||||
|
$tenant1 = Tenant::create();
|
||||||
|
$tenant2 = Tenant::create();
|
||||||
|
|
||||||
|
tenancy()->initialize($tenant1);
|
||||||
|
Storage::disk('public')->put('foo', 'bar');
|
||||||
|
$this->assertSame('bar', Storage::disk('public')->get('foo'));
|
||||||
|
|
||||||
|
tenancy()->initialize($tenant2);
|
||||||
|
$this->assertFalse(Storage::disk('public')->exists('foo'));
|
||||||
|
Storage::disk('public')->put('foo', 'xyz');
|
||||||
|
Storage::disk('public')->put('abc', 'def');
|
||||||
|
$this->assertSame('xyz', Storage::disk('public')->get('foo'));
|
||||||
|
$this->assertSame('def', Storage::disk('public')->get('abc'));
|
||||||
|
|
||||||
|
tenancy()->initialize($tenant1);
|
||||||
|
$this->assertSame('bar', Storage::disk('public')->get('foo'));
|
||||||
|
$this->assertFalse(Storage::disk('public')->exists('abc'));
|
||||||
|
|
||||||
|
$tenant3 = Tenant::create();
|
||||||
|
tenancy()->initialize($tenant3);
|
||||||
|
$this->assertFalse(Storage::disk('public')->exists('foo'));
|
||||||
|
$this->assertFalse(Storage::disk('public')->exists('abc'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @test */
|
/** @test */
|
||||||
public function queue_data_is_separated()
|
public function queue_data_is_separated()
|
||||||
{
|
{
|
||||||
|
// todo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use Stancl\Tenancy\Events\Listeners\JobPipeline;
|
||||||
use Stancl\Tenancy\Events\TenantCreated;
|
use Stancl\Tenancy\Events\TenantCreated;
|
||||||
use Stancl\Tenancy\Tests\TestCase;
|
use Stancl\Tenancy\Tests\TestCase;
|
||||||
|
|
||||||
|
// todo the shouldQueue() doesnt make sense? test if it really works or if its just because of sync queue driver
|
||||||
class JobPipelineTest extends TestCase
|
class JobPipelineTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @test */
|
/** @test */
|
||||||
|
|
@ -19,7 +20,7 @@ class JobPipelineTest extends TestCase
|
||||||
])->toListener());
|
])->toListener());
|
||||||
|
|
||||||
$this->assertFalse(app()->bound('foo'));
|
$this->assertFalse(app()->bound('foo'));
|
||||||
|
|
||||||
Tenant::create();
|
Tenant::create();
|
||||||
|
|
||||||
$this->assertSame('bar', app('foo'));
|
$this->assertSame('bar', app('foo'));
|
||||||
|
|
@ -60,6 +61,12 @@ class JobPipelineTest extends TestCase
|
||||||
|
|
||||||
$this->assertSame('first job changed property', app('foo'));
|
$this->assertSame('first job changed property', app('foo'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function send_can_return_multiple_arguments()
|
||||||
|
{
|
||||||
|
// todo
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FooJob
|
class FooJob
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue