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;
|
||||
|
||||
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
|
||||
{
|
||||
|
|
|
|||
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\Foundation\Application;
|
||||
use Stancl\Tenancy\Contracts\TenantCannotBeCreatedException;
|
||||
use Stancl\Tenancy\Contracts\TenantWithDatabase;
|
||||
use Stancl\Tenancy\Exceptions\DatabaseManagerNotRegisteredException;
|
||||
use Stancl\Tenancy\Exceptions\TenantDatabaseAlreadyExistsException;
|
||||
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.
|
||||
*/
|
||||
// todo rewrite everything
|
||||
class DatabaseManager
|
||||
{
|
||||
/** @var string */
|
||||
|
|
@ -41,7 +43,7 @@ class DatabaseManager
|
|||
/**
|
||||
* Set the TenantManager instance, used to dispatch tenancy events.
|
||||
*/
|
||||
public function withTenantManager(TenantManager $tenantManager): self
|
||||
public function withTenantManager(Tenancy $tenantManager): self
|
||||
{
|
||||
$this->tenancy = $tenantManager;
|
||||
|
||||
|
|
@ -51,7 +53,7 @@ class DatabaseManager
|
|||
/**
|
||||
* Connect to a tenant's database.
|
||||
*/
|
||||
public function connect(Tenant $tenant)
|
||||
public function connect(TenantWithDatabase $tenant)
|
||||
{
|
||||
$this->createTenantConnection($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,11 +4,11 @@ namespace Stancl\Tenancy\Events\Listeners;
|
|||
|
||||
use Stancl\Tenancy\Events\TenancyEnded;
|
||||
|
||||
class RevertToCentral
|
||||
class RevertToCentralContext
|
||||
{
|
||||
public function handle(TenancyEnded $event)
|
||||
{
|
||||
foreach (tenancy()->getBootstrappers() as $bootstrapper) {
|
||||
foreach ($event->tenancy->getBootstrappers() as $bootstrapper) {
|
||||
$bootstrapper->end();
|
||||
}
|
||||
}
|
||||
|
|
@ -2,15 +2,15 @@
|
|||
|
||||
namespace Stancl\Tenancy\Events;
|
||||
|
||||
use Stancl\Tenancy\Database\Models\Tenant;
|
||||
use Stancl\Tenancy\Tenancy;
|
||||
|
||||
class TenancyEnded
|
||||
{
|
||||
/** @var Tenant */
|
||||
protected $tenant;
|
||||
/** @var Tenancy */
|
||||
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;
|
||||
|
||||
use Stancl\Tenancy\Database\Models\Tenant;
|
||||
use Stancl\Tenancy\Tenancy;
|
||||
|
||||
class TenancyInitialized
|
||||
{
|
||||
/** @var Tenant */
|
||||
protected $tenant;
|
||||
/** @var Tenancy */
|
||||
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;
|
||||
|
||||
event(new Events\TenancyInitialized($tenant));
|
||||
event(new Events\TenancyInitialized($this));
|
||||
}
|
||||
|
||||
public function end(): void
|
||||
{
|
||||
$this->initialized = false;
|
||||
|
||||
event(new Events\TenancyEnded($this->tenant));
|
||||
event(new Events\TenancyEnded($this));
|
||||
|
||||
$this->tenant = null;
|
||||
}
|
||||
|
|
@ -43,7 +43,7 @@ class Tenancy
|
|||
{
|
||||
// If no callback for getting bootstrappers is set, we just return all of them.
|
||||
$resolve = static::$getBootstrappers ?? function (Tenant $tenant) {
|
||||
return config('tenancy.bootstrappers');
|
||||
return array_map('app', config('tenancy.bootstrappers'));
|
||||
};
|
||||
|
||||
return $resolve($this->tenant);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use Illuminate\Cache\CacheManager;
|
|||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Stancl\Tenancy\CacheManager as TenantCacheManager;
|
||||
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
||||
use Stancl\Tenancy\Tenant;
|
||||
use Stancl\Tenancy\Contracts\Tenant;
|
||||
|
||||
class CacheTenancyBootstrapper implements TenancyBootstrapper
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ namespace Stancl\Tenancy\TenancyBootstrappers;
|
|||
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
||||
use Stancl\Tenancy\DatabaseManager;
|
||||
use Stancl\Tenancy\Exceptions\TenantDatabaseDoesNotExistException;
|
||||
use Stancl\Tenancy\Tenant;
|
||||
use Stancl\Tenancy\Contracts\TenantWithDatabase;
|
||||
|
||||
class DatabaseTenancyBootstrapper implements TenancyBootstrapper
|
||||
{
|
||||
|
|
@ -19,18 +19,18 @@ class DatabaseTenancyBootstrapper implements TenancyBootstrapper
|
|||
$this->database = $database;
|
||||
}
|
||||
|
||||
public function start(Tenant $tenant)
|
||||
public function start(TenantWithDatabase $tenant)
|
||||
{
|
||||
$database = $tenant->database()->getName();
|
||||
if (! $tenant->database()->manager()->databaseExists($database)) {
|
||||
throw new TenantDatabaseDoesNotExistException($database);
|
||||
}
|
||||
|
||||
$this->database->connect($tenant);
|
||||
$this->database->connectToTenant($tenant);
|
||||
}
|
||||
|
||||
public function end()
|
||||
{
|
||||
$this->database->reconnect();
|
||||
$this->database->revertToCentral();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use Illuminate\Filesystem\FilesystemAdapter;
|
|||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
||||
use Stancl\Tenancy\Tenant;
|
||||
use Stancl\Tenancy\Contracts\Tenant;
|
||||
|
||||
class FilesystemTenancyBootstrapper implements TenancyBootstrapper
|
||||
{
|
||||
|
|
@ -36,7 +36,7 @@ class FilesystemTenancyBootstrapper implements TenancyBootstrapper
|
|||
|
||||
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()
|
||||
if ($this->app['config']['tenancy.filesystem.suffix_storage_path'] ?? true) {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@ use Illuminate\Config\Repository;
|
|||
use Illuminate\Queue\QueueManager;
|
||||
use Illuminate\Support\Testing\Fakes\QueueFake;
|
||||
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
||||
use Stancl\Tenancy\Tenant;
|
||||
use Stancl\Tenancy\Contracts\Tenant;
|
||||
|
||||
// todo rewrite this
|
||||
class QueueTenancyBootstrapper implements TenancyBootstrapper
|
||||
{
|
||||
/** @var bool Has tenancy been started. */
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ namespace Stancl\Tenancy\TenancyBootstrappers;
|
|||
use Illuminate\Contracts\Config\Repository;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
||||
use Stancl\Tenancy\Tenant;
|
||||
use Stancl\Tenancy\Contracts\Tenant;
|
||||
|
||||
class RedisTenancyBootstrapper implements TenancyBootstrapper
|
||||
{
|
||||
|
|
@ -25,7 +25,7 @@ class RedisTenancyBootstrapper implements TenancyBootstrapper
|
|||
public function start(Tenant $tenant)
|
||||
{
|
||||
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();
|
||||
|
||||
$this->originalPrefixes[$connection] = $client->getOption($client::OPT_PREFIX);
|
||||
|
|
|
|||
|
|
@ -2,31 +2,85 @@
|
|||
|
||||
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;
|
||||
|
||||
class AutomaticModeTest extends TestCase
|
||||
{
|
||||
/** @test */
|
||||
public function custom_bootstrappers_can_be_registered()
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
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 */
|
||||
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 */
|
||||
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;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
|
|
@ -10,6 +11,7 @@ use Stancl\Tenancy\Database\Models\Tenant;
|
|||
use Stancl\Tenancy\Events\TenantCreated;
|
||||
use Stancl\Tenancy\Tests\TestCase;
|
||||
use Stancl\Tenancy\UniqueIDGenerators\UUIDGenerator;
|
||||
use Stancl\Tenancy\Contracts;
|
||||
|
||||
class TenantModelTest extends TestCase
|
||||
{
|
||||
|
|
@ -110,12 +112,43 @@ class TenantModelTest extends TestCase
|
|||
/** @test */
|
||||
public function custom_tenant_model_can_be_used()
|
||||
{
|
||||
$tenant = MyTenant::create();
|
||||
|
||||
tenancy()->initialize($tenant);
|
||||
|
||||
$this->assertTrue(tenant() instanceof MyTenant);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
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