mirror of
https://github.com/archtechx/tenancy.git
synced 2026-02-05 15:54:03 +00:00
Merge branch 'master' of https://github.com/archtechx/tenancy into add-skip-failing-options-to-migrate
This commit is contained in:
commit
5c7a2c26ad
24 changed files with 828 additions and 35 deletions
|
|
@ -28,6 +28,7 @@ class TenancyServiceProvider extends ServiceProvider
|
|||
Jobs\CreateDatabase::class,
|
||||
Jobs\MigrateDatabase::class,
|
||||
// Jobs\SeedDatabase::class,
|
||||
Jobs\CreateStorageSymlinks::class,
|
||||
|
||||
// Your own jobs to prepare the tenant.
|
||||
// Provision API keys, create S3 buckets, anything you want!
|
||||
|
|
@ -46,10 +47,13 @@ class TenancyServiceProvider extends ServiceProvider
|
|||
])->send(function (Events\DeletingTenant $event) {
|
||||
return $event->tenant;
|
||||
})->shouldBeQueued(false),
|
||||
|
||||
// Listeners\DeleteTenantStorage::class,
|
||||
],
|
||||
Events\TenantDeleted::class => [
|
||||
JobPipeline::make([
|
||||
Jobs\DeleteDatabase::class,
|
||||
Jobs\RemoveStorageSymlinks::class,
|
||||
])->send(function (Events\TenantDeleted $event) {
|
||||
return $event->tenant;
|
||||
})->shouldBeQueued(false), // `false` by default, but you probably want to make this `true` for production.
|
||||
|
|
@ -93,6 +97,12 @@ class TenancyServiceProvider extends ServiceProvider
|
|||
Listeners\UpdateSyncedResource::class,
|
||||
],
|
||||
|
||||
// Storage symlinks
|
||||
Events\CreatingStorageSymlink::class => [],
|
||||
Events\StorageSymlinkCreated::class => [],
|
||||
Events\RemovingStorageSymlink::class => [],
|
||||
Events\StorageSymlinkRemoved::class => [],
|
||||
|
||||
// Fired only when a synced resource is changed in a different DB than the origin DB (to avoid infinite loops)
|
||||
Events\SyncedResourceChangedInForeignDatabase::class => [],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ return [
|
|||
Stancl\Tenancy\Bootstrappers\CacheTenancyBootstrapper::class,
|
||||
Stancl\Tenancy\Bootstrappers\FilesystemTenancyBootstrapper::class,
|
||||
Stancl\Tenancy\Bootstrappers\QueueTenancyBootstrapper::class,
|
||||
Stancl\Tenancy\Bootstrappers\BatchTenancyBootstrapper::class,
|
||||
// Stancl\Tenancy\Bootstrappers\RedisTenancyBootstrapper::class, // Note: phpredis is needed
|
||||
],
|
||||
|
||||
|
|
@ -118,6 +119,24 @@ return [
|
|||
'public' => '%storage_path%/app/public/',
|
||||
],
|
||||
|
||||
/*
|
||||
* Tenant-aware Storage::disk()->url() can be enabled for specific local disks here
|
||||
* by mapping the disk's name to a name with '%tenant_id%' (this will be used as the public name of the disk).
|
||||
* Doing that will override the disk's default URL with a URL containing the current tenant's key.
|
||||
*
|
||||
* For example, Storage::disk('public')->url('') will return https://your-app.test/storage/ by default.
|
||||
* After adding 'public' => 'public-%tenant_id%' to 'url_override',
|
||||
* the returned URL will be https://your-app.test/public-1/ (%tenant_id% gets substitued by the current tenant's ID).
|
||||
*
|
||||
* Use `php artisan tenants:link` to create a symbolic link from the tenant's storage to its public directory.
|
||||
*/
|
||||
'url_override' => [
|
||||
// Note that the local disk you add must exist in the tenancy.filesystem.root_override config
|
||||
// todo@v4 Rename %tenant_id% to %tenant_key%
|
||||
// todo@v4 Rename url_override to something that describes the config key better
|
||||
'public' => 'public-%tenant_id%',
|
||||
],
|
||||
|
||||
/**
|
||||
* Should storage_path() be suffixed.
|
||||
*
|
||||
|
|
|
|||
55
src/Actions/CreateStorageSymlinksAction.php
Normal file
55
src/Actions/CreateStorageSymlinksAction.php
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Stancl\Tenancy\Actions;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use Stancl\Tenancy\Concerns\DealsWithTenantSymlinks;
|
||||
use Stancl\Tenancy\Database\Models\Tenant;
|
||||
use Stancl\Tenancy\Events\CreatingStorageSymlink;
|
||||
use Stancl\Tenancy\Events\StorageSymlinkCreated;
|
||||
|
||||
class CreateStorageSymlinksAction
|
||||
{
|
||||
use DealsWithTenantSymlinks;
|
||||
|
||||
public static function handle(Tenant|Collection|LazyCollection $tenants, bool $relativeLink = false, bool $force = false): void
|
||||
{
|
||||
$tenants = $tenants instanceof Tenant ? collect([$tenants]) : $tenants;
|
||||
|
||||
/** @var Tenant $tenant */
|
||||
foreach ($tenants as $tenant) {
|
||||
foreach (static::possibleTenantSymlinks($tenant) as $publicPath => $storagePath) {
|
||||
static::createLink($publicPath, $storagePath, $tenant, $relativeLink, $force);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static function createLink(string $publicPath, string $storagePath, Tenant $tenant, bool $relativeLink, bool $force): void
|
||||
{
|
||||
event(new CreatingStorageSymlink($tenant));
|
||||
|
||||
if (static::symlinkExists($publicPath)) {
|
||||
// If $force isn't passed, don't overwrite the existing symlink
|
||||
throw_if(! $force, new Exception("The [$publicPath] link already exists."));
|
||||
|
||||
app()->make('files')->delete($publicPath);
|
||||
}
|
||||
|
||||
// Make sure the storage path exists before we create a symlink
|
||||
if (! is_dir($storagePath)) {
|
||||
mkdir($storagePath, 0777, true);
|
||||
}
|
||||
|
||||
if ($relativeLink) {
|
||||
app()->make('files')->relativeLink($storagePath, $publicPath);
|
||||
} else {
|
||||
app()->make('files')->link($storagePath, $publicPath);
|
||||
}
|
||||
|
||||
event((new StorageSymlinkCreated($tenant)));
|
||||
}
|
||||
}
|
||||
40
src/Actions/RemoveStorageSymlinksAction.php
Normal file
40
src/Actions/RemoveStorageSymlinksAction.php
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Stancl\Tenancy\Actions;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use Stancl\Tenancy\Concerns\DealsWithTenantSymlinks;
|
||||
use Stancl\Tenancy\Database\Models\Tenant;
|
||||
use Stancl\Tenancy\Events\RemovingStorageSymlink;
|
||||
use Stancl\Tenancy\Events\StorageSymlinkRemoved;
|
||||
|
||||
class RemoveStorageSymlinksAction
|
||||
{
|
||||
use DealsWithTenantSymlinks;
|
||||
|
||||
public static function handle(Tenant|Collection|LazyCollection $tenants): void
|
||||
{
|
||||
$tenants = $tenants instanceof Tenant ? collect([$tenants]) : $tenants;
|
||||
|
||||
/** @var Tenant $tenant */
|
||||
foreach ($tenants as $tenant) {
|
||||
foreach (static::possibleTenantSymlinks($tenant) as $publicPath => $storagePath) {
|
||||
static::removeLink($publicPath, $tenant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static function removeLink(string $publicPath, Tenant $tenant): void
|
||||
{
|
||||
if (static::symlinkExists($publicPath)) {
|
||||
event(new RemovingStorageSymlink($tenant));
|
||||
|
||||
app()->make('files')->delete($publicPath);
|
||||
|
||||
event(new StorageSymlinkRemoved($tenant));
|
||||
}
|
||||
}
|
||||
}
|
||||
41
src/Bootstrappers/BatchTenancyBootstrapper.php
Normal file
41
src/Bootstrappers/BatchTenancyBootstrapper.php
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Stancl\Tenancy\Bootstrappers;
|
||||
|
||||
use Illuminate\Bus\DatabaseBatchRepository;
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Database\DatabaseManager;
|
||||
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
||||
use Stancl\Tenancy\Contracts\Tenant;
|
||||
|
||||
class BatchTenancyBootstrapper implements TenancyBootstrapper
|
||||
{
|
||||
/**
|
||||
* The previous database connection instance.
|
||||
*/
|
||||
protected ?Connection $previousConnection = null;
|
||||
|
||||
public function __construct(
|
||||
protected DatabaseBatchRepository $batchRepository,
|
||||
protected DatabaseManager $databaseManager
|
||||
) {
|
||||
}
|
||||
|
||||
public function bootstrap(Tenant $tenant)
|
||||
{
|
||||
// Update batch repository connection to use the tenant connection
|
||||
$this->previousConnection = $this->batchRepository->getConnection();
|
||||
$this->batchRepository->setConnection($this->databaseManager->connection('tenant'));
|
||||
}
|
||||
|
||||
public function revert()
|
||||
{
|
||||
if ($this->previousConnection) {
|
||||
// Replace batch repository connection with the previously replaced one
|
||||
$this->batchRepository->setConnection($this->previousConnection);
|
||||
$this->previousConnection = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -57,9 +57,10 @@ class FilesystemTenancyBootstrapper implements TenancyBootstrapper
|
|||
|
||||
foreach ($this->app['config']['tenancy.filesystem.disks'] as $disk) {
|
||||
// todo@v4 \League\Flysystem\PathPrefixer is making this a lot more painful in flysystem v2
|
||||
$diskConfig = $this->app['config']["filesystems.disks.{$disk}"];
|
||||
$originalRoot = $diskConfig['root'] ?? null;
|
||||
|
||||
$originalRoot = $this->app['config']["filesystems.disks.{$disk}.root"];
|
||||
$this->originalPaths['disks'][$disk] = $originalRoot;
|
||||
$this->originalPaths['disks']['path'][$disk] = $originalRoot;
|
||||
|
||||
$finalPrefix = str_replace(
|
||||
['%storage_path%', '%tenant%'],
|
||||
|
|
@ -74,6 +75,19 @@ class FilesystemTenancyBootstrapper implements TenancyBootstrapper
|
|||
}
|
||||
|
||||
$this->app['config']["filesystems.disks.{$disk}.root"] = $finalPrefix;
|
||||
|
||||
// Storage Url
|
||||
if ($diskConfig['driver'] === 'local') {
|
||||
$this->originalPaths['disks']['url'][$disk] = $diskConfig['url'] ?? null;
|
||||
|
||||
if ($url = str_replace(
|
||||
'%tenant_id%',
|
||||
$tenant->getTenantKey(),
|
||||
$this->app['config']["tenancy.filesystem.url_override.{$disk}"] ?? ''
|
||||
)) {
|
||||
$this->app['config']["filesystems.disks.{$disk}.url"] = url($url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -88,8 +102,16 @@ class FilesystemTenancyBootstrapper implements TenancyBootstrapper
|
|||
|
||||
// Storage facade
|
||||
Storage::forgetDisk($this->app['config']['tenancy.filesystem.disks']);
|
||||
foreach ($this->app['config']['tenancy.filesystem.disks'] as $disk) {
|
||||
$this->app['config']["filesystems.disks.{$disk}.root"] = $this->originalPaths['disks'][$disk];
|
||||
foreach ($this->app['config']['tenancy.filesystem.disks'] as $diskName) {
|
||||
$this->app['config']["filesystems.disks.$diskName.root"] = $this->originalPaths['disks']['path'][$diskName];
|
||||
$diskConfig = $this->app['config']['filesystems.disks.' . $diskName];
|
||||
|
||||
// Storage Url
|
||||
$url = $this->originalPaths['disks.url.' . $diskName] ?? null;
|
||||
|
||||
if ($diskConfig['driver'] === 'local' && ! is_null($url)) {
|
||||
$$this->app['config']["filesystems.disks.$diskName.url"] = $url;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
73
src/Commands/Link.php
Normal file
73
src/Commands/Link.php
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Stancl\Tenancy\Commands;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use Stancl\Tenancy\Actions\CreateStorageSymlinksAction;
|
||||
use Stancl\Tenancy\Actions\RemoveStorageSymlinksAction;
|
||||
use Stancl\Tenancy\Concerns\HasATenantsOption;
|
||||
|
||||
class Link extends Command
|
||||
{
|
||||
use HasATenantsOption;
|
||||
|
||||
/**
|
||||
* The console command signature.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'tenants:link
|
||||
{--tenants=* : The tenant(s) to run the command for. Default: all}
|
||||
{--relative : Create the symbolic link using relative paths}
|
||||
{--force : Recreate existing symbolic links}
|
||||
{--remove : Remove symbolic links}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create or remove tenant symbolic links.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$tenants = $this->getTenants();
|
||||
|
||||
try {
|
||||
if ($this->option('remove')) {
|
||||
$this->removeLinks($tenants);
|
||||
} else {
|
||||
$this->createLinks($tenants);
|
||||
}
|
||||
} catch (Exception $exception) {
|
||||
$this->error($exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected function removeLinks(LazyCollection $tenants): void
|
||||
{
|
||||
RemoveStorageSymlinksAction::handle($tenants);
|
||||
|
||||
$this->info('The links have been removed.');
|
||||
}
|
||||
|
||||
protected function createLinks(LazyCollection $tenants): void
|
||||
{
|
||||
CreateStorageSymlinksAction::handle(
|
||||
$tenants,
|
||||
$this->option('relative') ?? false,
|
||||
$this->option('force') ?? false,
|
||||
);
|
||||
|
||||
$this->info('The links have been created.');
|
||||
}
|
||||
}
|
||||
44
src/Concerns/DealsWithTenantSymlinks.php
Normal file
44
src/Concerns/DealsWithTenantSymlinks.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Stancl\Tenancy\Concerns;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Stancl\Tenancy\Database\Models\Tenant;
|
||||
|
||||
trait DealsWithTenantSymlinks
|
||||
{
|
||||
/**
|
||||
* Get all possible tenant symlinks, existing or not (array of ['public path' => 'storage path']).
|
||||
*
|
||||
* Tenants can have a symlink for each disk registered in the tenancy.filesystem.url_override config.
|
||||
*
|
||||
* This is used for creating all possible tenant symlinks and removing all existing tenant symlinks.
|
||||
*/
|
||||
protected static function possibleTenantSymlinks(Tenant $tenant): Collection
|
||||
{
|
||||
$diskUrls = config('tenancy.filesystem.url_override');
|
||||
$disks = config('tenancy.filesystem.root_override');
|
||||
$suffixBase = config('tenancy.filesystem.suffix_base');
|
||||
$symlinks = collect();
|
||||
$tenantKey = $tenant->getTenantKey();
|
||||
|
||||
foreach ($diskUrls as $disk => $publicPath) {
|
||||
$storagePath = str_replace('%storage_path%', $suffixBase . $tenantKey, $disks[$disk]);
|
||||
$publicPath = str_replace('%tenant_id%', $tenantKey, $publicPath);
|
||||
|
||||
tenancy()->central(function () use ($symlinks, $publicPath, $storagePath) {
|
||||
$symlinks->push([public_path($publicPath) => storage_path($storagePath)]);
|
||||
});
|
||||
}
|
||||
|
||||
return $symlinks->mapWithKeys(fn ($item) => $item);
|
||||
}
|
||||
|
||||
/** Determine if the provided path is an existing symlink. */
|
||||
protected static function symlinkExists(string $link): bool
|
||||
{
|
||||
return file_exists($link) && is_link($link);
|
||||
}
|
||||
}
|
||||
|
|
@ -10,10 +10,15 @@ use Throwable;
|
|||
|
||||
class SQLiteDatabaseManager implements TenantDatabaseManager
|
||||
{
|
||||
/**
|
||||
* SQLite Database path without ending slash.
|
||||
*/
|
||||
public static string|null $path = null;
|
||||
|
||||
public function createDatabase(TenantWithDatabase $tenant): bool
|
||||
{
|
||||
try {
|
||||
return file_put_contents(database_path($tenant->database()->getName()), '');
|
||||
return (bool) file_put_contents($this->getPath($tenant->database()->getName()), '');
|
||||
} catch (Throwable) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -22,7 +27,7 @@ class SQLiteDatabaseManager implements TenantDatabaseManager
|
|||
public function deleteDatabase(TenantWithDatabase $tenant): bool
|
||||
{
|
||||
try {
|
||||
return unlink(database_path($tenant->database()->getName()));
|
||||
return unlink($this->getPath($tenant->database()->getName()));
|
||||
} catch (Throwable) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -30,7 +35,7 @@ class SQLiteDatabaseManager implements TenantDatabaseManager
|
|||
|
||||
public function databaseExists(string $name): bool
|
||||
{
|
||||
return file_exists(database_path($name));
|
||||
return file_exists($this->getPath($name));
|
||||
}
|
||||
|
||||
public function makeConnectionConfig(array $baseConfig, string $databaseName): array
|
||||
|
|
@ -44,4 +49,13 @@ class SQLiteDatabaseManager implements TenantDatabaseManager
|
|||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function getPath(string $name): string
|
||||
{
|
||||
if (static::$path) {
|
||||
return static::$path . DIRECTORY_SEPARATOR . $name;
|
||||
}
|
||||
|
||||
return database_path($name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
9
src/Events/CreatingStorageSymlink.php
Normal file
9
src/Events/CreatingStorageSymlink.php
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Stancl\Tenancy\Events;
|
||||
|
||||
class CreatingStorageSymlink extends Contracts\TenantEvent
|
||||
{
|
||||
}
|
||||
9
src/Events/RemovingStorageSymlink.php
Normal file
9
src/Events/RemovingStorageSymlink.php
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Stancl\Tenancy\Events;
|
||||
|
||||
class RemovingStorageSymlink extends Contracts\TenantEvent
|
||||
{
|
||||
}
|
||||
9
src/Events/StorageSymlinkCreated.php
Normal file
9
src/Events/StorageSymlinkCreated.php
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Stancl\Tenancy\Events;
|
||||
|
||||
class StorageSymlinkCreated extends Contracts\TenantEvent
|
||||
{
|
||||
}
|
||||
9
src/Events/StorageSymlinkRemoved.php
Normal file
9
src/Events/StorageSymlinkRemoved.php
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Stancl\Tenancy\Events;
|
||||
|
||||
class StorageSymlinkRemoved extends Contracts\TenantEvent
|
||||
{
|
||||
}
|
||||
40
src/Jobs/CreateStorageSymlinks.php
Normal file
40
src/Jobs/CreateStorageSymlinks.php
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Stancl\Tenancy\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Stancl\Tenancy\Actions\CreateStorageSymlinksAction;
|
||||
use Stancl\Tenancy\Contracts\Tenant;
|
||||
|
||||
class CreateStorageSymlinks implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public Tenant $tenant;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Tenant $tenant)
|
||||
{
|
||||
$this->tenant = $tenant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
CreateStorageSymlinksAction::handle($this->tenant);
|
||||
}
|
||||
}
|
||||
40
src/Jobs/RemoveStorageSymlinks.php
Normal file
40
src/Jobs/RemoveStorageSymlinks.php
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Stancl\Tenancy\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Stancl\Tenancy\Actions\RemoveStorageSymlinksAction;
|
||||
use Stancl\Tenancy\Contracts\Tenant;
|
||||
|
||||
class RemoveStorageSymlinks implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public Tenant $tenant;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Tenant $tenant)
|
||||
{
|
||||
$this->tenant = $tenant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
RemoveStorageSymlinksAction::handle($this->tenant);
|
||||
}
|
||||
}
|
||||
16
src/Listeners/DeleteTenantStorage.php
Normal file
16
src/Listeners/DeleteTenantStorage.php
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Stancl\Tenancy\Listeners;
|
||||
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Stancl\Tenancy\Events\DeletingTenant;
|
||||
|
||||
class DeleteTenantStorage
|
||||
{
|
||||
public function handle(DeletingTenant $event): void
|
||||
{
|
||||
File::deleteDirectory($event->tenant->run(fn () => storage_path()));
|
||||
}
|
||||
}
|
||||
|
|
@ -78,6 +78,7 @@ class TenancyServiceProvider extends ServiceProvider
|
|||
{
|
||||
$this->commands([
|
||||
Commands\Run::class,
|
||||
Commands\Link::class,
|
||||
Commands\Seed::class,
|
||||
Commands\Install::class,
|
||||
Commands\Migrate::class,
|
||||
|
|
|
|||
69
tests/ActionTest.php
Normal file
69
tests/ActionTest.php
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Stancl\Tenancy\Events\TenancyEnded;
|
||||
use Stancl\Tenancy\Database\Models\Tenant;
|
||||
use Stancl\Tenancy\Events\TenancyInitialized;
|
||||
use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
||||
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
||||
use Stancl\Tenancy\Actions\CreateStorageSymlinksAction;
|
||||
use Stancl\Tenancy\Actions\RemoveStorageSymlinksAction;
|
||||
use Stancl\Tenancy\Bootstrappers\FilesystemTenancyBootstrapper;
|
||||
|
||||
beforeEach(function () {
|
||||
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
|
||||
});
|
||||
|
||||
// todo move these to be in the same file as the other tests from this PR (#909) rather than generic "action tests"
|
||||
|
||||
test('create storage symlinks action works', function() {
|
||||
config([
|
||||
'tenancy.bootstrappers' => [
|
||||
FilesystemTenancyBootstrapper::class,
|
||||
],
|
||||
'tenancy.filesystem.suffix_base' => 'tenant-',
|
||||
'tenancy.filesystem.root_override.public' => '%storage_path%/app/public/',
|
||||
'tenancy.filesystem.url_override.public' => 'public-%tenant_id%'
|
||||
]);
|
||||
|
||||
/** @var Tenant $tenant */
|
||||
$tenant = Tenant::create();
|
||||
$tenantKey = $tenant->getTenantKey();
|
||||
|
||||
tenancy()->initialize($tenant);
|
||||
|
||||
$this->assertDirectoryDoesNotExist($publicPath = public_path("public-$tenantKey"));
|
||||
|
||||
CreateStorageSymlinksAction::handle($tenant);
|
||||
|
||||
$this->assertDirectoryExists($publicPath);
|
||||
$this->assertEquals(storage_path("app/public/"), readlink($publicPath));
|
||||
});
|
||||
|
||||
test('remove storage symlinks action works', function() {
|
||||
config([
|
||||
'tenancy.bootstrappers' => [
|
||||
FilesystemTenancyBootstrapper::class,
|
||||
],
|
||||
'tenancy.filesystem.suffix_base' => 'tenant-',
|
||||
'tenancy.filesystem.root_override.public' => '%storage_path%/app/public/',
|
||||
'tenancy.filesystem.url_override.public' => 'public-%tenant_id%'
|
||||
]);
|
||||
|
||||
/** @var Tenant $tenant */
|
||||
$tenant = Tenant::create();
|
||||
$tenantKey = $tenant->getTenantKey();
|
||||
|
||||
tenancy()->initialize($tenant);
|
||||
|
||||
CreateStorageSymlinksAction::handle($tenant);
|
||||
|
||||
$this->assertDirectoryExists($publicPath = public_path("public-$tenantKey"));
|
||||
|
||||
RemoveStorageSymlinksAction::handle($tenant);
|
||||
|
||||
$this->assertDirectoryDoesNotExist($publicPath);
|
||||
});
|
||||
44
tests/BatchTest.php
Normal file
44
tests/BatchTest.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Bus\BatchRepository;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Stancl\Tenancy\Bootstrappers\BatchTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Events\TenancyEnded;
|
||||
use Stancl\Tenancy\Events\TenancyInitialized;
|
||||
use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
||||
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
|
||||
beforeEach(function () {
|
||||
config([
|
||||
'tenancy.bootstrappers' => [
|
||||
DatabaseTenancyBootstrapper::class,
|
||||
BatchTenancyBootstrapper::class,
|
||||
],
|
||||
]);
|
||||
|
||||
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
|
||||
});
|
||||
|
||||
test('batch repository is set to tenant connection and reverted', function () {
|
||||
$tenant = Tenant::create();
|
||||
$tenant2 = Tenant::create();
|
||||
|
||||
expect(getBatchRepositoryConnectionName())->toBe('central');
|
||||
|
||||
tenancy()->initialize($tenant);
|
||||
expect(getBatchRepositoryConnectionName())->toBe('tenant');
|
||||
|
||||
tenancy()->initialize($tenant2);
|
||||
expect(getBatchRepositoryConnectionName())->toBe('tenant');
|
||||
|
||||
tenancy()->end();
|
||||
expect(getBatchRepositoryConnectionName())->toBe('central');
|
||||
})->skip(fn() => version_compare(app()->version(), '8.0', '<'), 'Job batches are only supported in Laravel 8+');
|
||||
|
||||
function getBatchRepositoryConnectionName()
|
||||
{
|
||||
return app(BatchRepository::class)->getConnection()->getName();
|
||||
}
|
||||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Stancl\JobPipeline\JobPipeline;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
|
|
@ -14,8 +14,14 @@ use Illuminate\Support\Facades\Storage;
|
|||
use Stancl\Tenancy\Events\TenancyEnded;
|
||||
use Stancl\Tenancy\Jobs\CreateDatabase;
|
||||
use Stancl\Tenancy\Events\TenantCreated;
|
||||
use Stancl\Tenancy\Events\TenantDeleted;
|
||||
use Stancl\Tenancy\Events\DeletingTenant;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Stancl\Tenancy\Events\TenancyInitialized;
|
||||
use Stancl\Tenancy\Jobs\CreateStorageSymlinks;
|
||||
use Stancl\Tenancy\Jobs\RemoveStorageSymlinks;
|
||||
use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
||||
use Stancl\Tenancy\Listeners\DeleteTenantStorage;
|
||||
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
||||
use Stancl\Tenancy\Bootstrappers\CacheTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\RedisTenancyBootstrapper;
|
||||
|
|
@ -184,6 +190,142 @@ test('filesystem data is separated', function () {
|
|||
expect($new_storage_path)->toEqual($expected_storage_path);
|
||||
});
|
||||
|
||||
test('tenant storage can get deleted after the tenant when DeletingTenant listens to DeleteTenantStorage', function () {
|
||||
config([
|
||||
'tenancy.bootstrappers' => [
|
||||
FilesystemTenancyBootstrapper::class,
|
||||
],
|
||||
'tenancy.filesystem.root_override.public' => '%storage_path%/app/public/',
|
||||
'tenancy.filesystem.url_override.public' => 'public-%tenant_id%'
|
||||
]);
|
||||
|
||||
$tenant1 = Tenant::create();
|
||||
$tenant2 = Tenant::create();
|
||||
$tenant1StorageUrl = 'http://localhost/public-' . $tenant1->getKey().'/';
|
||||
$tenant2StorageUrl = 'http://localhost/public-' . $tenant2->getKey().'/';
|
||||
|
||||
tenancy()->initialize($tenant1);
|
||||
|
||||
$this->assertEquals(
|
||||
$tenant1StorageUrl,
|
||||
Storage::disk('public')->url('')
|
||||
);
|
||||
|
||||
Storage::disk('public')->put($tenant1FileName = 'tenant1.txt', 'text');
|
||||
|
||||
$this->assertEquals(
|
||||
$tenant1StorageUrl . $tenant1FileName,
|
||||
Storage::disk('public')->url($tenant1FileName)
|
||||
);
|
||||
|
||||
tenancy()->initialize($tenant2);
|
||||
|
||||
$this->assertEquals(
|
||||
$tenant2StorageUrl,
|
||||
Storage::disk('public')->url('')
|
||||
);
|
||||
|
||||
Storage::disk('public')->put($tenant2FileName = 'tenant2.txt', 'text');
|
||||
|
||||
$this->assertEquals(
|
||||
$tenant2StorageUrl . $tenant2FileName,
|
||||
Storage::disk('public')->url($tenant2FileName)
|
||||
);
|
||||
});
|
||||
|
||||
test('files can get fetched using the storage url', function() {
|
||||
config([
|
||||
'tenancy.bootstrappers' => [
|
||||
FilesystemTenancyBootstrapper::class,
|
||||
],
|
||||
'tenancy.filesystem.root_override.public' => '%storage_path%/app/public/',
|
||||
'tenancy.filesystem.url_override.public' => 'public-%tenant_id%'
|
||||
]);
|
||||
|
||||
$tenant1 = Tenant::create();
|
||||
$tenant2 = Tenant::create();
|
||||
|
||||
pest()->artisan('tenants:link');
|
||||
|
||||
// First tenant
|
||||
tenancy()->initialize($tenant1);
|
||||
Storage::disk('public')->put($tenantFileName = 'tenant1.txt', $tenantKey = $tenant1->getTenantKey());
|
||||
|
||||
$url = Storage::disk('public')->url($tenantFileName);
|
||||
$tenantDiskName = Str::of(config('tenancy.filesystem.url_override.public'))->replace('%tenant_id%', $tenantKey);
|
||||
$hostname = Str::of($url)->before($tenantDiskName);
|
||||
$parsedUrl = Str::of($url)->after($hostname);
|
||||
|
||||
expect(file_get_contents(public_path($parsedUrl)))->toBe($tenantKey);
|
||||
|
||||
// Second tenant
|
||||
tenancy()->initialize($tenant2);
|
||||
Storage::disk('public')->put($tenantFileName = 'tenant2.txt', $tenantKey = $tenant2->getTenantKey());
|
||||
|
||||
$url = Storage::disk('public')->url($tenantFileName);
|
||||
$tenantDiskName = Str::of(config('tenancy.filesystem.url_override.public'))->replace('%tenant_id%', $tenantKey);
|
||||
$hostname = Str::of($url)->before($tenantDiskName);
|
||||
$parsedUrl = Str::of($url)->after($hostname);
|
||||
|
||||
expect(file_get_contents(public_path($parsedUrl)))->toBe($tenantKey);
|
||||
});
|
||||
|
||||
test('create and delete storage symlinks jobs work', function() {
|
||||
Event::listen(
|
||||
TenantCreated::class,
|
||||
JobPipeline::make([CreateStorageSymlinks::class])->send(function (TenantCreated $event) {
|
||||
return $event->tenant;
|
||||
})->toListener()
|
||||
);
|
||||
|
||||
Event::listen(
|
||||
TenantDeleted::class,
|
||||
JobPipeline::make([RemoveStorageSymlinks::class])->send(function (TenantDeleted $event) {
|
||||
return $event->tenant;
|
||||
})->toListener()
|
||||
);
|
||||
|
||||
config([
|
||||
'tenancy.bootstrappers' => [
|
||||
FilesystemTenancyBootstrapper::class,
|
||||
],
|
||||
'tenancy.filesystem.suffix_base' => 'tenant-',
|
||||
'tenancy.filesystem.root_override.public' => '%storage_path%/app/public/',
|
||||
'tenancy.filesystem.url_override.public' => 'public-%tenant_id%'
|
||||
]);
|
||||
|
||||
/** @var Tenant $tenant */
|
||||
$tenant = Tenant::create();
|
||||
|
||||
tenancy()->initialize($tenant);
|
||||
|
||||
$tenantKey = $tenant->getTenantKey();
|
||||
|
||||
$this->assertDirectoryExists(storage_path("app/public"));
|
||||
$this->assertEquals(storage_path("app/public/"), readlink(public_path("public-$tenantKey")));
|
||||
|
||||
$tenant->delete();
|
||||
|
||||
$this->assertDirectoryDoesNotExist(public_path("public-$tenantKey"));
|
||||
});
|
||||
|
||||
test('local storage public urls are generated correctly', function() {
|
||||
Event::listen(DeletingTenant::class, DeleteTenantStorage::class);
|
||||
|
||||
tenancy()->initialize(Tenant::create());
|
||||
$tenantStoragePath = storage_path();
|
||||
|
||||
Storage::fake('test');
|
||||
|
||||
expect(File::isDirectory($tenantStoragePath))->toBeTrue();
|
||||
|
||||
Storage::put('test.txt', 'testing file');
|
||||
|
||||
tenant()->delete();
|
||||
|
||||
expect(File::isDirectory($tenantStoragePath))->toBeFalse();
|
||||
});
|
||||
|
||||
function getDiskPrefix(string $disk): string
|
||||
{
|
||||
/** @var FilesystemAdapter $disk */
|
||||
|
|
@ -194,14 +336,14 @@ function getDiskPrefix(string $disk): string
|
|||
return $adapter->getPathPrefix();
|
||||
}
|
||||
|
||||
$prefixer = (new ReflectionObject($adapter))->getProperty('prefixer');
|
||||
$prefixer->setAccessible(true);
|
||||
$prefixer = (new ReflectionObject($adapter))->getProperty('prefixer');
|
||||
$prefixer->setAccessible(true);
|
||||
|
||||
// reflection -> instance
|
||||
$prefixer = $prefixer->getValue($adapter);
|
||||
// reflection -> instance
|
||||
$prefixer = $prefixer->getValue($adapter);
|
||||
|
||||
$prefix = (new ReflectionProperty($prefixer, 'prefix'));
|
||||
$prefix->setAccessible(true);
|
||||
$prefix = (new ReflectionProperty($prefixer, 'prefix'));
|
||||
$prefix->setAccessible(true);
|
||||
|
||||
return $prefix->getValue($prefixer);
|
||||
return $prefix->getValue($prefixer);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,17 +2,19 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Stancl\Tenancy\Tests\Etc\User;
|
||||
use Stancl\JobPipeline\JobPipeline;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Stancl\Tenancy\Events\TenancyEnded;
|
||||
use Stancl\Tenancy\Jobs\CreateDatabase;
|
||||
use Illuminate\Database\DatabaseManager;
|
||||
use Stancl\Tenancy\Events\TenantCreated;
|
||||
use Stancl\Tenancy\Tests\Etc\TestSeeder;
|
||||
use Stancl\Tenancy\Tests\Etc\ExampleSeeder;
|
||||
use Stancl\Tenancy\Events\TenancyInitialized;
|
||||
use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
||||
|
|
@ -28,6 +30,15 @@ beforeEach(function () {
|
|||
DatabaseTenancyBootstrapper::class,
|
||||
]]);
|
||||
|
||||
config([
|
||||
'tenancy.bootstrappers' => [
|
||||
DatabaseTenancyBootstrapper::class,
|
||||
],
|
||||
'tenancy.filesystem.suffix_base' => 'tenant-',
|
||||
'tenancy.filesystem.root_override.public' => '%storage_path%/app/public/',
|
||||
'tenancy.filesystem.url_override.public' => 'public-%tenant_id%'
|
||||
]);
|
||||
|
||||
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
|
||||
});
|
||||
|
|
@ -42,9 +53,9 @@ afterEach(function () {
|
|||
test('migrate command doesnt change the db connection', function () {
|
||||
expect(Schema::hasTable('users'))->toBeFalse();
|
||||
|
||||
$old_connection_name = app(\Illuminate\Database\DatabaseManager::class)->connection()->getName();
|
||||
$old_connection_name = app(DatabaseManager::class)->connection()->getName();
|
||||
Artisan::call('tenants:migrate');
|
||||
$new_connection_name = app(\Illuminate\Database\DatabaseManager::class)->connection()->getName();
|
||||
$new_connection_name = app(DatabaseManager::class)->connection()->getName();
|
||||
|
||||
expect(Schema::hasTable('users'))->toBeFalse();
|
||||
expect($new_connection_name)->toEqual($old_connection_name);
|
||||
|
|
@ -131,8 +142,22 @@ test('rollback command works', function () {
|
|||
expect(Schema::hasTable('users'))->toBeFalse();
|
||||
});
|
||||
|
||||
// Incomplete test
|
||||
test('seed command works');
|
||||
test('seed command works', function (){
|
||||
$tenant = Tenant::create();
|
||||
Artisan::call('tenants:migrate');
|
||||
|
||||
$tenant->run(function (){
|
||||
expect(DB::table('users')->count())->toBe(0);
|
||||
});
|
||||
|
||||
Artisan::call('tenants:seed', ['--class' => TestSeeder::class]);
|
||||
|
||||
$tenant->run(function (){
|
||||
$user = DB::table('users');
|
||||
expect($user->count())->toBe(1)
|
||||
->and($user->first()->email)->toBe('seeded@user');
|
||||
});
|
||||
});
|
||||
|
||||
test('database connection is switched to default', function () {
|
||||
databaseConnectionSwitchedToDefault();
|
||||
|
|
@ -195,6 +220,43 @@ test('run command with array of tenants works', function () {
|
|||
->expectsOutput('Tenant: ' . $tenantId2);
|
||||
});
|
||||
|
||||
test('link command works', function() {
|
||||
$tenantId1 = Tenant::create()->getTenantKey();
|
||||
$tenantId2 = Tenant::create()->getTenantKey();
|
||||
pest()->artisan('tenants:link');
|
||||
|
||||
$this->assertDirectoryExists(storage_path("tenant-$tenantId1/app/public"));
|
||||
$this->assertEquals(storage_path("tenant-$tenantId1/app/public/"), readlink(public_path("public-$tenantId1")));
|
||||
|
||||
$this->assertDirectoryExists(storage_path("tenant-$tenantId2/app/public"));
|
||||
$this->assertEquals(storage_path("tenant-$tenantId2/app/public/"), readlink(public_path("public-$tenantId2")));
|
||||
|
||||
pest()->artisan('tenants:link', [
|
||||
'--remove' => true,
|
||||
]);
|
||||
|
||||
$this->assertDirectoryDoesNotExist(public_path("public-$tenantId1"));
|
||||
$this->assertDirectoryDoesNotExist(public_path("public-$tenantId2"));
|
||||
});
|
||||
|
||||
test('link command works with a specified tenant', function() {
|
||||
$tenantKey = Tenant::create()->getTenantKey();
|
||||
|
||||
pest()->artisan('tenants:link', [
|
||||
'--tenants' => [$tenantKey],
|
||||
]);
|
||||
|
||||
$this->assertDirectoryExists(storage_path("tenant-$tenantKey/app/public"));
|
||||
$this->assertEquals(storage_path("tenant-$tenantKey/app/public/"), readlink(public_path("public-$tenantKey")));
|
||||
|
||||
pest()->artisan('tenants:link', [
|
||||
'--remove' => true,
|
||||
'--tenants' => [$tenantKey],
|
||||
]);
|
||||
|
||||
$this->assertDirectoryDoesNotExist(public_path("public-$tenantKey"));
|
||||
});
|
||||
|
||||
test('run command works when sub command asks questions and accepts arguments', function () {
|
||||
$tenant = Tenant::create();
|
||||
$id = $tenant->getTenantKey();
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ test('secondary models are not scoped to the current tenant when accessed direct
|
|||
expect(Comment::count())->toBe(2);
|
||||
});
|
||||
|
||||
test('secondary models a r e scoped to the current tenant when accessed directly and parent relationship traitis used', function () {
|
||||
test('secondary models ARE scoped to the current tenant when accessed directly and parent relationship trait is used', function () {
|
||||
$acme = Tenant::create([
|
||||
'id' => 'acme',
|
||||
]);
|
||||
|
|
@ -207,13 +207,13 @@ test('the model returned by the tenant helper has unique and exists validation r
|
|||
$uniqueFails = Validator::make($data, [
|
||||
'slug' => 'unique:posts',
|
||||
])->fails();
|
||||
$existsFails = Validator::make($data, [
|
||||
$existsPass = Validator::make($data, [
|
||||
'slug' => 'exists:posts',
|
||||
])->fails();
|
||||
])->passes();
|
||||
|
||||
// Assert that 'unique' and 'exists' aren't scoped by default
|
||||
// pest()->assertFalse($uniqueFails); // todo get these two assertions to pass. for some reason, the validator is passing for both 'unique' and 'exists'
|
||||
// pest()->assertTrue($existsFails); // todo get these two assertions to pass. for some reason, the validator is passing for both 'unique' and 'exists'
|
||||
expect($uniqueFails)->toBeTrue(); // Expect unique rule failed to pass because slug 'foo' already exists
|
||||
expect($existsPass)->toBeTrue(); // Expect exists rule pass because slug 'foo' exists
|
||||
|
||||
$uniqueFails = Validator::make($data, [
|
||||
'slug' => tenant()->unique('posts'),
|
||||
|
|
|
|||
|
|
@ -225,7 +225,25 @@ test('tenant database can be created on a foreign server', function () {
|
|||
});
|
||||
|
||||
test('path used by sqlite manager can be customized', function () {
|
||||
pest()->markTestIncomplete();
|
||||
Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
|
||||
return $event->tenant;
|
||||
})->toListener());
|
||||
|
||||
// Set custom path for SQLite file
|
||||
SQLiteDatabaseManager::$path = $customPath = database_path('custom_' . Str::random(8));
|
||||
|
||||
if (! is_dir($customPath)) {
|
||||
// Create custom directory
|
||||
mkdir($customPath);
|
||||
}
|
||||
|
||||
$name = Str::random(8). '.sqlite';
|
||||
Tenant::create([
|
||||
'tenancy_db_name' => $name,
|
||||
'tenancy_db_connection' => 'sqlite',
|
||||
]);
|
||||
|
||||
expect(file_exists( $customPath . '/' . $name))->toBeTrue();
|
||||
});
|
||||
|
||||
// Datasets
|
||||
|
|
|
|||
|
|
@ -4,8 +4,15 @@ declare(strict_types=1);
|
|||
|
||||
namespace Stancl\Tenancy\Tests;
|
||||
|
||||
use Dotenv\Dotenv;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use PDO;
|
||||
use Stancl\Tenancy\Bootstrappers\BatchTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\RedisTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Facades\GlobalCache;
|
||||
use Stancl\Tenancy\Facades\Tenancy;
|
||||
use Stancl\Tenancy\TenancyServiceProvider;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
|
||||
abstract class TestCase extends \Orchestra\Testbench\TestCase
|
||||
|
|
@ -42,13 +49,13 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
|
|||
/**
|
||||
* Define environment setup.
|
||||
*
|
||||
* @param \Illuminate\Foundation\Application $app
|
||||
* @param Application $app
|
||||
* @return void
|
||||
*/
|
||||
protected function getEnvironmentSetUp($app)
|
||||
{
|
||||
if (file_exists(__DIR__ . '/../.env')) {
|
||||
\Dotenv\Dotenv::createImmutable(__DIR__ . '/..')->load();
|
||||
Dotenv::createImmutable(__DIR__ . '/..')->load();
|
||||
}
|
||||
|
||||
$app['config']->set([
|
||||
|
|
@ -96,7 +103,7 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
|
|||
'--realpath' => true,
|
||||
'--force' => true,
|
||||
],
|
||||
'tenancy.bootstrappers.redis' => \Stancl\Tenancy\Bootstrappers\RedisTenancyBootstrapper::class, // todo0 change this to []? two tests in TenantDatabaseManagerTest are failing with that
|
||||
'tenancy.bootstrappers.redis' => RedisTenancyBootstrapper::class, // todo0 change this to []? two tests in TenantDatabaseManagerTest are failing with that
|
||||
'queue.connections.central' => [
|
||||
'driver' => 'sync',
|
||||
'central' => true,
|
||||
|
|
@ -105,28 +112,28 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
|
|||
'tenancy.tenant_model' => Tenant::class, // Use test tenant w/ DBs & domains
|
||||
]);
|
||||
|
||||
$app->singleton(\Stancl\Tenancy\Bootstrappers\RedisTenancyBootstrapper::class);
|
||||
$app->singleton(RedisTenancyBootstrapper::class); // todo (Samuel) use proper approach eg config for singleton registration
|
||||
}
|
||||
|
||||
protected function getPackageProviders($app)
|
||||
{
|
||||
return [
|
||||
\Stancl\Tenancy\TenancyServiceProvider::class,
|
||||
TenancyServiceProvider::class,
|
||||
];
|
||||
}
|
||||
|
||||
protected function getPackageAliases($app)
|
||||
{
|
||||
return [
|
||||
'Tenancy' => \Stancl\Tenancy\Facades\Tenancy::class,
|
||||
'GlobalCache' => \Stancl\Tenancy\Facades\GlobalCache::class,
|
||||
'Tenancy' => Tenancy::class,
|
||||
'GlobalCache' => GlobalCache::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve application HTTP Kernel implementation.
|
||||
*
|
||||
* @param \Illuminate\Foundation\Application $app
|
||||
* @param Application $app
|
||||
* @return void
|
||||
*/
|
||||
protected function resolveApplicationHttpKernel($app)
|
||||
|
|
@ -137,7 +144,7 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
|
|||
/**
|
||||
* Resolve application Console Kernel implementation.
|
||||
*
|
||||
* @param \Illuminate\Foundation\Application $app
|
||||
* @param Application $app
|
||||
* @return void
|
||||
*/
|
||||
protected function resolveApplicationConsoleKernel($app)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue