mirror of
https://github.com/archtechx/tenancy.git
synced 2026-05-06 17:04:04 +00:00
Merge branch 'master' into boilerplate-dev
This commit is contained in:
commit
5d047089ea
8 changed files with 95 additions and 7 deletions
|
|
@ -32,7 +32,7 @@
|
|||
"league/flysystem-aws-s3-v3": "^3.12.2",
|
||||
"doctrine/dbal": "^3.6.0",
|
||||
"spatie/valuestore": "^1.2.5",
|
||||
"pestphp/pest": "^3.0",
|
||||
"pestphp/pest": "^4.0",
|
||||
"larastan/larastan": "^3.0",
|
||||
"league/flysystem-path-prefixing": "^3.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Stancl\Tenancy\Bootstrappers;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Session\FileSessionHandler;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
|
@ -75,8 +76,13 @@ class FilesystemTenancyBootstrapper implements TenancyBootstrapper
|
|||
: $this->originalStoragePath . '/framework/cache';
|
||||
|
||||
if (! is_dir($path)) {
|
||||
// Create tenant framework/cache directory if it does not exist
|
||||
mkdir($path, 0750, true);
|
||||
// Create tenant framework/cache directory if it does not exist.
|
||||
// We ignore errors due to TOCTOU race conditions, instead we check for success below.
|
||||
@mkdir($path, 0750, true);
|
||||
|
||||
if (! is_dir($path)) {
|
||||
throw new Exception("Unable to create tenant storage directory [{$path}].");
|
||||
}
|
||||
}
|
||||
|
||||
if ($suffix === false) {
|
||||
|
|
@ -222,8 +228,13 @@ class FilesystemTenancyBootstrapper implements TenancyBootstrapper
|
|||
: $this->originalStoragePath . '/framework/sessions';
|
||||
|
||||
if (! is_dir($path)) {
|
||||
// Create tenant framework/sessions directory if it does not exist
|
||||
mkdir($path, 0750, true);
|
||||
// Create tenant framework/sessions directory if it does not exist.
|
||||
// We ignore errors due to TOCTOU race conditions, instead we check for success below.
|
||||
@mkdir($path, 0750, true);
|
||||
|
||||
if (! is_dir($path)) {
|
||||
throw new Exception("Unable to create tenant session directory [{$path}].");
|
||||
}
|
||||
}
|
||||
|
||||
$this->app['config']['session.files'] = $path;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use Illuminate\Database\Eloquent\Builder;
|
|||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Scope;
|
||||
|
||||
/** @implements Scope<Model> */
|
||||
class PendingScope implements Scope
|
||||
{
|
||||
/**
|
||||
|
|
@ -57,8 +58,10 @@ class PendingScope implements Scope
|
|||
{
|
||||
$builder->macro('withoutPending', function (Builder $builder) {
|
||||
$builder->withoutGlobalScope(static::class)
|
||||
->whereNull($builder->getModel()->getColumnForQuery('pending_since'))
|
||||
->orWhereNull($builder->getModel()->getDataColumn());
|
||||
->where(function (Builder $query) {
|
||||
$query->whereNull($query->getModel()->getColumnForQuery('pending_since'))
|
||||
->orWhereNull($query->getModel()->getDataColumn());
|
||||
});
|
||||
|
||||
return $builder;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use Illuminate\Database\Eloquent\Builder;
|
|||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Scope;
|
||||
|
||||
/** @implements Scope<Model> */
|
||||
class ParentModelScope implements Scope
|
||||
{
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Model;
|
|||
use Illuminate\Database\Eloquent\Scope;
|
||||
use Stancl\Tenancy\Tenancy;
|
||||
|
||||
/** @implements Scope<Model> */
|
||||
class TenantScope implements Scope
|
||||
{
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -152,6 +152,26 @@ class Tenancy
|
|||
$this->initialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* End tenancy and initialize it again for the current tenant.
|
||||
*
|
||||
* This can be helpful when changing "dependencies" of bootstrappers such as
|
||||
* attributes of the current tenant that are only read once, during bootstrap().
|
||||
*
|
||||
* If tenancy is not initialized, this method is a no-op.
|
||||
*/
|
||||
public function reinitialize(): void
|
||||
{
|
||||
if ($this->tenant === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tenant = $this->tenant;
|
||||
$this->end();
|
||||
|
||||
$this->initialize($tenant);
|
||||
}
|
||||
|
||||
/** @return TenancyBootstrapper[] */
|
||||
public function getBootstrappers(): array
|
||||
{
|
||||
|
|
|
|||
|
|
@ -103,6 +103,33 @@ test('central helper doesnt change tenancy state when called in central context'
|
|||
expect(tenant())->toBeNull();
|
||||
});
|
||||
|
||||
test('reinitialize method does nothing in the central context', function () {
|
||||
expect(tenancy()->initialized)->toBe(false);
|
||||
expect(fn () => tenancy()->reinitialize())->not()->toThrow(\Throwable::class);
|
||||
expect(tenancy()->initialized)->toBe(false);
|
||||
});
|
||||
|
||||
test('reinitialize method runs bootstrappers again for the current tenant', function () {
|
||||
config(['tenancy.bootstrappers' => [
|
||||
ReinitBootstrapper::class,
|
||||
]]);
|
||||
|
||||
tenancy()->initialize($tenant = Tenant::create(['reinit_bootstrapper_key' => 'foo']));
|
||||
|
||||
expect(tenant()->getKey())->toBe($tenant->getKey());
|
||||
expect(app('tenancy_reinit_bootstrapper_key'))->toBe('foo');
|
||||
|
||||
$tenant->update(['reinit_bootstrapper_key' => 'bar']);
|
||||
|
||||
// Unchanged until we reinitialize...
|
||||
expect(app('tenancy_reinit_bootstrapper_key'))->toBe('foo');
|
||||
|
||||
tenancy()->reinitialize();
|
||||
|
||||
expect(tenant()->getKey())->toBe($tenant->getKey());
|
||||
expect(app('tenancy_reinit_bootstrapper_key'))->toBe('bar');
|
||||
});
|
||||
|
||||
class MyBootstrapper implements TenancyBootstrapper
|
||||
{
|
||||
public function bootstrap(\Stancl\Tenancy\Contracts\Tenant $tenant): void
|
||||
|
|
@ -115,3 +142,16 @@ class MyBootstrapper implements TenancyBootstrapper
|
|||
app()->instance('tenancy_ended', true);
|
||||
}
|
||||
}
|
||||
|
||||
class ReinitBootstrapper implements TenancyBootstrapper
|
||||
{
|
||||
public function bootstrap(\Stancl\Tenancy\Contracts\Tenant $tenant): void
|
||||
{
|
||||
app()->instance('tenancy_reinit_bootstrapper_key', $tenant->getAttribute('reinit_bootstrapper_key'));
|
||||
}
|
||||
|
||||
public function revert(): void
|
||||
{
|
||||
app()->instance('tenancy_reinit_bootstrapper_key', null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,6 +111,18 @@ test('a new tenant gets created while pulling a pending tenant if the pending po
|
|||
expect(Tenant::withPending()->get()->count())->toBe(1); // All tenants
|
||||
});
|
||||
|
||||
test('withoutPending chained with where clauses returns correct results', function () {
|
||||
$tenant = Tenant::create();
|
||||
$pendingTenant = Tenant::createPending();
|
||||
|
||||
// The query returned the correct tenant
|
||||
expect(Tenant::withoutPending()->where('id', $tenant->id)->first()->id)->toBe($tenant->id);
|
||||
// No tenant with this ID exists, the query returns null
|
||||
expect(Tenant::withoutPending()->where('id', Str::random(8) . 'nonexistent-id')->first())->toBeNull();
|
||||
// withoutPending() correctly excludes the pending tenant from the query
|
||||
expect(Tenant::withoutPending()->where('id', $pendingTenant->id)->first())->toBeNull();
|
||||
});
|
||||
|
||||
test('pending tenants are included in all queries based on the include_in_queries config', function () {
|
||||
Tenant::createPending();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue