mirror of
https://github.com/archtechx/tenancy.git
synced 2025-12-12 14:54:04 +00:00
Automatic mode
This commit is contained in:
parent
2492345280
commit
73fc525126
16 changed files with 154 additions and 40 deletions
|
|
@ -4,10 +4,8 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Stancl\Tenancy\Contracts;
|
namespace Stancl\Tenancy\Contracts;
|
||||||
|
|
||||||
use Stancl\Tenancy\Tenant;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TenancyBootstrappers are classes that make existing code tenant-aware.
|
* TenancyBootstrappers are classes that make your application tenant-aware automatically.
|
||||||
*/
|
*/
|
||||||
interface TenancyBootstrapper
|
interface TenancyBootstrapper
|
||||||
{
|
{
|
||||||
|
|
|
||||||
10
src/Contracts/TenantWithDatabase.php
Normal file
10
src/Contracts/TenantWithDatabase.php
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Stancl\Tenancy\Contracts;
|
||||||
|
|
||||||
|
use Stancl\Tenancy\DatabaseConfig;
|
||||||
|
|
||||||
|
interface TenantWithDatabase extends Tenant
|
||||||
|
{
|
||||||
|
public function database(): DatabaseConfig;
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ 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;
|
||||||
use Stancl\Tenancy\Contracts\TenantCannotBeCreatedException;
|
use Stancl\Tenancy\Contracts\TenantCannotBeCreatedException;
|
||||||
|
use Stancl\Tenancy\Contracts\TenantWithDatabase;
|
||||||
use Stancl\Tenancy\Exceptions\DatabaseManagerNotRegisteredException;
|
use Stancl\Tenancy\Exceptions\DatabaseManagerNotRegisteredException;
|
||||||
use Stancl\Tenancy\Exceptions\TenantDatabaseAlreadyExistsException;
|
use Stancl\Tenancy\Exceptions\TenantDatabaseAlreadyExistsException;
|
||||||
use Stancl\Tenancy\Jobs\QueuedTenantDatabaseCreator;
|
use Stancl\Tenancy\Jobs\QueuedTenantDatabaseCreator;
|
||||||
|
|
@ -17,6 +18,7 @@ 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 */
|
/** @var string */
|
||||||
|
|
@ -41,7 +43,7 @@ class DatabaseManager
|
||||||
/**
|
/**
|
||||||
* Set the TenantManager instance, used to dispatch tenancy events.
|
* Set the TenantManager instance, used to dispatch tenancy events.
|
||||||
*/
|
*/
|
||||||
public function withTenantManager(TenantManager $tenantManager): self
|
public function withTenantManager(Tenancy $tenantManager): self
|
||||||
{
|
{
|
||||||
$this->tenancy = $tenantManager;
|
$this->tenancy = $tenantManager;
|
||||||
|
|
||||||
|
|
@ -51,7 +53,7 @@ class DatabaseManager
|
||||||
/**
|
/**
|
||||||
* Connect to a tenant's database.
|
* Connect to a tenant's database.
|
||||||
*/
|
*/
|
||||||
public function connect(Tenant $tenant)
|
public function connect(TenantWithDatabase $tenant)
|
||||||
{
|
{
|
||||||
$this->createTenantConnection($tenant);
|
$this->createTenantConnection($tenant);
|
||||||
$this->setDefaultConnection('tenant');
|
$this->setDefaultConnection('tenant');
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Stancl\Tenancy\Events\Listeners;
|
||||||
|
|
||||||
|
use Stancl\Tenancy\Events\TenancyInitialized;
|
||||||
|
|
||||||
|
class BootstrapTenancy
|
||||||
|
{
|
||||||
|
public function handle(TenancyInitialized $event)
|
||||||
|
{
|
||||||
|
foreach ($event->tenancy->getBootstrappers() as $bootstrapper) {
|
||||||
|
$bootstrapper->start($event->tenancy->tenant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,12 +4,12 @@ namespace Stancl\Tenancy\Events\Listeners;
|
||||||
|
|
||||||
use Stancl\Tenancy\Events\TenancyEnded;
|
use Stancl\Tenancy\Events\TenancyEnded;
|
||||||
|
|
||||||
class RevertToCentral
|
class RevertToCentralContext
|
||||||
{
|
{
|
||||||
public function handle(TenancyEnded $event)
|
public function handle(TenancyEnded $event)
|
||||||
{
|
{
|
||||||
foreach (tenancy()->getBootstrappers() as $bootstrapper) {
|
foreach ($event->tenancy->getBootstrappers() as $bootstrapper) {
|
||||||
$bootstrapper->end();
|
$bootstrapper->end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,15 +2,15 @@
|
||||||
|
|
||||||
namespace Stancl\Tenancy\Events;
|
namespace Stancl\Tenancy\Events;
|
||||||
|
|
||||||
use Stancl\Tenancy\Database\Models\Tenant;
|
use Stancl\Tenancy\Tenancy;
|
||||||
|
|
||||||
class TenancyEnded
|
class TenancyEnded
|
||||||
{
|
{
|
||||||
/** @var Tenant */
|
/** @var Tenancy */
|
||||||
protected $tenant;
|
public $tenancy;
|
||||||
|
|
||||||
public function __construct(Tenant $tenant)
|
public function __construct(Tenancy $tenancy)
|
||||||
{
|
{
|
||||||
$this->tenant = $tenant;
|
$this->tenancy = $tenancy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,15 @@
|
||||||
|
|
||||||
namespace Stancl\Tenancy\Events;
|
namespace Stancl\Tenancy\Events;
|
||||||
|
|
||||||
use Stancl\Tenancy\Database\Models\Tenant;
|
use Stancl\Tenancy\Tenancy;
|
||||||
|
|
||||||
class TenancyInitialized
|
class TenancyInitialized
|
||||||
{
|
{
|
||||||
/** @var Tenant */
|
/** @var Tenancy */
|
||||||
protected $tenant;
|
public $tenancy;
|
||||||
|
|
||||||
public function __construct(Tenant $tenant)
|
public function __construct(Tenancy $tenancy)
|
||||||
{
|
{
|
||||||
$this->tenant = $tenant;
|
$this->tenancy = $tenancy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,14 +26,14 @@ class Tenancy
|
||||||
|
|
||||||
$this->initialized = true;
|
$this->initialized = true;
|
||||||
|
|
||||||
event(new Events\TenancyInitialized($tenant));
|
event(new Events\TenancyInitialized($this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function end(): void
|
public function end(): void
|
||||||
{
|
{
|
||||||
$this->initialized = false;
|
$this->initialized = false;
|
||||||
|
|
||||||
event(new Events\TenancyEnded($this->tenant));
|
event(new Events\TenancyEnded($this));
|
||||||
|
|
||||||
$this->tenant = null;
|
$this->tenant = null;
|
||||||
}
|
}
|
||||||
|
|
@ -43,7 +43,7 @@ class Tenancy
|
||||||
{
|
{
|
||||||
// If no callback for getting bootstrappers is set, we just return all of them.
|
// If no callback for getting bootstrappers is set, we just return all of them.
|
||||||
$resolve = static::$getBootstrappers ?? function (Tenant $tenant) {
|
$resolve = static::$getBootstrappers ?? function (Tenant $tenant) {
|
||||||
return config('tenancy.bootstrappers');
|
return array_map('app', config('tenancy.bootstrappers'));
|
||||||
};
|
};
|
||||||
|
|
||||||
return $resolve($this->tenant);
|
return $resolve($this->tenant);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use Illuminate\Cache\CacheManager;
|
||||||
use Illuminate\Contracts\Foundation\Application;
|
use Illuminate\Contracts\Foundation\Application;
|
||||||
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\Tenant;
|
use Stancl\Tenancy\Contracts\Tenant;
|
||||||
|
|
||||||
class CacheTenancyBootstrapper implements TenancyBootstrapper
|
class CacheTenancyBootstrapper implements TenancyBootstrapper
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ namespace Stancl\Tenancy\TenancyBootstrappers;
|
||||||
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
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\Tenant;
|
use Stancl\Tenancy\Contracts\TenantWithDatabase;
|
||||||
|
|
||||||
class DatabaseTenancyBootstrapper implements TenancyBootstrapper
|
class DatabaseTenancyBootstrapper implements TenancyBootstrapper
|
||||||
{
|
{
|
||||||
|
|
@ -19,18 +19,18 @@ class DatabaseTenancyBootstrapper implements TenancyBootstrapper
|
||||||
$this->database = $database;
|
$this->database = $database;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function start(Tenant $tenant)
|
public function start(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);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->database->connect($tenant);
|
$this->database->connectToTenant($tenant);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function end()
|
public function end()
|
||||||
{
|
{
|
||||||
$this->database->reconnect();
|
$this->database->revertToCentral();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use Illuminate\Filesystem\FilesystemAdapter;
|
||||||
use Illuminate\Foundation\Application;
|
use Illuminate\Foundation\Application;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
||||||
use Stancl\Tenancy\Tenant;
|
use Stancl\Tenancy\Contracts\Tenant;
|
||||||
|
|
||||||
class FilesystemTenancyBootstrapper implements TenancyBootstrapper
|
class FilesystemTenancyBootstrapper implements TenancyBootstrapper
|
||||||
{
|
{
|
||||||
|
|
@ -36,7 +36,7 @@ class FilesystemTenancyBootstrapper implements TenancyBootstrapper
|
||||||
|
|
||||||
public function start(Tenant $tenant)
|
public function start(Tenant $tenant)
|
||||||
{
|
{
|
||||||
$suffix = $this->app['config']['tenancy.filesystem.suffix_base'] . $tenant->id;
|
$suffix = $this->app['config']['tenancy.filesystem.suffix_base'] . $tenant->getTenantKey();
|
||||||
|
|
||||||
// storage_path()
|
// storage_path()
|
||||||
if ($this->app['config']['tenancy.filesystem.suffix_storage_path'] ?? true) {
|
if ($this->app['config']['tenancy.filesystem.suffix_storage_path'] ?? true) {
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,9 @@ use Illuminate\Config\Repository;
|
||||||
use Illuminate\Queue\QueueManager;
|
use Illuminate\Queue\QueueManager;
|
||||||
use Illuminate\Support\Testing\Fakes\QueueFake;
|
use Illuminate\Support\Testing\Fakes\QueueFake;
|
||||||
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
||||||
use Stancl\Tenancy\Tenant;
|
use Stancl\Tenancy\Contracts\Tenant;
|
||||||
|
|
||||||
|
// todo rewrite this
|
||||||
class QueueTenancyBootstrapper implements TenancyBootstrapper
|
class QueueTenancyBootstrapper implements TenancyBootstrapper
|
||||||
{
|
{
|
||||||
/** @var bool Has tenancy been started. */
|
/** @var bool Has tenancy been started. */
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ namespace Stancl\Tenancy\TenancyBootstrappers;
|
||||||
use Illuminate\Contracts\Config\Repository;
|
use Illuminate\Contracts\Config\Repository;
|
||||||
use Illuminate\Support\Facades\Redis;
|
use Illuminate\Support\Facades\Redis;
|
||||||
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
||||||
use Stancl\Tenancy\Tenant;
|
use Stancl\Tenancy\Contracts\Tenant;
|
||||||
|
|
||||||
class RedisTenancyBootstrapper implements TenancyBootstrapper
|
class RedisTenancyBootstrapper implements TenancyBootstrapper
|
||||||
{
|
{
|
||||||
|
|
@ -25,7 +25,7 @@ class RedisTenancyBootstrapper implements TenancyBootstrapper
|
||||||
public function start(Tenant $tenant)
|
public function start(Tenant $tenant)
|
||||||
{
|
{
|
||||||
foreach ($this->prefixedConnections() as $connection) {
|
foreach ($this->prefixedConnections() as $connection) {
|
||||||
$prefix = $this->config['tenancy.redis.prefix_base'] . $tenant['id'];
|
$prefix = $this->config['tenancy.redis.prefix_base'] . $tenant->getTenantKey();
|
||||||
$client = Redis::connection($connection)->client();
|
$client = Redis::connection($connection)->client();
|
||||||
|
|
||||||
$this->originalPrefixes[$connection] = $client->getOption($client::OPT_PREFIX);
|
$this->originalPrefixes[$connection] = $client->getOption($client::OPT_PREFIX);
|
||||||
|
|
|
||||||
|
|
@ -2,31 +2,85 @@
|
||||||
|
|
||||||
namespace Stancl\Tenancy\Tests\v3;
|
namespace Stancl\Tenancy\Tests\v3;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Event;
|
||||||
|
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
||||||
|
use Stancl\Tenancy\Database\Models\Tenant;
|
||||||
|
use Stancl\Tenancy\Events\Listeners\BootstrapTenancy;
|
||||||
|
use Stancl\Tenancy\Events\Listeners\RevertToCentralContext;
|
||||||
|
use Stancl\Tenancy\Events\TenancyEnded;
|
||||||
|
use Stancl\Tenancy\Events\TenancyInitialized;
|
||||||
use Stancl\Tenancy\Tests\TestCase;
|
use Stancl\Tenancy\Tests\TestCase;
|
||||||
|
|
||||||
class AutomaticModeTest extends TestCase
|
class AutomaticModeTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @test */
|
public function setUp(): void
|
||||||
public function custom_bootstrappers_can_be_registered()
|
|
||||||
{
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||||
|
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @test */
|
/** @test */
|
||||||
public function context_is_switched_when_tenancy_is_initialized()
|
public function context_is_switched_when_tenancy_is_initialized()
|
||||||
{
|
{
|
||||||
|
config(['tenancy.bootstrappers' => [
|
||||||
|
MyBootstrapper::class,
|
||||||
|
]]);
|
||||||
|
|
||||||
|
$tenant = Tenant::create([
|
||||||
|
'id' => 'acme',
|
||||||
|
]);
|
||||||
|
|
||||||
|
tenancy()->initialize($tenant);
|
||||||
|
|
||||||
|
$this->assertSame('acme', app('tenancy_initialized_for_tenant'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @test */
|
/** @test */
|
||||||
public function context_is_reverted_when_tenancy_is_ended()
|
public function context_is_reverted_when_tenancy_is_ended()
|
||||||
{
|
{
|
||||||
|
$this->context_is_switched_when_tenancy_is_initialized();
|
||||||
|
|
||||||
|
tenancy()->end();
|
||||||
|
|
||||||
|
$this->assertSame(true, app('tenancy_ended'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @test */
|
/** @test */
|
||||||
public function context_is_switched_when_tenancy_is_reinitialized()
|
public function context_is_switched_when_tenancy_is_reinitialized()
|
||||||
{
|
{
|
||||||
|
config(['tenancy.bootstrappers' => [
|
||||||
|
MyBootstrapper::class,
|
||||||
|
]]);
|
||||||
|
|
||||||
|
$tenant = Tenant::create([
|
||||||
|
'id' => 'acme',
|
||||||
|
]);
|
||||||
|
|
||||||
|
tenancy()->initialize($tenant);
|
||||||
|
|
||||||
|
$this->assertSame('acme', app('tenancy_initialized_for_tenant'));
|
||||||
|
|
||||||
|
$tenant2 = Tenant::create([
|
||||||
|
'id' => 'foobar',
|
||||||
|
]);
|
||||||
|
|
||||||
|
tenancy()->initialize($tenant2);
|
||||||
|
|
||||||
|
$this->assertSame('foobar', app('tenancy_initialized_for_tenant'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyBootstrapper implements TenancyBootstrapper
|
||||||
|
{
|
||||||
|
public function start(\Stancl\Tenancy\Contracts\Tenant $tenant)
|
||||||
|
{
|
||||||
|
app()->instance('tenancy_initialized_for_tenant', $tenant->getTenantKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function end()
|
||||||
|
{
|
||||||
|
app()->instance('tenancy_ended', true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
// test DB creation, migration, seeding
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Stancl\Tenancy\Tests\v3;
|
namespace Stancl\Tenancy\Tests\v3;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Event;
|
use Illuminate\Support\Facades\Event;
|
||||||
|
|
@ -10,6 +11,7 @@ use Stancl\Tenancy\Database\Models\Tenant;
|
||||||
use Stancl\Tenancy\Events\TenantCreated;
|
use Stancl\Tenancy\Events\TenantCreated;
|
||||||
use Stancl\Tenancy\Tests\TestCase;
|
use Stancl\Tenancy\Tests\TestCase;
|
||||||
use Stancl\Tenancy\UniqueIDGenerators\UUIDGenerator;
|
use Stancl\Tenancy\UniqueIDGenerators\UUIDGenerator;
|
||||||
|
use Stancl\Tenancy\Contracts;
|
||||||
|
|
||||||
class TenantModelTest extends TestCase
|
class TenantModelTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
@ -110,12 +112,43 @@ class TenantModelTest extends TestCase
|
||||||
/** @test */
|
/** @test */
|
||||||
public function custom_tenant_model_can_be_used()
|
public function custom_tenant_model_can_be_used()
|
||||||
{
|
{
|
||||||
|
$tenant = MyTenant::create();
|
||||||
|
|
||||||
|
tenancy()->initialize($tenant);
|
||||||
|
|
||||||
|
$this->assertTrue(tenant() instanceof MyTenant);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @test */
|
/** @test */
|
||||||
public function custom_tenant_model_that_doesnt_extend_vendor_Tenant_model_can_be_used()
|
public function custom_tenant_model_that_doesnt_extend_vendor_Tenant_model_can_be_used()
|
||||||
{
|
{
|
||||||
|
$tenant = AnotherTenant::create([
|
||||||
|
'id' => 'acme',
|
||||||
|
]);
|
||||||
|
|
||||||
|
tenancy()->initialize($tenant);
|
||||||
|
|
||||||
|
$this->assertTrue(tenant() instanceof AnotherTenant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyTenant extends Tenant
|
||||||
|
{
|
||||||
|
protected $table = 'tenants';
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnotherTenant extends Model implements Contracts\Tenant
|
||||||
|
{
|
||||||
|
protected $guarded = [];
|
||||||
|
protected $table = 'tenants';
|
||||||
|
|
||||||
|
public function getTenantKeyName(): string
|
||||||
|
{
|
||||||
|
return 'id';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTenantKey(): string
|
||||||
|
{
|
||||||
|
return $this->getAttribute('id');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue