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

Refactor more old code and get tests to pass

This commit is contained in:
Samuel Štancl 2020-05-13 04:51:37 +02:00
parent c5377a16f7
commit c32f229dd5
72 changed files with 425 additions and 531 deletions

View file

@ -2,20 +2,36 @@
namespace App\Providers; namespace App\Providers;
use Closure; use Stancl\Tenancy\Contracts\Tenant;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Stancl\Tenancy\Contracts\UniqueIdentifierGenerator;
use Stancl\Tenancy\Events\DatabaseCreated; use Stancl\Tenancy\Events\DatabaseCreated;
use Stancl\Tenancy\Events\DatabaseDeleted; use Stancl\Tenancy\Events\DatabaseDeleted;
use Stancl\Tenancy\Events\DatabaseMigrated; use Stancl\Tenancy\Events\DatabaseMigrated;
use Stancl\Tenancy\Events\DatabaseRolledBack;
use Stancl\Tenancy\Events\DatabaseSeeded; use Stancl\Tenancy\Events\DatabaseSeeded;
use Stancl\Tenancy\Events\Listeners\JobPipeline; use Stancl\Tenancy\Events\DomainCreated;
use Stancl\Tenancy\Events\DomainDeleted;
use Stancl\Tenancy\Events\DomainSaved;
use Stancl\Tenancy\Events\DomainUpdated;
use Stancl\Tenancy\Events\RevertedToCentralContext;
use Stancl\Tenancy\Events\TenancyBootstrapped;
use Stancl\Tenancy\Events\TenancyEnded;
use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Listeners\JobPipeline;
use Stancl\Tenancy\Events\TenantCreated; use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Events\TenantDeleted; use Stancl\Tenancy\Events\TenantDeleted;
use Stancl\Tenancy\Events\TenantSaved;
use Stancl\Tenancy\Events\TenantUpdated;
use Stancl\Tenancy\Jobs\CreateDatabase; use Stancl\Tenancy\Jobs\CreateDatabase;
use Stancl\Tenancy\Jobs\DeleteDatabase; use Stancl\Tenancy\Jobs\DeleteDatabase;
use Stancl\Tenancy\Jobs\MigrateDatabase; use Stancl\Tenancy\Jobs\MigrateDatabase;
use Stancl\Tenancy\Jobs\SeedDatabase; use Stancl\Tenancy\Jobs\SeedDatabase;
use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Listeners\RevertToCentralContext;
use Stancl\Tenancy\Tenancy;
class TenancyServiceProvider extends ServiceProvider class TenancyServiceProvider extends ServiceProvider
{ {
@ -25,35 +41,82 @@ class TenancyServiceProvider extends ServiceProvider
TenantCreated::class => [ TenantCreated::class => [
JobPipeline::make([ JobPipeline::make([
CreateDatabase::class, CreateDatabase::class,
MigrateDatabase::class, // triggers DatabaseMigrated event MigrateDatabase::class,
SeedDatabase::class, SeedDatabase::class,
// Your own jobs to prepare the tenant.
// Provision API keys, create S3 buckets, anything you want!
])->send(function (TenantCreated $event) { ])->send(function (TenantCreated $event) {
return $event->tenant; return $event->tenant;
})->queue(true), })->queue(false), // `false` by default, but you probably want to make this `true` for production.
], ],
DatabaseCreated::class => [], TenantSaved::class => [],
DatabaseMigrated::class => [], TenantUpdated::class => [],
DatabaseSeeded::class => [],
TenantDeleted::class => [ TenantDeleted::class => [
JobPipeline::make([ JobPipeline::make([
DeleteDatabase::class, DeleteDatabase::class,
])->send(function (TenantDeleted $event) { ])->send(function (TenantDeleted $event) {
return $event->tenant; return $event->tenant;
})->queue(true), })->queue(false), // `false` by default, but you probably want to make this `true` for production.
// DeleteStorage::class,
], ],
DomainCreated::class => [],
DomainSaved::class => [],
DomainUpdated::class => [],
DomainDeleted::class => [],
DatabaseCreated::class => [],
DatabaseMigrated::class => [],
DatabaseSeeded::class => [],
DatabaseRolledBack::class => [],
DatabaseDeleted::class => [], DatabaseDeleted::class => [],
TenancyInitialized::class => [
BootstrapTenancy::class,
],
TenancyEnded::class => [
RevertToCentralContext::class,
],
TenancyBootstrapped::class => [],
RevertedToCentralContext::class => [],
]; ];
} }
public function register() public function register()
{ {
// // Make sure Tenancy is stateful.
$this->app->singleton(Tenancy::class);
// Make sure features are bootstrapped as soon as Tenancy is instantiated.
$this->app->extend(Tenancy::class, function (Tenancy $tenancy) {
foreach ($this->app['config']['tenancy.features'] as $feature) {
$this->app[$feature]->bootstrap($tenancy);
}
return $tenancy;
});
// Make it possible to inject the current tenant by typehinting the Tenant contract.
$this->app->bind(Tenant::class, function ($app) {
return $app[Tenancy::class]->tenant;
});
// Make sure bootstrappers are stateful (singletons).
foreach ($this->app['config']['tenancy.bootstrappers'] as $bootstrapper) {
$this->app->singleton($bootstrapper);
}
// Bind the class in the tenancy.id_generator config to the UniqueIdentifierGenerator abstract.
$this->app->bind(UniqueIdentifierGenerator::class, $this->app['config']['tenancy.id_generator']);
} }
public function boot() public function boot()
{ {
$this->bootEvents(); $this->bootEvents();
$this->mapRoutes();
// //
} }
@ -70,4 +133,15 @@ class TenancyServiceProvider extends ServiceProvider
} }
} }
} }
protected function mapRoutes()
{
$this->app->booted(function () {
if (file_exists(base_path('routes/tenant.php'))) {
Route::middleware(['web'])
->namespace($this->app['config']['tenancy.tenant_route_namespace'] ?? 'App\Http\Controllers')
->group(base_path('routes/tenant.php'));
}
});
}
} }

View file

@ -24,23 +24,6 @@ return [
'localhost', 'localhost',
], ],
'storage' => [
'data_column' => 'data',
'custom_columns' => [
// 'plan',
],
/**
* Here you can enable the Cached Tenant Lookup.
*
* You can specify what cache store should be used to cache the tenant resolution.
* Set to string with a specific cache store name, or to null to disable cache.
*/
'cache_store' => null, // env('CACHE_DRIVER')
'cache_ttl' => 3600, // seconds
],
/** /**
* Controller namespace used by routes in routes/tenant.php. * Controller namespace used by routes in routes/tenant.php.
*/ */
@ -76,7 +59,6 @@ return [
*/ */
'prefix' => 'tenant', 'prefix' => 'tenant',
'suffix' => '', 'suffix' => '',
// todo get rid of this stuff, just set the closure instead
], ],
/** /**
@ -194,18 +176,11 @@ return [
* See the documentation page for each class to * See the documentation page for each class to
* understand which ones you want to enable. * understand which ones you want to enable.
*/ */
'features' => [ // todo test features 'features' => [
// Stancl\Tenancy\Features\Timestamps::class, // https://tenancy.samuelstancl.me/docs/v2/features/timestamps/
// Stancl\Tenancy\Features\TenantConfig::class, // https://tenancy.samuelstancl.me/docs/v2/features/tenant-config/ // Stancl\Tenancy\Features\TenantConfig::class, // https://tenancy.samuelstancl.me/docs/v2/features/tenant-config/
// Stancl\Tenancy\Features\TelescopeTags::class, // https://tenancy.samuelstancl.me/docs/v2/telescope/
// Stancl\Tenancy\Features\CrossDomainRedirect::class, // https://tenancy.samuelstancl.me/docs/v2/features/tenant-redirect/ // Stancl\Tenancy\Features\CrossDomainRedirect::class, // https://tenancy.samuelstancl.me/docs/v2/features/tenant-redirect/
], ],
/**
* The URL to which users will be redirected when they try to acceess a central route on a tenant domain.
*/
'home_url' => '/app', // todo move this to static
'migration_parameters' => [ 'migration_parameters' => [
'--force' => true, // Set this to true to be able to run migrations in production '--force' => true, // Set this to true to be able to run migrations in production
'--path' => [database_path('migrations/tenant')], '--path' => [database_path('migrations/tenant')],

View file

@ -7,10 +7,17 @@
| |
| Here you can register the tenant routes for your application. | Here you can register the tenant routes for your application.
| These routes are loaded by the TenantRouteServiceProvider | These routes are loaded by the TenantRouteServiceProvider
| with the tenancy and web middleware groups. Good luck! | with the namespace configured in your tenancy config.
|
| Feel free to customize them however you want. Good luck!
| |
*/ */
Route::get('/app', function () { Route::group([
'middleware' => InitializeTenancyByDomain::class,
'prefix' => '/app',
], function () {
Route::get('/', function () {
return 'This is your multi-tenant application. The id of the current tenant is ' . tenant('id'); return 'This is your multi-tenant application. The id of the current tenant is ' . tenant('id');
});
}); });

View file

@ -9,6 +9,7 @@ use Illuminate\Database\Console\Migrations\RollbackCommand;
use Illuminate\Database\Migrations\Migrator; use Illuminate\Database\Migrations\Migrator;
use Stancl\Tenancy\Contracts\TenantWithDatabase; use Stancl\Tenancy\Contracts\TenantWithDatabase;
use Stancl\Tenancy\DatabaseManager; use Stancl\Tenancy\DatabaseManager;
use Stancl\Tenancy\Events\DatabaseRolledBack;
use Stancl\Tenancy\Traits\DealsWithMigrations; use Stancl\Tenancy\Traits\DealsWithMigrations;
use Stancl\Tenancy\Traits\HasATenantsOption; use Stancl\Tenancy\Traits\HasATenantsOption;
@ -62,7 +63,7 @@ class Rollback extends RollbackCommand
// Rollback // Rollback
parent::handle(); parent::handle();
// todo DatabaseRolledBack event event(new DatabaseRolledBack($tenant));
}); });
} }
} }

View file

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Contracts; namespace Stancl\Tenancy\Contracts;
use Stancl\Tenancy\Facades\Tenancy; use Stancl\Tenancy\Tenancy;
/** Additional features, like Telescope tags and tenant redirects. */ /** Additional features, like Telescope tags and tenant redirects. */
interface Feature interface Feature

View file

@ -1,21 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Contracts\Future;
use Stancl\Tenancy\Tenant;
/**
* This interface will be part of the StorageDriver interface in 3.x.
*/
interface CanDeleteKeys
{
/**
* Delete keys from the storage.
*
* @param string[] $keys
* @return void
*/
public function deleteMany(array $keys, Tenant $tenant = null): void;
}

View file

@ -1,24 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Contracts\Future;
use Stancl\Tenancy\Exceptions\TenantDoesNotExistException;
use Stancl\Tenancy\Tenant;
/**
* This interface *might* be part of the StorageDriver interface in 3.x.
*/
interface CanFindByAnyKey
{
/**
* Find a tenant using an arbitrary key.
*
* @param string $key
* @param mixed $value
* @return Tenant
* @throws TenantDoesNotExistException
*/
public function findBy(string $key, $value): Tenant;
}

View file

@ -1,13 +0,0 @@
<?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

@ -9,8 +9,7 @@ namespace Stancl\Tenancy\Contracts;
*/ */
interface TenancyBootstrapper interface TenancyBootstrapper
{ {
// todo rename methods public function bootstrap(Tenant $tenant);
public function start(Tenant $tenant);
public function end(); public function revert();
} }

View file

@ -32,4 +32,12 @@ interface TenantDatabaseManager
* @return array * @return array
*/ */
public function makeConnectionConfig(array $baseConfig, string $databaseName): array; public function makeConnectionConfig(array $baseConfig, string $databaseName): array;
/**
* Set the DB connection that should be used by the tenant database manager.
*
* @param string $connection
* @return void
*/
public function setConnection(string $connection): void;
} }

View file

@ -1,6 +1,6 @@
<?php <?php
namespace Stancl\Tenancy\Database\Models\Concerns; namespace Stancl\Tenancy\Database\Concerns;
trait CentralConnection trait CentralConnection
{ {

View file

@ -1,6 +1,6 @@
<?php <?php
namespace Stancl\Tenancy\Database\Models\Concerns; namespace Stancl\Tenancy\Database\Concerns;
use Stancl\Tenancy\Contracts\UniqueIdentifierGenerator; use Stancl\Tenancy\Contracts\UniqueIdentifierGenerator;
@ -14,4 +14,9 @@ trait GeneratesIds
} }
}); });
} }
public function getIncrementing()
{
return ! app()->bound(UniqueIdentifierGenerator::class);
}
} }

View file

@ -1,9 +1,7 @@
<?php <?php
// todo move namespace one dir above namespace Stancl\Tenancy\Database\Concerns;
namespace Stancl\Tenancy\Database\Models\Concerns;
// todo rename
trait HasADataColumn trait HasADataColumn
{ {
public static $priorityListeners = []; public static $priorityListeners = [];
@ -53,11 +51,17 @@ trait HasADataColumn
$model->dataEncodingStatus = 'decoded'; $model->dataEncodingStatus = 'decoded';
}; };
static::registerPriorityListener('retrieved', $decode); static::registerPriorityListener('retrieved', function ($model) use ($decode) {
// We always decode after model retrieval.
$model->dataEncodingStatus = 'encoded';
$decode($model);
});
static::registerPriorityListener('saving', $encode); static::registerPriorityListener('saving', $encode);
static::registerPriorityListener('creating', $encode); static::registerPriorityListener('creating', $encode);
static::registerPriorityListener('updating', $encode); static::registerPriorityListener('updating', $encode);
static::registerPriorityListener('saved', $decode); static::registerPriorityListener('saved', $decode);
static::registerPriorityListener('created', $decode); static::registerPriorityListener('created', $decode);
static::registerPriorityListener('updated', $decode); static::registerPriorityListener('updated', $decode);

View file

@ -0,0 +1,16 @@
<?php
namespace Stancl\Tenancy\Database\Concerns;
use Stancl\Tenancy\Contracts\Domain;
/**
* @property-read Domain[] $domains
*/
trait HasDomains
{
public function domains()
{
return $this->hasMany(config('tenancy.domain_model'), 'tenant_id');
}
}

View file

@ -1,6 +1,6 @@
<?php <?php
namespace Stancl\Tenancy\Database\Models\Concerns; namespace Stancl\Tenancy\Database\Concerns;
/** /**
* @property-read string $primary_domain_hostname * @property-read string $primary_domain_hostname

View file

@ -1,6 +1,6 @@
<?php <?php
namespace Stancl\Tenancy\Database\Models\Concerns; namespace Stancl\Tenancy\Database\Concerns;
use Stancl\Tenancy\Contracts\Syncable; use Stancl\Tenancy\Contracts\Syncable;
use Stancl\Tenancy\Contracts\UniqueIdentifierGenerator; use Stancl\Tenancy\Contracts\UniqueIdentifierGenerator;

View file

@ -1,6 +1,6 @@
<?php <?php
namespace Stancl\Tenancy\Database\Models\Concerns; namespace Stancl\Tenancy\Database\Concerns;
trait TenantConnection trait TenantConnection
{ {

View file

@ -1,11 +0,0 @@
<?php
namespace Stancl\Tenancy\Database\Models\Concerns;
trait HasDomains
{
public function domains()
{
return $this->hasMany(config('tenancy.domain_model'));
}
}

View file

@ -2,18 +2,30 @@
namespace Stancl\Tenancy\Database\Models; namespace Stancl\Tenancy\Database\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Stancl\Tenancy\DatabaseConfig; use Stancl\Tenancy\DatabaseConfig;
use Stancl\Tenancy\Events; use Stancl\Tenancy\Events;
use Stancl\Tenancy\Contracts; use Stancl\Tenancy\Contracts;
use Stancl\Tenancy\Database\Concerns;
// todo @property /**
class Tenant extends Model implements Contracts\TenantWithDatabase * @property string|int $id
* @property Carbon $created_at
* @property Carbon $updated_at
* @property array $data
*/
class Tenant extends Model implements Contracts\TenantWithDatabase // todo base model that isn't TenantWithDatabase & domains
{ {
use Concerns\CentralConnection, Concerns\HasADataColumn, Concerns\GeneratesIds, Concerns\HasADataColumn { use Concerns\CentralConnection,
Concerns\HasADataColumn,
Concerns\GeneratesIds,
Concerns\HasADataColumn,
Concerns\HasDomains {
Concerns\HasADataColumn::getCasts as dataColumnCasts; Concerns\HasADataColumn::getCasts as dataColumnCasts;
} }
protected $table = 'tenants';
public $primaryKey = 'id'; public $primaryKey = 'id';
public $guarded = []; public $guarded = [];
@ -34,11 +46,6 @@ class Tenant extends Model implements Contracts\TenantWithDatabase
]); ]);
} }
public function getIncrementing()
{
return config('tenancy.id_generator') === null;
}
public static function internalPrefix(): string public static function internalPrefix(): string
{ {
return config('tenancy.internal_prefix'); return config('tenancy.internal_prefix');

View file

@ -6,9 +6,7 @@ namespace Stancl\Tenancy;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Stancl\Tenancy\Contracts\Future\CanSetConnection;
use Stancl\Tenancy\Contracts\ManagesDatabaseUsers; use Stancl\Tenancy\Contracts\ManagesDatabaseUsers;
use Stancl\Tenancy\Contracts\ModifiesDatabaseNameForConnection;
use Stancl\Tenancy\Contracts\TenantDatabaseManager; use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Exceptions\DatabaseManagerNotRegisteredException; use Stancl\Tenancy\Exceptions\DatabaseManagerNotRegisteredException;
@ -79,6 +77,11 @@ class DatabaseConfig
return $this->tenant->getInternal('db_password') ?? null; return $this->tenant->getInternal('db_password') ?? null;
} }
/**
* Generate DB name, username & password and write them to the tenant model.
*
* @return void
*/
public function makeCredentials(): void public function makeCredentials(): void
{ {
$this->tenant->setInternal('db_name', $this->getName() ?? (static::$databaseNameGenerator)($this->tenant)); $this->tenant->setInternal('db_name', $this->getName() ?? (static::$databaseNameGenerator)($this->tenant));
@ -151,9 +154,7 @@ class DatabaseConfig
/** @var TenantDatabaseManager $databaseManager */ /** @var TenantDatabaseManager $databaseManager */
$databaseManager = app($databaseManagers[$driver]); $databaseManager = app($databaseManagers[$driver]);
if ($databaseManager instanceof CanSetConnection) {
$databaseManager->setConnection($this->getTemplateConnectionName()); $databaseManager->setConnection($this->getTemplateConnectionName());
}
return $databaseManager; return $databaseManager;
} }

View file

@ -0,0 +1,6 @@
<?php
namespace Stancl\Tenancy\Events;
class DatabaseRolledBack extends Contracts\TenantEvent
{}

View file

@ -0,0 +1,16 @@
<?php
namespace Stancl\Tenancy\Events;
use Stancl\Tenancy\Tenancy;
class RevertedToCentralContext
{
/** @var Tenancy */
public $tenancy;
public function __construct(Tenancy $tenancy)
{
$this->tenancy = $tenancy;
}
}

View file

@ -0,0 +1,16 @@
<?php
namespace Stancl\Tenancy\Events;
use Stancl\Tenancy\Tenancy;
class TenancyBootstrapped
{
/** @var Tenancy */
public $tenancy;
public function __construct(Tenancy $tenancy)
{
$this->tenancy = $tenancy;
}
}

View file

@ -5,12 +5,11 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Facades; namespace Stancl\Tenancy\Facades;
use Illuminate\Support\Facades\Facade; use Illuminate\Support\Facades\Facade;
use Stancl\Tenancy\TenantManager;
class Tenancy extends Facade class Tenancy extends Facade
{ {
protected static function getFacadeAccessor() protected static function getFacadeAccessor()
{ {
return TenantManager::class; return \Stancl\Tenancy\Tenancy::class;
} }
} }

View file

@ -1,63 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Features;
use Laravel\Telescope\IncomingEntry;
use Laravel\Telescope\Telescope;
use Stancl\Tenancy\Contracts\Feature;
use Stancl\Tenancy\Middleware\PreventAccessFromTenantDomains;
use Stancl\Tenancy\Tenancy;
// todo rewrite this
class TelescopeTags implements Feature
{
/** @var callable User-specific callback that returns tags. */
protected $callback;
public function __construct()
{
$this->callback = function ($entry) {
return [];
};
}
public function bootstrap(Tenancy $tenancy): void
{
if (! class_exists(Telescope::class)) {
return;
}
Telescope::tag(function (IncomingEntry $entry) {
$tags = $this->getTags($entry);
if (! request()->route()) {
return $tags;
}
// todo lines below
$tenantRoute = PreventAccessFromTenantDomains::routeHasMiddleware(request()->route(), 'tenancy')
|| PreventAccessFromTenantDomains::routeHasMiddleware(request()->route(), 'universal');
// Don't do anything if we're visiting a universal route on a central domain
if ($tenantRoute && tenancy()->initialized) {
$tags = array_merge($tags, [
'tenant:' . tenant('id'),
]);
}
return $tags;
});
}
public function getTags(IncomingEntry $entry): array
{
return ($this->callback)($entry);
}
public function setCallback(callable $callback)
{
$this->callback = $callback;
}
}

View file

@ -5,12 +5,13 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Features; namespace Stancl\Tenancy\Features;
use Illuminate\Contracts\Config\Repository; use Illuminate\Contracts\Config\Repository;
use Illuminate\Support\Facades\Event;
use Stancl\Tenancy\Contracts\Feature; use Stancl\Tenancy\Contracts\Feature;
use Stancl\Tenancy\Events\RevertedToCentralContext;
use Stancl\Tenancy\Events\TenancyBootstrapped;
use Stancl\Tenancy\Tenancy; use Stancl\Tenancy\Tenancy;
use Stancl\Tenancy\Tenant; use Stancl\Tenancy\Contracts\Tenant;
use Stancl\Tenancy\TenantManager;
// todo rewrite this
class TenantConfig implements Feature class TenantConfig implements Feature
{ {
/** @var Repository */ /** @var Repository */
@ -27,26 +28,26 @@ class TenantConfig implements Feature
{ {
$this->config = $config; $this->config = $config;
foreach ($this->getStorageToConfigMap() as $configKey) { foreach (static::$storageToConfigMap as $configKey) {
$this->originalConfig[$configKey] = $this->config[$configKey]; $this->originalConfig[$configKey] = $this->config[$configKey];
} }
} }
public function bootstrap(Tenancy $tenancy): void public function bootstrap(Tenancy $tenancy): void
{ {
$tenantManager->eventListener('bootstrapped', function (TenantManager $manager) { Event::listen(TenancyBootstrapped::class, function (TenancyBootstrapped $event) {
$this->setTenantConfig($manager->getTenant()); $this->setTenantConfig($event->tenancy->tenant);
}); });
$tenantManager->eventListener('ended', function () { Event::listen(RevertedToCentralContext::class, function () {
$this->unsetTenantConfig(); $this->unsetTenantConfig();
}); });
} }
public function setTenantConfig(Tenant $tenant): void public function setTenantConfig(Tenant $tenant): void
{ {
foreach ($this->getStorageToConfigMap() as $storageKey => $configKey) { foreach (static::$storageToConfigMap as $storageKey => $configKey) {
$override = $tenant->data[$storageKey] ?? null; $override = $tenant->$storageKey ?? null;
if (! is_null($override)) { if (! is_null($override)) {
$this->config[$configKey] = $override; $this->config[$configKey] = $override;
} }
@ -55,13 +56,8 @@ class TenantConfig implements Feature
public function unsetTenantConfig(): void public function unsetTenantConfig(): void
{ {
foreach ($this->getStorageToConfigMap() as $configKey) { foreach (static::$storageToConfigMap as $configKey) {
$this->config[$configKey] = $this->originalConfig[$configKey]; $this->config[$configKey] = $this->originalConfig[$configKey];
} }
} }
public function getStorageToConfigMap(): array
{
return static::$storageToConfigMap;
}
} }

View file

@ -1,46 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Features;
use Illuminate\Config\Repository;
use Illuminate\Support\Facades\Date;
use Stancl\Tenancy\Contracts\Feature;
use Stancl\Tenancy\Tenant;
use Stancl\Tenancy\TenantManager;
// todo rewrite this
class Timestamps implements Feature
{
/** @var Repository */
protected $config;
public static $format = 'c'; // ISO 8601
public function __construct(Repository $config)
{
$this->config = $config;
}
public function bootstrap(TenantManager $tenantManager): void
{
$tenantManager->hook('tenant.creating', function ($tm, Tenant $tenant) {
$tenant->with('created_at', $this->now());
$tenant->with('updated_at', $this->now());
});
$tenantManager->hook('tenant.updating', function ($tm, Tenant $tenant) {
$tenant->with('updated_at', $this->now());
});
$tenantManager->hook('tenant.softDeleting', function ($tm, Tenant $tenant) {
$tenant->with('deleted_at', $this->now());
});
}
public function now(): string
{
return Date::now()->format(static::$format);
}
}

View file

@ -12,6 +12,7 @@ use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Stancl\Tenancy\Contracts\TenantWithDatabase; use Stancl\Tenancy\Contracts\TenantWithDatabase;
use Stancl\Tenancy\Contracts\Tenant; use Stancl\Tenancy\Contracts\Tenant;
use Stancl\Tenancy\Events\DatabaseCreated;
class CreateDatabase implements ShouldQueue class CreateDatabase implements ShouldQueue
{ {
@ -30,6 +31,8 @@ class CreateDatabase implements ShouldQueue
if ($this->tenant->getInternal('create_database') !== false) { if ($this->tenant->getInternal('create_database') !== false) {
$this->tenant->database()->makeCredentials(); $this->tenant->database()->makeCredentials();
$this->tenant->database()->manager()->createDatabase($this->tenant); $this->tenant->database()->manager()->createDatabase($this->tenant);
event(new DatabaseCreated($this->tenant));
} }
} }
} }

View file

@ -11,6 +11,7 @@ use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Stancl\Tenancy\Contracts\TenantDatabaseManager; use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\DatabaseDeleted;
class DeleteDatabase implements ShouldQueue class DeleteDatabase implements ShouldQueue
{ {
@ -27,5 +28,7 @@ class DeleteDatabase implements ShouldQueue
public function handle() public function handle()
{ {
$this->tenant->database()->manager()->deleteDatabase($this->tenant); $this->tenant->database()->manager()->deleteDatabase($this->tenant);
event(new DatabaseDeleted($this->tenant));
} }
} }

View file

@ -31,12 +31,8 @@ class MigrateDatabase implements ShouldQueue
*/ */
public function handle() public function handle()
{ {
$migrationParameters = [
// todo ...
];
Artisan::call('tenants:migrate', [ Artisan::call('tenants:migrate', [
'--tenants' => [$this->tenant->id], '--tenants' => [$this->tenant->id],
] + $migrationParameters); ]);
} }
} }

View file

@ -1,7 +1,8 @@
<?php <?php
namespace Stancl\Tenancy\Events\Listeners; namespace Stancl\Tenancy\Listeners;
use Stancl\Tenancy\Events\TenancyBootstrapped;
use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Events\TenancyInitialized;
class BootstrapTenancy class BootstrapTenancy
@ -9,7 +10,9 @@ class BootstrapTenancy
public function handle(TenancyInitialized $event) public function handle(TenancyInitialized $event)
{ {
foreach ($event->tenancy->getBootstrappers() as $bootstrapper) { foreach ($event->tenancy->getBootstrappers() as $bootstrapper) {
$bootstrapper->start($event->tenancy->tenant); $bootstrapper->bootstrap($event->tenancy->tenant);
} }
event(new TenancyBootstrapped($event->tenancy));
} }
} }

View file

@ -1,6 +1,6 @@
<?php <?php
namespace Stancl\Tenancy\Events\Listeners; namespace Stancl\Tenancy\Listeners;
use Closure; use Closure;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
@ -8,7 +8,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
class JobPipeline implements ShouldQueue class JobPipeline implements ShouldQueue
{ {
/** @var bool */ /** @var bool */
public static $shouldBeQueuedByDefault = false; public static $queueByDefault = false;
/** @var callable[]|string[] */ /** @var callable[]|string[] */
public $jobs; public $jobs;
@ -22,16 +22,16 @@ class JobPipeline implements ShouldQueue
public $passable; public $passable;
/** @var bool */ /** @var bool */
public $shouldBeQueued; public $queue;
public function __construct($jobs, callable $send = null, bool $shouldBeQueued = null) public function __construct($jobs, callable $send = null, bool $queue = null)
{ {
$this->jobs = $jobs; $this->jobs = $jobs;
$this->send = $send ?? function ($event) { $this->send = $send ?? function ($event) {
// If no $send callback is set, we'll just pass the event through the jobs. // If no $send callback is set, we'll just pass the event through the jobs.
return $event; return $event;
}; };
$this->shouldBeQueued = $shouldBeQueued ?? static::$shouldBeQueuedByDefault; $this->queue = $queue ?? static::$queueByDefault;
} }
/** @param callable[]|string[] $jobs */ /** @param callable[]|string[] $jobs */
@ -47,9 +47,9 @@ class JobPipeline implements ShouldQueue
return $this; return $this;
} }
public function shouldBeQueued(bool $shouldBeQueued) public function queue(bool $queue)
{ {
$this->shouldBeQueued = $shouldBeQueued; $this->queue = $queue;
return $this; return $this;
} }
@ -69,7 +69,7 @@ class JobPipeline implements ShouldQueue
return function (...$args) { return function (...$args) {
$executable = $this->executable($args); $executable = $this->executable($args);
if ($this->shouldBeQueued) { if ($this->queue) {
dispatch($executable); dispatch($executable);
} else { } else {
dispatch_now($executable); dispatch_now($executable);

View file

@ -1,6 +1,6 @@
<?php <?php
namespace Stancl\Tenancy\Events\Listeners; namespace Stancl\Tenancy\Listeners;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;

View file

@ -1,7 +1,8 @@
<?php <?php
namespace Stancl\Tenancy\Events\Listeners; namespace Stancl\Tenancy\Listeners;
use Stancl\Tenancy\Events\RevertedToCentralContext;
use Stancl\Tenancy\Events\TenancyEnded; use Stancl\Tenancy\Events\TenancyEnded;
class RevertToCentralContext class RevertToCentralContext
@ -9,7 +10,9 @@ class RevertToCentralContext
public function handle(TenancyEnded $event) public function handle(TenancyEnded $event)
{ {
foreach ($event->tenancy->getBootstrappers() as $bootstrapper) { foreach ($event->tenancy->getBootstrappers() as $bootstrapper) {
$bootstrapper->end(); $bootstrapper->revert();
} }
event(new RevertedToCentralContext($event->tenancy));
} }
} }

View file

@ -1,6 +1,6 @@
<?php <?php
namespace Stancl\Tenancy\Events\Listeners; namespace Stancl\Tenancy\Listeners;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Pivot; use Illuminate\Database\Eloquent\Relations\Pivot;

View file

@ -9,7 +9,6 @@ use Illuminate\Http\Request;
use Stancl\Tenancy\Resolvers\RequestDataTenantResolver; use Stancl\Tenancy\Resolvers\RequestDataTenantResolver;
use Stancl\Tenancy\Tenancy; use Stancl\Tenancy\Tenancy;
// todo write tests for this
class InitializeTenancyByRequestData extends IdentificationMiddleware class InitializeTenancyByRequestData extends IdentificationMiddleware
{ {
/** @var string|null */ /** @var string|null */

View file

@ -24,7 +24,7 @@ class CacheTenancyBootstrapper implements TenancyBootstrapper
$this->app = $app; $this->app = $app;
} }
public function start(Tenant $tenant) public function bootstrap(Tenant $tenant)
{ {
$this->resetFacadeCache(); $this->resetFacadeCache();
@ -34,7 +34,7 @@ class CacheTenancyBootstrapper implements TenancyBootstrapper
}); });
} }
public function end() public function revert()
{ {
$this->resetFacadeCache(); $this->resetFacadeCache();

View file

@ -20,7 +20,7 @@ class DatabaseTenancyBootstrapper implements TenancyBootstrapper
$this->database = $database; $this->database = $database;
} }
public function start(Tenant $tenant) public function bootstrap(Tenant $tenant)
{ {
/** @var TenantWithDatabase $tenant */ /** @var TenantWithDatabase $tenant */
@ -32,7 +32,7 @@ class DatabaseTenancyBootstrapper implements TenancyBootstrapper
$this->database->connectToTenant($tenant); $this->database->connectToTenant($tenant);
} }
public function end() public function revert()
{ {
$this->database->reconnectToCentral(); $this->database->reconnectToCentral();
} }

View file

@ -34,7 +34,7 @@ class FilesystemTenancyBootstrapper implements TenancyBootstrapper
}); });
} }
public function start(Tenant $tenant) public function bootstrap(Tenant $tenant)
{ {
$suffix = $this->app['config']['tenancy.filesystem.suffix_base'] . $tenant->getTenantKey(); $suffix = $this->app['config']['tenancy.filesystem.suffix_base'] . $tenant->getTenantKey();
@ -69,7 +69,7 @@ class FilesystemTenancyBootstrapper implements TenancyBootstrapper
} }
} }
public function end() public function revert()
{ {
// storage_path() // storage_path()
$this->app->useStoragePath($this->originalPaths['storage']); $this->app->useStoragePath($this->originalPaths['storage']);

View file

@ -67,12 +67,12 @@ class QueueTenancyBootstrapper implements TenancyBootstrapper
} }
} }
public function start(Tenant $tenant) public function bootstrap(Tenant $tenant)
{ {
$this->tenancyInitialized = true; $this->tenancyInitialized = true;
} }
public function end() public function revert()
{ {
$this->tenancyInitialized = false; $this->tenancyInitialized = false;
} }

View file

@ -22,7 +22,7 @@ class RedisTenancyBootstrapper implements TenancyBootstrapper
$this->config = $config; $this->config = $config;
} }
public function start(Tenant $tenant) public function bootstrap(Tenant $tenant)
{ {
foreach ($this->prefixedConnections() as $connection) { foreach ($this->prefixedConnections() as $connection) {
$prefix = $this->config['tenancy.redis.prefix_base'] . $tenant->getTenantKey(); $prefix = $this->config['tenancy.redis.prefix_base'] . $tenant->getTenantKey();
@ -33,7 +33,7 @@ class RedisTenancyBootstrapper implements TenancyBootstrapper
} }
} }
public function end() public function revert()
{ {
foreach ($this->prefixedConnections() as $connection) { foreach ($this->prefixedConnections() as $connection) {
$client = Redis::connection($connection)->client(); $client = Redis::connection($connection)->client();

View file

@ -21,23 +21,7 @@ class TenancyServiceProvider extends ServiceProvider
{ {
$this->mergeConfigFrom(__DIR__ . '/../assets/config.php', 'tenancy'); $this->mergeConfigFrom(__DIR__ . '/../assets/config.php', 'tenancy');
$this->app->bind(Contracts\UniqueIdentifierGenerator::class, $this->app['config']['tenancy.id_generator']);
$this->app->singleton(DatabaseManager::class); $this->app->singleton(DatabaseManager::class);
$this->app->singleton(Tenancy::class);
$this->app->extend(Tenancy::class, function (Tenancy $tenancy) {
foreach ($this->app['config']['tenancy.features'] as $feature) {
$this->app[$feature]->bootstrap($tenancy);
}
return $tenancy;
});
$this->app->bind(Tenant::class, function ($app) {
return $app[Tenancy::class]->tenant;
});
foreach ($this->app['config']['tenancy.bootstrappers'] as $bootstrapper) {
$this->app->singleton($bootstrapper);
}
$this->app->singleton(Commands\Migrate::class, function ($app) { $this->app->singleton(Commands\Migrate::class, function ($app) {
return new Commands\Migrate($app['migrator'], $app[DatabaseManager::class]); return new Commands\Migrate($app['migrator'], $app[DatabaseManager::class]);
@ -52,8 +36,6 @@ class TenancyServiceProvider extends ServiceProvider
$this->app->bind('globalCache', function ($app) { $this->app->bind('globalCache', function ($app) {
return new CacheManager($app); return new CacheManager($app);
}); });
$this->app->register(TenantRouteServiceProvider::class);
} }
/** /**

View file

@ -7,11 +7,10 @@ namespace Stancl\Tenancy\TenantDatabaseManagers;
use Illuminate\Contracts\Config\Repository; use Illuminate\Contracts\Config\Repository;
use Illuminate\Database\Connection; use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\Contracts\Future\CanSetConnection;
use Stancl\Tenancy\Contracts\TenantDatabaseManager; use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Contracts\TenantWithDatabase; use Stancl\Tenancy\Contracts\TenantWithDatabase;
class MySQLDatabaseManager implements TenantDatabaseManager, CanSetConnection class MySQLDatabaseManager implements TenantDatabaseManager
{ {
/** @var string */ /** @var string */
protected $connection; protected $connection;

View file

@ -7,11 +7,10 @@ namespace Stancl\Tenancy\TenantDatabaseManagers;
use Illuminate\Contracts\Config\Repository; use Illuminate\Contracts\Config\Repository;
use Illuminate\Database\Connection; use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\Contracts\Future\CanSetConnection;
use Stancl\Tenancy\Contracts\TenantDatabaseManager; use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Contracts\TenantWithDatabase; use Stancl\Tenancy\Contracts\TenantWithDatabase;
class PostgreSQLDatabaseManager implements TenantDatabaseManager, CanSetConnection class PostgreSQLDatabaseManager implements TenantDatabaseManager
{ {
/** @var string */ /** @var string */
protected $connection; protected $connection;

View file

@ -7,11 +7,10 @@ namespace Stancl\Tenancy\TenantDatabaseManagers;
use Illuminate\Contracts\Config\Repository; use Illuminate\Contracts\Config\Repository;
use Illuminate\Database\Connection; use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\Contracts\Future\CanSetConnection;
use Stancl\Tenancy\Contracts\TenantDatabaseManager; use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Contracts\TenantWithDatabase; use Stancl\Tenancy\Contracts\TenantWithDatabase;
class PostgreSQLSchemaManager implements TenantDatabaseManager, CanSetConnection class PostgreSQLSchemaManager implements TenantDatabaseManager
{ {
/** @var string */ /** @var string */
protected $connection; protected $connection;

View file

@ -38,4 +38,9 @@ class SQLiteDatabaseManager implements TenantDatabaseManager
return $baseConfig; return $baseConfig;
} }
public function setConnection(string $connection): void
{
//
}
} }

View file

@ -1,22 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider;
use Illuminate\Support\Facades\Route;
class TenantRouteServiceProvider extends RouteServiceProvider
{
public function map()
{
$this->app->booted(function () {
if (file_exists(base_path('routes/tenant.php'))) {
Route::middleware(['web'])
->namespace($this->app['config']['tenancy.tenant_route_namespace'] ?? 'App\Http\Controllers')
->group(base_path('routes/tenant.php'));
}
});
}
}

View file

@ -4,9 +4,9 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Traits; namespace Stancl\Tenancy\Traits;
use Stancl\Tenancy\Tenant;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Stancl\Tenancy\Contracts\Tenant;
trait TenantAwareCommand trait TenantAwareCommand
{ {

View file

@ -1,12 +1,12 @@
<?php <?php
namespace Stancl\Tenancy\Tests\v3; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Stancl\Tenancy\Contracts\TenancyBootstrapper; use Stancl\Tenancy\Contracts\TenancyBootstrapper;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\Listeners\RevertToCentralContext; use Stancl\Tenancy\Listeners\RevertToCentralContext;
use Stancl\Tenancy\Events\TenancyEnded; use Stancl\Tenancy\Events\TenancyEnded;
use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Tests\TestCase; use Stancl\Tenancy\Tests\TestCase;

View file

@ -1,6 +1,6 @@
<?php <?php
namespace Stancl\Tenancy\Tests\v3; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -8,9 +8,9 @@ use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Redis; use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\Listeners\JobPipeline; use Stancl\Tenancy\Listeners\JobPipeline;
use Stancl\Tenancy\Events\Listeners\RevertToCentralContext; use Stancl\Tenancy\Listeners\RevertToCentralContext;
use Stancl\Tenancy\Events\TenancyEnded; use Stancl\Tenancy\Events\TenancyEnded;
use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Events\TenantCreated; use Stancl\Tenancy\Events\TenantCreated;

View file

@ -2,11 +2,11 @@
declare(strict_types=1); declare(strict_types=1);
namespace Stancl\Tenancy\Tests\v3; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\TenancyBootstrappers\CacheTenancyBootstrapper; use Stancl\Tenancy\TenancyBootstrappers\CacheTenancyBootstrapper;
use Stancl\Tenancy\Tests\TestCase; use Stancl\Tenancy\Tests\TestCase;

View file

@ -1,10 +1,10 @@
<?php <?php
namespace Stancl\Tenancy\Tests\v3; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Stancl\Tenancy\Database\Models; use Stancl\Tenancy\Database\Models;
use Stancl\Tenancy\Database\Models\Concerns\HasDomains; use Stancl\Tenancy\Database\Concerns\HasDomains;
use Stancl\Tenancy\Middleware\InitializeTenancyByDomainOrSubdomain; use Stancl\Tenancy\Middleware\InitializeTenancyByDomainOrSubdomain;
use Stancl\Tenancy\Tests\TestCase; use Stancl\Tenancy\Tests\TestCase;
@ -22,7 +22,7 @@ class CombinedDomainAndSubdomainIdentificationTest extends TestCase
}); });
}); });
config(['tenancy.tenant_model' => Tenant::class]); config(['tenancy.tenant_model' => CombinedTenant::class]);
} }
/** @test */ /** @test */
@ -30,7 +30,7 @@ class CombinedDomainAndSubdomainIdentificationTest extends TestCase
{ {
config(['tenancy.central_domains' => ['localhost']]); config(['tenancy.central_domains' => ['localhost']]);
$tenant = Tenant::create([ $tenant = CombinedTenant::create([
'id' => 'acme', 'id' => 'acme',
]); ]);
@ -53,7 +53,7 @@ class CombinedDomainAndSubdomainIdentificationTest extends TestCase
{ {
config(['tenancy.central_domains' => []]); config(['tenancy.central_domains' => []]);
$tenant = Tenant::create([ $tenant = CombinedTenant::create([
'id' => 'acme', 'id' => 'acme',
]); ]);
@ -72,7 +72,7 @@ class CombinedDomainAndSubdomainIdentificationTest extends TestCase
} }
} }
class Tenant extends Models\Tenant class CombinedTenant extends Models\Tenant
{ {
use HasDomains; use HasDomains;
} }

View file

@ -2,7 +2,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Stancl\Tenancy\Tests\v3; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -10,9 +10,9 @@ use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Stancl\Tenancy\Tests\Etc\ExampleSeeder; use Stancl\Tenancy\Tests\Etc\ExampleSeeder;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\Listeners\JobPipeline; use Stancl\Tenancy\Listeners\JobPipeline;
use Stancl\Tenancy\Events\Listeners\RevertToCentralContext; use Stancl\Tenancy\Listeners\RevertToCentralContext;
use Stancl\Tenancy\Events\TenancyEnded; use Stancl\Tenancy\Events\TenancyEnded;
use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Events\TenantCreated; use Stancl\Tenancy\Events\TenantCreated;

View file

@ -1,6 +1,6 @@
<?php <?php
namespace Stancl\Tenancy\Tests\v3; namespace Stancl\Tenancy\Tests;
use Illuminate\Database\Seeder; use Illuminate\Database\Seeder;
use Illuminate\Foundation\Auth\User as Authenticable; use Illuminate\Foundation\Auth\User as Authenticable;
@ -8,7 +8,7 @@ use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\Listeners\JobPipeline; use Stancl\Tenancy\Listeners\JobPipeline;
use Stancl\Tenancy\Events\TenantCreated; use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Jobs\CreateDatabase; use Stancl\Tenancy\Jobs\CreateDatabase;
use Stancl\Tenancy\Jobs\MigrateDatabase; use Stancl\Tenancy\Jobs\MigrateDatabase;

View file

@ -2,7 +2,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Stancl\Tenancy\Tests\v3; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
@ -12,9 +12,12 @@ use Stancl\Tenancy\Exceptions\TenantDatabaseUserAlreadyExistsException;
use Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager; use Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager;
use Stancl\Tenancy\TenantDatabaseManagers\PermissionControlledMySQLDatabaseManager; use Stancl\Tenancy\TenantDatabaseManagers\PermissionControlledMySQLDatabaseManager;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\Listeners\JobPipeline; use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Listeners\JobPipeline;
use Stancl\Tenancy\Events\TenantCreated; use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Jobs\CreateDatabase; use Stancl\Tenancy\Jobs\CreateDatabase;
use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\TenancyBootstrappers\DatabaseTenancyBootstrapper;
use Stancl\Tenancy\Tests\TestCase; use Stancl\Tenancy\Tests\TestCase;
class DatabaseUsersTest extends TestCase class DatabaseUsersTest extends TestCase
@ -96,19 +99,20 @@ class DatabaseUsersTest extends TestCase
config([ config([
'tenancy.database_managers.mysql' => MySQLDatabaseManager::class, 'tenancy.database_managers.mysql' => MySQLDatabaseManager::class,
'tenancy.database.suffix' => '', 'tenancy.database.suffix' => '',
'tenancy.database.template_connection' => 'mysql', 'tenancy.template_tenant_connection' => 'mysql',
'tenancy.bootstrappers' => [
DatabaseTenancyBootstrapper::class,
],
]); ]);
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
$tenant = Tenant::create([ $tenant = Tenant::create([
'id' => 'foo' . Str::random(10), 'id' => 'foo' . Str::random(10),
]); ]);
$this->assertTrue($tenant->database()->manager() instanceof MySQLDatabaseManager); $this->assertTrue($tenant->database()->manager() instanceof MySQLDatabaseManager);
$tenant = Tenant::create([
'id' => 'foo' . Str::random(10),
]);
tenancy()->initialize($tenant); // check if everything works tenancy()->initialize($tenant); // check if everything works
tenancy()->end(); tenancy()->end();

View file

@ -1,10 +1,10 @@
<?php <?php
namespace Stancl\Tenancy\Tests\v3; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Stancl\Tenancy\Database\Models; use Stancl\Tenancy\Database\Models;
use Stancl\Tenancy\Database\Models\Concerns\HasDomains; use Stancl\Tenancy\Database\Concerns\HasDomains;
use Stancl\Tenancy\Exceptions\DomainOccupiedByOtherTenantException; use Stancl\Tenancy\Exceptions\DomainOccupiedByOtherTenantException;
use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedOnDomainException; use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedOnDomainException;
use Stancl\Tenancy\Middleware\InitializeTenancyByDomain; use Stancl\Tenancy\Middleware\InitializeTenancyByDomain;
@ -25,13 +25,13 @@ class DomainTest extends TestCase
}); });
}); });
config(['tenancy.tenant_model' => Tenant::class]); config(['tenancy.tenant_model' => DomainTenant::class]);
} }
/** @test */ /** @test */
public function tenant_can_be_identified_using_hostname() public function tenant_can_be_identified_using_hostname()
{ {
$tenant = Tenant::create(); $tenant = DomainTenant::create();
$id = $tenant->id; $id = $tenant->id;
@ -48,13 +48,13 @@ class DomainTest extends TestCase
/** @test */ /** @test */
public function a_domain_can_belong_to_only_one_tenant() public function a_domain_can_belong_to_only_one_tenant()
{ {
$tenant = Tenant::create(); $tenant = DomainTenant::create();
$tenant->domains()->create([ $tenant->domains()->create([
'domain' => 'foo.localhost', 'domain' => 'foo.localhost',
]); ]);
$tenant2 = Tenant::create(); $tenant2 = DomainTenant::create();
$this->expectException(DomainOccupiedByOtherTenantException::class); $this->expectException(DomainOccupiedByOtherTenantException::class);
$tenant2->domains()->create([ $tenant2->domains()->create([
@ -73,7 +73,7 @@ class DomainTest extends TestCase
/** @test */ /** @test */
public function tenant_can_be_identified_by_domain() public function tenant_can_be_identified_by_domain()
{ {
$tenant = Tenant::create([ $tenant = DomainTenant::create([
'id' => 'acme', 'id' => 'acme',
]); ]);
@ -104,7 +104,7 @@ class DomainTest extends TestCase
} }
} }
class Tenant extends Models\Tenant class DomainTenant extends Models\Tenant
{ {
use HasDomains; use HasDomains;
} }

View file

@ -6,7 +6,7 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
class CreateUsersTable extends Migration class TestCreateUsersTable extends Migration
{ {
/** /**
* Run the migrations. * Run the migrations.

View file

@ -1 +0,0 @@
{"foo":"bar"}

View file

@ -1,12 +1,12 @@
<?php <?php
namespace Stancl\Tenancy\Tests\v3; namespace Stancl\Tenancy\Tests;
use Illuminate\Events\CallQueuedListener; use Illuminate\Events\CallQueuedListener;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Queue; use Illuminate\Support\Facades\Queue;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\Listeners\QueueableListener; use Stancl\Tenancy\Listeners\QueueableListener;
use Stancl\Tenancy\Events\TenantCreated; use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Tests\TestCase; use Stancl\Tenancy\Tests\TestCase;
@ -41,8 +41,6 @@ class EventListenerTest extends TestCase
$this->assertFalse(app()->bound('foo')); $this->assertFalse(app()->bound('foo'));
} }
// todo test that the way the published SP registers events works
} }
class FooListener extends QueueableListener class FooListener extends QueueableListener

View file

@ -4,32 +4,39 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Tests\Features; namespace Stancl\Tenancy\Tests\Features;
use Illuminate\Support\Facades\Event;
use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\TenancyEnded;
use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Features\TenantConfig; use Stancl\Tenancy\Features\TenantConfig;
use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Listeners\RevertToCentralContext;
use Stancl\Tenancy\Tests\TestCase; use Stancl\Tenancy\Tests\TestCase;
class TenantConfigTest extends TestCase class TenantConfigTest extends TestCase
{ {
public $autoInitTenancy = false;
public $autoCreateTenant = false;
/** @test */ /** @test */
public function config_is_merged_and_removed() public function config_is_merged_and_removed()
{ {
$this->assertSame(null, config('services.paypal')); $this->assertSame(null, config('services.paypal'));
config([ config([
'tenancy.features' => [TenantConfig::class], 'tenancy.features' => [TenantConfig::class],
'tenancy.bootstrappers' => [],
]); ]);
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
TenantConfig::$storageToConfigMap = [ TenantConfig::$storageToConfigMap = [
'paypal_api_public' => 'services.paypal.public', 'paypal_api_public' => 'services.paypal.public',
'paypal_api_private' => 'services.paypal.private', 'paypal_api_private' => 'services.paypal.private',
]; ];
tenancy()->create('foo.localhost', [ $tenant = Tenant::create([
'paypal_api_public' => 'foo', 'paypal_api_public' => 'foo',
'paypal_api_private' => 'bar', 'paypal_api_private' => 'bar',
]); ]);
tenancy()->init('foo.localhost'); tenancy()->initialize($tenant);
$this->assertSame(['public' => 'foo', 'private' => 'bar'], config('services.paypal')); $this->assertSame(['public' => 'foo', 'private' => 'bar'], config('services.paypal'));
tenancy()->end(); tenancy()->end();

View file

@ -1,56 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Tests\Features;
use Stancl\Tenancy\Features\Timestamps;
use Stancl\Tenancy\Tenant;
use Stancl\Tenancy\Tests\TestCase;
class TimestampTest extends TestCase
{
public $autoCreateTenant = false;
public $autoInitTenancy = false;
public function setUp(): void
{
parent::setUp();
config(['tenancy.features' => [
Timestamps::class,
]]);
}
/** @test */
public function create_and_update_timestamps_are_added_on_create()
{
$tenant = Tenant::new()->save();
$this->assertArrayHasKey('created_at', $tenant->data);
$this->assertArrayHasKey('updated_at', $tenant->data);
}
/** @test */
public function update_timestamps_are_added()
{
$tenant = Tenant::new()->save();
$this->assertSame($tenant->created_at, $tenant->updated_at);
$this->assertSame('string', gettype($tenant->created_at));
sleep(1);
$tenant->put('abc', 'def');
$this->assertTrue($tenant->updated_at > $tenant->created_at);
}
/** @test */
public function softdelete_timestamps_are_added()
{
$tenant = Tenant::new()->save();
$this->assertNull($tenant->deleted_at);
$tenant->softDelete();
$this->assertNotNull($tenant->deleted_at);
}
}

View file

@ -2,12 +2,12 @@
declare(strict_types=1); declare(strict_types=1);
namespace Stancl\Tenancy\Tests\v3; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Stancl\Tenancy\Facades\GlobalCache; use Stancl\Tenancy\Facades\GlobalCache;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\TenancyBootstrappers\CacheTenancyBootstrapper; use Stancl\Tenancy\TenancyBootstrappers\CacheTenancyBootstrapper;
use Stancl\Tenancy\Tests\TestCase; use Stancl\Tenancy\Tests\TestCase;

View file

@ -1,12 +1,12 @@
<?php <?php
namespace Stancl\Tenancy\Tests\v3; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Queue; use Illuminate\Support\Facades\Queue;
use Spatie\Valuestore\Valuestore; use Spatie\Valuestore\Valuestore;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\Listeners\JobPipeline; use Stancl\Tenancy\Listeners\JobPipeline;
use Stancl\Tenancy\Events\TenantCreated; use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Tests\TestCase; use Stancl\Tenancy\Tests\TestCase;
@ -23,7 +23,7 @@ class JobPipelineTest extends TestCase
config(['queue.default' => 'redis']); config(['queue.default' => 'redis']);
$this->valuestore = Valuestore::make(__DIR__ . '/../Etc/tmp/jobpipelinetest.json')->flush(); $this->valuestore = Valuestore::make(__DIR__ . '/Etc/tmp/jobpipelinetest.json')->flush();
} }
/** @test */ /** @test */
@ -51,7 +51,7 @@ class JobPipelineTest extends TestCase
FooJob::class, FooJob::class,
])->send(function () { ])->send(function () {
return $this->valuestore; return $this->valuestore;
})->shouldBeQueued(true)->toListener()); })->queue(true)->toListener());
Queue::assertNothingPushed(); Queue::assertNothingPushed();
@ -70,7 +70,7 @@ class JobPipelineTest extends TestCase
FooJob::class, FooJob::class,
])->send(function () { ])->send(function () {
return $this->valuestore; return $this->valuestore;
})->shouldBeQueued(true)->toListener()); })->queue(true)->toListener());
$this->assertFalse($this->valuestore->has('foo')); $this->assertFalse($this->valuestore->has('foo'));
Tenant::create(); Tenant::create();

View file

@ -1,6 +1,6 @@
<?php <?php
namespace Stancl\Tenancy\Tests\v3; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Database\Models\Tenant;

View file

@ -1,6 +1,6 @@
<?php <?php
namespace Stancl\Tenancy\Tests\v3; namespace Stancl\Tenancy\Tests;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
@ -11,7 +11,7 @@ use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Spatie\Valuestore\Valuestore; use Spatie\Valuestore\Valuestore;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\TenancyBootstrappers\QueueTenancyBootstrapper; use Stancl\Tenancy\TenancyBootstrappers\QueueTenancyBootstrapper;
use Stancl\Tenancy\Tests\TestCase; use Stancl\Tenancy\Tests\TestCase;
@ -36,17 +36,19 @@ class QueueTest extends TestCase
Event::listen(TenancyInitialized::class, BootstrapTenancy::class); Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
$this->valuestore = Valuestore::make(__DIR__ . '/../Etc/tmp/queuetest.json')->flush(); $this->valuestore = Valuestore::make(__DIR__ . '/Etc/tmp/queuetest.json')->flush();
} }
/** @test */ /** @test */
public function tenant_id_is_passed_to_tenant_queues() public function tenant_id_is_passed_to_tenant_queues()
{ {
config(['queue.default' => 'sync']);
$tenant = Tenant::create(); $tenant = Tenant::create();
tenancy()->initialize($tenant); tenancy()->initialize($tenant);
Event::fake(); Event::fake([JobProcessing::class]);
dispatch(new TestJob($this->valuestore)); dispatch(new TestJob($this->valuestore));

View file

@ -2,7 +2,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Stancl\Tenancy\Tests\v3; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Database\Models\Tenant;

View file

@ -9,14 +9,14 @@ use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Queue; use Illuminate\Support\Facades\Queue;
use Stancl\Tenancy\Contracts\Syncable; use Stancl\Tenancy\Contracts\Syncable;
use Stancl\Tenancy\Contracts\SyncMaster; use Stancl\Tenancy\Contracts\SyncMaster;
use Stancl\Tenancy\Database\Models\Concerns\CentralConnection; use Stancl\Tenancy\Database\Concerns\CentralConnection;
use Stancl\Tenancy\Database\Models\Concerns\ResourceSyncing; use Stancl\Tenancy\Database\Concerns\ResourceSyncing;
use Stancl\Tenancy\Database\Models; use Stancl\Tenancy\Database\Models;
use Stancl\Tenancy\Database\Models\TenantPivot; use Stancl\Tenancy\Database\Models\TenantPivot;
use Stancl\Tenancy\Events\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\Listeners\JobPipeline; use Stancl\Tenancy\Listeners\JobPipeline;
use Stancl\Tenancy\Events\Listeners\RevertToCentralContext; use Stancl\Tenancy\Listeners\RevertToCentralContext;
use Stancl\Tenancy\Events\Listeners\UpdateSyncedResource; use Stancl\Tenancy\Listeners\UpdateSyncedResource;
use Stancl\Tenancy\Events\SyncedResourceChangedInForeignDatabase; use Stancl\Tenancy\Events\SyncedResourceChangedInForeignDatabase;
use Stancl\Tenancy\Events\SyncedResourceSaved; use Stancl\Tenancy\Events\SyncedResourceSaved;
use Stancl\Tenancy\Events\TenancyEnded; use Stancl\Tenancy\Events\TenancyEnded;
@ -49,8 +49,8 @@ class ResourceSyncingTest extends TestCase
$this->artisan('migrate', [ $this->artisan('migrate', [
'--path' => [ '--path' => [
__DIR__ . '/../Etc/synced_resource_migrations', __DIR__ . '/Etc/synced_resource_migrations',
__DIR__ . '/../Etc/synced_resource_migrations/users' __DIR__ . '/Etc/synced_resource_migrations/users'
], ],
'--realpath' => true, '--realpath' => true,
])->assertExitCode(0); ])->assertExitCode(0);
@ -59,7 +59,7 @@ class ResourceSyncingTest extends TestCase
protected function migrateTenants() protected function migrateTenants()
{ {
$this->artisan('tenants:migrate', [ $this->artisan('tenants:migrate', [
'--path' => __DIR__ . '/../Etc/synced_resource_migrations/users', '--path' => __DIR__ . '/Etc/synced_resource_migrations/users',
'--realpath' => true, '--realpath' => true,
])->assertExitCode(0); ])->assertExitCode(0);
} }
@ -69,7 +69,7 @@ class ResourceSyncingTest extends TestCase
{ {
Event::fake([SyncedResourceSaved::class]); Event::fake([SyncedResourceSaved::class]);
$user = User::create([ $user = ResourceUser::create([
'name' => 'Foo', 'name' => 'Foo',
'email' => 'foo@email.com', 'email' => 'foo@email.com',
'password' => 'secret', 'password' => 'secret',
@ -94,13 +94,13 @@ class ResourceSyncingTest extends TestCase
'role' => 'superadmin', // unsynced 'role' => 'superadmin', // unsynced
]); ]);
$tenant = Tenant::create(); $tenant = ResourceTenant::create();
$this->migrateTenants(); $this->migrateTenants();
tenancy()->initialize($tenant); tenancy()->initialize($tenant);
// Create the same user in tenant DB // Create the same user in tenant DB
$user = User::create([ $user = ResourceUser::create([
'global_id' => 'acme', 'global_id' => 'acme',
'name' => 'John Doe', 'name' => 'John Doe',
'email' => 'john@localhost', 'email' => 'john@localhost',
@ -135,22 +135,22 @@ class ResourceSyncingTest extends TestCase
'email' => 'john@foreignhost', // synced 'email' => 'john@foreignhost', // synced
'password' => 'secret', // no changes 'password' => 'secret', // no changes
'role' => 'superadmin', // unsynced 'role' => 'superadmin', // unsynced
], User::first()->getAttributes()); ], ResourceUser::first()->getAttributes());
} }
/** @test */ /** @test */
public function creating_the_resource_in_tenant_database_creates_it_in_central_database_and_creates_the_mapping() public function creating_the_resource_in_tenant_database_creates_it_in_central_database_and_creates_the_mapping()
{ {
// Assert no user in central DB // Assert no user in central DB
$this->assertCount(0, User::all()); $this->assertCount(0, ResourceUser::all());
$tenant = Tenant::create(); $tenant = ResourceTenant::create();
$this->migrateTenants(); $this->migrateTenants();
tenancy()->initialize($tenant); tenancy()->initialize($tenant);
// Create the same user in tenant DB // Create the same user in tenant DB
User::create([ ResourceUser::create([
'global_id' => 'acme', 'global_id' => 'acme',
'name' => 'John Doe', 'name' => 'John Doe',
'email' => 'john@localhost', 'email' => 'john@localhost',
@ -170,7 +170,7 @@ class ResourceSyncingTest extends TestCase
// Assert role change doesn't cascade // Assert role change doesn't cascade
CentralUser::first()->update(['role' => 'central superadmin']); CentralUser::first()->update(['role' => 'central superadmin']);
tenancy()->initialize($tenant); tenancy()->initialize($tenant);
$this->assertSame('commenter', User::first()->role); $this->assertSame('commenter', ResourceUser::first()->role);
} }
/** @test */ /** @test */
@ -182,7 +182,7 @@ class ResourceSyncingTest extends TestCase
$this->assertFalse(tenancy()->initialized); $this->assertFalse(tenancy()->initialized);
$this->expectException(ModelNotSyncMaster::class); $this->expectException(ModelNotSyncMaster::class);
User::first()->update(['role' => 'foobar']); ResourceUser::first()->update(['role' => 'foobar']);
} }
/** @test */ /** @test */
@ -196,19 +196,19 @@ class ResourceSyncingTest extends TestCase
'role' => 'commenter', // unsynced 'role' => 'commenter', // unsynced
]); ]);
$tenant = Tenant::create([ $tenant = ResourceTenant::create([
'id' => 't1', 'id' => 't1',
]); ]);
$this->migrateTenants(); $this->migrateTenants();
$tenant->run(function () { $tenant->run(function () {
$this->assertCount(0, User::all()); $this->assertCount(0, ResourceUser::all());
}); });
$centralUser->tenants()->attach('t1'); $centralUser->tenants()->attach('t1');
$tenant->run(function () { $tenant->run(function () {
$this->assertCount(1, User::all()); $this->assertCount(1, ResourceUser::all());
}); });
} }
@ -223,13 +223,13 @@ class ResourceSyncingTest extends TestCase
'role' => 'commenter', // unsynced 'role' => 'commenter', // unsynced
]); ]);
$tenant = Tenant::create([ $tenant = ResourceTenant::create([
'id' => 't1', 'id' => 't1',
]); ]);
$this->migrateTenants(); $this->migrateTenants();
$tenant->run(function () { $tenant->run(function () {
$this->assertCount(0, User::all()); $this->assertCount(0, ResourceUser::all());
}); });
// The child model is inaccessible in the Pivot Model, so we can't fire any events. // The child model is inaccessible in the Pivot Model, so we can't fire any events.
@ -237,7 +237,7 @@ class ResourceSyncingTest extends TestCase
$tenant->run(function () { $tenant->run(function () {
// Still zero // Still zero
$this->assertCount(0, User::all()); $this->assertCount(0, ResourceUser::all());
}); });
} }
@ -252,15 +252,15 @@ class ResourceSyncingTest extends TestCase
'role' => 'commenter', // unsynced 'role' => 'commenter', // unsynced
]); ]);
$t1 = Tenant::create([ $t1 = ResourceTenant::create([
'id' => 't1', 'id' => 't1',
]); ]);
$t2 = Tenant::create([ $t2 = ResourceTenant::create([
'id' => 't2', 'id' => 't2',
]); ]);
$t3 = Tenant::create([ $t3 = ResourceTenant::create([
'id' => 't3', 'id' => 't3',
]); ]);
$this->migrateTenants(); $this->migrateTenants();
@ -271,17 +271,17 @@ class ResourceSyncingTest extends TestCase
$t1->run(function () { $t1->run(function () {
// assert user exists // assert user exists
$this->assertCount(1, User::all()); $this->assertCount(1, ResourceUser::all());
}); });
$t2->run(function () { $t2->run(function () {
// assert user exists // assert user exists
$this->assertCount(1, User::all()); $this->assertCount(1, ResourceUser::all());
}); });
$t3->run(function () { $t3->run(function () {
// assert user does NOT exist // assert user does NOT exist
$this->assertCount(0, User::all()); $this->assertCount(0, ResourceUser::all());
}); });
} }
@ -297,10 +297,10 @@ class ResourceSyncingTest extends TestCase
'role' => 'commenter', // unsynced 'role' => 'commenter', // unsynced
]); ]);
$t1 = Tenant::create([ $t1 = ResourceTenant::create([
'id' => 't1', 'id' => 't1',
]); ]);
$t2 = Tenant::create([ $t2 = ResourceTenant::create([
'id' => 't2', 'id' => 't2',
]); ]);
$this->migrateTenants(); $this->migrateTenants();
@ -310,7 +310,7 @@ class ResourceSyncingTest extends TestCase
$t2->run(function () { $t2->run(function () {
// Create user with the same global ID in t2 database // Create user with the same global ID in t2 database
User::create([ ResourceUser::create([
'global_id' => 'acme', 'global_id' => 'acme',
'name' => 'John Foo', // changed 'name' => 'John Foo', // changed
'email' => 'john@foo', // changed 'email' => 'john@foo', // changed
@ -325,7 +325,7 @@ class ResourceSyncingTest extends TestCase
$this->assertSame('commenter', $centralUser->role); // role didn't change $this->assertSame('commenter', $centralUser->role); // role didn't change
$t1->run(function () { $t1->run(function () {
$user = User::first(); $user = ResourceUser::first();
$this->assertSame('John Foo', $user->name); // name changed $this->assertSame('John Foo', $user->name); // name changed
$this->assertSame('john@foo', $user->email); // email changed $this->assertSame('john@foo', $user->email); // email changed
$this->assertSame('commenter', $user->role); // role didn't change, i.e. is the same as from the original copy from central $this->assertSame('commenter', $user->role); // role didn't change, i.e. is the same as from the original copy from central
@ -344,13 +344,13 @@ class ResourceSyncingTest extends TestCase
'role' => 'commenter', // unsynced 'role' => 'commenter', // unsynced
]); ]);
$t1 = Tenant::create([ $t1 = ResourceTenant::create([
'id' => 't1', 'id' => 't1',
]); ]);
$t2 = Tenant::create([ $t2 = ResourceTenant::create([
'id' => 't2', 'id' => 't2',
]); ]);
$t3 = Tenant::create([ $t3 = ResourceTenant::create([
'id' => 't3', 'id' => 't3',
]); ]);
$this->migrateTenants(); $this->migrateTenants();
@ -361,17 +361,17 @@ class ResourceSyncingTest extends TestCase
$centralUser->tenants()->attach('t3'); $centralUser->tenants()->attach('t3');
$t3->run(function () { $t3->run(function () {
User::first()->update([ ResourceUser::first()->update([
'name' => 'John 3', 'name' => 'John 3',
'role' => 'employee', // unsynced 'role' => 'employee', // unsynced
]); ]);
$this->assertSame('employee', User::first()->role); $this->assertSame('employee', ResourceUser::first()->role);
}); });
// Check that change was cascaded to other tenants // Check that change was cascaded to other tenants
$t1->run($check = function () { $t1->run($check = function () {
$user = User::first(); $user = ResourceUser::first();
$this->assertSame('John 3', $user->name); // synced $this->assertSame('John 3', $user->name); // synced
$this->assertSame('commenter', $user->role); // unsynced $this->assertSame('commenter', $user->role); // unsynced
@ -408,7 +408,7 @@ class ResourceSyncingTest extends TestCase
'role' => 'employee', 'role' => 'employee',
]); ]);
$t1 = Tenant::create([ $t1 = ResourceTenant::create([
'id' => 't1', 'id' => 't1',
]); ]);
@ -417,21 +417,21 @@ class ResourceSyncingTest extends TestCase
$centralUser->tenants()->attach('t1'); $centralUser->tenants()->attach('t1');
$t1->run(function () { $t1->run(function () {
$this->assertSame('employee', User::first()->role); $this->assertSame('employee', ResourceUser::first()->role);
}); });
} }
/** @test */ /** @test */
public function when_the_resource_doesnt_exist_in_the_central_db_non_synced_columns_will_bubble_up_too() public function when_the_resource_doesnt_exist_in_the_central_db_non_synced_columns_will_bubble_up_too()
{ {
$t1 = Tenant::create([ $t1 = ResourceTenant::create([
'id' => 't1', 'id' => 't1',
]); ]);
$this->migrateTenants(); $this->migrateTenants();
$t1->run(function () { $t1->run(function () {
User::create([ ResourceUser::create([
'name' => 'John Doe', 'name' => 'John Doe',
'email' => 'john@doe', 'email' => 'john@doe',
'password' => 'secret', 'password' => 'secret',
@ -448,7 +448,7 @@ class ResourceSyncingTest extends TestCase
Queue::fake(); Queue::fake();
UpdateSyncedResource::$shouldQueue = true; UpdateSyncedResource::$shouldQueue = true;
$t1 = Tenant::create([ $t1 = ResourceTenant::create([
'id' => 't1', 'id' => 't1',
]); ]);
@ -457,7 +457,7 @@ class ResourceSyncingTest extends TestCase
Queue::assertNothingPushed(); Queue::assertNothingPushed();
$t1->run(function () { $t1->run(function () {
User::create([ ResourceUser::create([
'name' => 'John Doe', 'name' => 'John Doe',
'email' => 'john@doe', 'email' => 'john@doe',
'password' => 'secret', 'password' => 'secret',
@ -484,13 +484,13 @@ class ResourceSyncingTest extends TestCase
'role' => 'commenter', // unsynced 'role' => 'commenter', // unsynced
]); ]);
$t1 = Tenant::create([ $t1 = ResourceTenant::create([
'id' => 't1', 'id' => 't1',
]); ]);
$t2 = Tenant::create([ $t2 = ResourceTenant::create([
'id' => 't2', 'id' => 't2',
]); ]);
$t3 = Tenant::create([ $t3 = ResourceTenant::create([
'id' => 't3', 'id' => 't3',
]); ]);
$this->migrateTenants(); $this->migrateTenants();
@ -520,12 +520,12 @@ class ResourceSyncingTest extends TestCase
Event::fake([SyncedResourceChangedInForeignDatabase::class]); Event::fake([SyncedResourceChangedInForeignDatabase::class]);
$t3->run(function () { $t3->run(function () {
User::first()->update([ ResourceUser::first()->update([
'name' => 'John 3', 'name' => 'John 3',
'role' => 'employee', // unsynced 'role' => 'employee', // unsynced
]); ]);
$this->assertSame('employee', User::first()->role); $this->assertSame('employee', ResourceUser::first()->role);
}); });
Event::assertDispatched(SyncedResourceChangedInForeignDatabase::class, function (SyncedResourceChangedInForeignDatabase $event) { Event::assertDispatched(SyncedResourceChangedInForeignDatabase::class, function (SyncedResourceChangedInForeignDatabase $event) {
@ -545,11 +545,30 @@ class ResourceSyncingTest extends TestCase
return $event->tenant === null; return $event->tenant === null;
}); });
// todo update in global // Flush
Event::fake([SyncedResourceChangedInForeignDatabase::class]);
$centralUser->update([
'name' => 'John Central',
]);
Event::assertDispatched(SyncedResourceChangedInForeignDatabase::class, function (SyncedResourceChangedInForeignDatabase $event) {
return optional($event->tenant)->getTenantKey() === 't1';
});
Event::assertDispatched(SyncedResourceChangedInForeignDatabase::class, function (SyncedResourceChangedInForeignDatabase $event) {
return optional($event->tenant)->getTenantKey() === 't2';
});
Event::assertDispatched(SyncedResourceChangedInForeignDatabase::class, function (SyncedResourceChangedInForeignDatabase $event) {
return optional($event->tenant)->getTenantKey() === 't3';
});
// Assert NOT dispatched in central
Event::assertNotDispatched(SyncedResourceChangedInForeignDatabase::class, function (SyncedResourceChangedInForeignDatabase $event) {
return $event->tenant === null;
});
} }
} }
class Tenant extends Models\Tenant class ResourceTenant extends Models\Tenant
{ {
public function users() public function users()
{ {
@ -568,13 +587,13 @@ class CentralUser extends Model implements SyncMaster
public function tenants(): BelongsToMany public function tenants(): BelongsToMany
{ {
return $this->belongsToMany(Tenant::class, 'tenant_users', 'global_user_id', 'tenant_id') return $this->belongsToMany(ResourceTenant::class, 'tenant_users', 'global_user_id', 'tenant_id')
->using(TenantPivot::class); ->using(TenantPivot::class);
} }
public function getTenantModelName(): string public function getTenantModelName(): string
{ {
return User::class; return ResourceUser::class;
} }
public function getTenantIdColumnInMapTable(): string public function getTenantIdColumnInMapTable(): string
@ -607,10 +626,11 @@ class CentralUser extends Model implements SyncMaster
} }
} }
class User extends Model implements Syncable class ResourceUser extends Model implements Syncable
{ {
use ResourceSyncing; use ResourceSyncing;
protected $table = 'users';
protected $guarded = []; protected $guarded = [];
public $timestamps = false; public $timestamps = false;

View file

@ -1,10 +1,10 @@
<?php <?php
namespace Stancl\Tenancy\Tests\v3; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Stancl\Tenancy\Database\Models; use Stancl\Tenancy\Database\Models;
use Stancl\Tenancy\Database\Models\Concerns\HasDomains; use Stancl\Tenancy\Database\Concerns\HasDomains;
use Stancl\Tenancy\Exceptions\NotASubdomainException; use Stancl\Tenancy\Exceptions\NotASubdomainException;
use Stancl\Tenancy\Middleware\InitializeTenancyBySubdomain; use Stancl\Tenancy\Middleware\InitializeTenancyBySubdomain;
use Stancl\Tenancy\Tests\TestCase; use Stancl\Tenancy\Tests\TestCase;
@ -26,13 +26,13 @@ class SubdomainTest extends TestCase
}); });
}); });
config(['tenancy.tenant_model' => Tenant::class]); config(['tenancy.tenant_model' => SubdomainTenant::class]);
} }
/** @test */ /** @test */
public function tenant_can_be_identified_by_subdomain() public function tenant_can_be_identified_by_subdomain()
{ {
$tenant = Tenant::create([ $tenant = SubdomainTenant::create([
'id' => 'acme', 'id' => 'acme',
]); ]);
@ -105,7 +105,7 @@ class SubdomainTest extends TestCase
// not 'localhost' // not 'localhost'
]]); ]]);
$tenant = Tenant::create([ $tenant = SubdomainTenant::create([
'id' => 'acme', 'id' => 'acme',
]); ]);
@ -121,7 +121,7 @@ class SubdomainTest extends TestCase
} }
} }
class Tenant extends Models\Tenant class SubdomainTenant extends Models\Tenant
{ {
use HasDomains; use HasDomains;
} }

View file

@ -2,13 +2,13 @@
declare(strict_types=1); declare(strict_types=1);
namespace Stancl\Tenancy\Tests\v3; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Stancl\Tenancy\Controllers\TenantAssetsController; use Stancl\Tenancy\Controllers\TenantAssetsController;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Middleware\InitializeTenancyByDomain; use Stancl\Tenancy\Middleware\InitializeTenancyByDomain;
use Stancl\Tenancy\Middleware\InitializeTenancyByRequestData; use Stancl\Tenancy\Middleware\InitializeTenancyByRequestData;

View file

@ -2,7 +2,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Stancl\Tenancy\Tests\v3; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;

View file

@ -2,13 +2,13 @@
declare(strict_types=1); declare(strict_types=1);
namespace Stancl\Tenancy\Tests\v3; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Stancl\Tenancy\Database\Models\Tenant; use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\DatabaseManager; use Stancl\Tenancy\DatabaseManager;
use Stancl\Tenancy\Events\Listeners\BootstrapTenancy; use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\Listeners\JobPipeline; use Stancl\Tenancy\Listeners\JobPipeline;
use Stancl\Tenancy\Events\TenancyInitialized; use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Events\TenantCreated; use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Jobs\CreateDatabase; use Stancl\Tenancy\Jobs\CreateDatabase;

View file

@ -1,6 +1,6 @@
<?php <?php
namespace Stancl\Tenancy\Tests\v3; namespace Stancl\Tenancy\Tests;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;
@ -39,7 +39,7 @@ class TenantModelTest extends TestCase
tenancy()->end(); tenancy()->end();
$this->assertSame(null, app(Tenant::class)); $this->assertSame(null, app(Contracts\Tenant::class));
} }
/** @test */ /** @test */
@ -53,7 +53,7 @@ class TenantModelTest extends TestCase
$this->assertSame('bar', $tenant->foo); $this->assertSame('bar', $tenant->foo);
$this->assertSame(null, $tenant->data); $this->assertSame(null, $tenant->data);
// Low level test to test database structure // Low level test to assert database structure
$this->assertSame(json_encode(['foo' => 'bar']), DB::table('tenants')->where('id', $tenant->id)->first()->data); $this->assertSame(json_encode(['foo' => 'bar']), DB::table('tenants')->where('id', $tenant->id)->first()->data);
$this->assertSame(null, DB::table('tenants')->where('id', $tenant->id)->first()->foo ?? null); $this->assertSame(null, DB::table('tenants')->where('id', $tenant->id)->first()->foo ?? null);
@ -103,8 +103,8 @@ class TenantModelTest extends TestCase
unset(app()[UniqueIdentifierGenerator::class]); unset(app()[UniqueIdentifierGenerator::class]);
$tenant1 = MyTenant::create(); $tenant1 = Tenant::create();
$tenant2 = MyTenant::create(); $tenant2 = Tenant::create();
$this->assertSame(1, $tenant1->id); $this->assertSame(1, $tenant1->id);
$this->assertSame(2, $tenant2->id); $this->assertSame(2, $tenant2->id);
@ -138,7 +138,6 @@ class TenantModelTest extends TestCase
class MyTenant extends Tenant class MyTenant extends Tenant
{ {
protected $table = 'tenants'; protected $table = 'tenants';
public $increments = true;
} }
class AnotherTenant extends Model implements Contracts\Tenant class AnotherTenant extends Model implements Contracts\Tenant