1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2025-12-12 17:44:04 +00:00

Automatic mode

This commit is contained in:
Samuel Štancl 2020-05-10 23:47:11 +02:00
parent 2492345280
commit 73fc525126
16 changed files with 154 additions and 40 deletions

View file

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

View file

@ -0,0 +1,10 @@
<?php
namespace Stancl\Tenancy\Contracts;
use Stancl\Tenancy\DatabaseConfig;
interface TenantWithDatabase extends Tenant
{
public function database(): DatabaseConfig;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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) {

View file

@ -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. */

View file

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

View file

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

View file

@ -0,0 +1 @@
// test DB creation, migration, seeding

View file

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