diff --git a/src/Database/Eloquent/Observers/BelongsToManyTenantsObserver.php b/src/Database/Eloquent/Observers/BelongsToManyTenantsObserver.php index a293084..d65f4b0 100644 --- a/src/Database/Eloquent/Observers/BelongsToManyTenantsObserver.php +++ b/src/Database/Eloquent/Observers/BelongsToManyTenantsObserver.php @@ -10,6 +10,7 @@ use Sprout\Exceptions\TenantMismatch; use Sprout\Exceptions\TenantMissing; use Sprout\TenancyOptions; +use function Sprout\sprout; /** * Belongs to Many Tenants Observer @@ -151,6 +152,10 @@ private function passesInitialChecks(Model $model, Tenancy $tenancy, BelongsToMa */ public function created(Model $model): void { + if (! sprout()->withinContext()) { + return; + } + /** * @var \Illuminate\Database\Eloquent\Relations\BelongsToMany $relation * @phpstan-ignore-next-line @@ -199,6 +204,10 @@ public function created(Model $model): void */ public function retrieved(Model $model): void { + if (! sprout()->withinContext()) { + return; + } + /** * @var \Illuminate\Database\Eloquent\Relations\BelongsToMany $relation * @phpstan-ignore-next-line diff --git a/src/Database/Eloquent/Observers/BelongsToTenantObserver.php b/src/Database/Eloquent/Observers/BelongsToTenantObserver.php index 47a1fd3..e8224ae 100644 --- a/src/Database/Eloquent/Observers/BelongsToTenantObserver.php +++ b/src/Database/Eloquent/Observers/BelongsToTenantObserver.php @@ -10,6 +10,7 @@ use Sprout\Exceptions\TenantMismatch; use Sprout\Exceptions\TenantMissing; use Sprout\TenancyOptions; +use function Sprout\sprout; /** * Belongs to Tenant Observer @@ -134,6 +135,10 @@ private function passesInitialChecks(Model $model, Tenancy $tenancy, BelongsTo $ */ public function creating(Model $model): bool { + if (! sprout()->withinContext()) { + return true; + } + /** * @var \Illuminate\Database\Eloquent\Relations\BelongsTo $relation * @phpstan-ignore-next-line @@ -175,6 +180,10 @@ public function creating(Model $model): bool */ public function retrieved(Model $model): void { + if (! sprout()->withinContext()) { + return; + } + /** * @var \Illuminate\Database\Eloquent\Relations\BelongsTo $relation * @phpstan-ignore-next-line diff --git a/src/Database/Eloquent/Scopes/BelongsToManyTenantsScope.php b/src/Database/Eloquent/Scopes/BelongsToManyTenantsScope.php index 3d23f82..0422c43 100644 --- a/src/Database/Eloquent/Scopes/BelongsToManyTenantsScope.php +++ b/src/Database/Eloquent/Scopes/BelongsToManyTenantsScope.php @@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Sprout\Exceptions\TenantMissing; +use function Sprout\sprout; /** * Belongs to many Tenants Scope @@ -38,6 +39,10 @@ final class BelongsToManyTenantsScope extends TenantChildScope */ public function apply(Builder $builder, Model $model): void { + if (! sprout()->withinContext()) { + return; + } + /** @phpstan-ignore-next-line */ $tenancy = $model->getTenancy(); diff --git a/src/Database/Eloquent/Scopes/BelongsToTenantScope.php b/src/Database/Eloquent/Scopes/BelongsToTenantScope.php index c166ba1..b3ec890 100644 --- a/src/Database/Eloquent/Scopes/BelongsToTenantScope.php +++ b/src/Database/Eloquent/Scopes/BelongsToTenantScope.php @@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Sprout\Exceptions\TenantMissing; +use function Sprout\sprout; /** * Belongs to Tenant Scope @@ -38,6 +39,10 @@ final class BelongsToTenantScope extends TenantChildScope */ public function apply(Builder $builder, Model $model): void { + if (! sprout()->withinContext()) { + return; + } + /** @phpstan-ignore-next-line */ $tenancy = $model->getTenancy(); diff --git a/src/Database/Eloquent/Scopes/TenantChildScope.php b/src/Database/Eloquent/Scopes/TenantChildScope.php index b183770..75a6bb6 100644 --- a/src/Database/Eloquent/Scopes/TenantChildScope.php +++ b/src/Database/Eloquent/Scopes/TenantChildScope.php @@ -19,6 +19,11 @@ */ abstract class TenantChildScope implements Scope { + /** + * @var array + */ + protected array $extensions = []; + /** * Extend the query builder with the necessary macros * @@ -34,5 +39,9 @@ public function extend(Builder $builder): void /** @phpstan-ignore-next-line */ return $builder->withoutGlobalScope($this); }); + + foreach ($this->extensions as $macro => $method) { + $builder->macro($macro, $this->$method(...)); + } } } diff --git a/tests/Database/Eloquent/BelongsToManyTenantsTest.php b/tests/Database/Eloquent/BelongsToManyTenantsTest.php index 2b39c40..2c4891a 100644 --- a/tests/Database/Eloquent/BelongsToManyTenantsTest.php +++ b/tests/Database/Eloquent/BelongsToManyTenantsTest.php @@ -23,6 +23,7 @@ use Workbench\App\Models\TenantChildren; use Workbench\App\Models\TenantChildrenOptional; use Workbench\App\Models\TenantModel; +use function Sprout\sprout; #[Group('database'), Group('eloquent')] class BelongsToManyTenantsTest extends TestCase @@ -73,7 +74,11 @@ public function automaticallyAssociatesWithTenantWhenCreating(): void { $tenant = TenantModel::factory()->create(); - app(TenancyManager::class)->get()->setTenant($tenant); + $tenancy = app(TenancyManager::class)->get(); + + sprout()->setCurrentTenancy($tenancy); + + $tenancy->setTenant($tenant); $child = TenantChildren::factory()->create(); @@ -82,9 +87,21 @@ public function automaticallyAssociatesWithTenantWhenCreating(): void $this->assertNotNull($child->tenants->first(fn (Model $model) => $model->is($tenant))); } + #[Test] + public function doesNotAutomaticallyAssociateWithTenantWhenCreatingWhenOutsideMultitenantedContext(): void + { + $child = TenantChildren::factory()->create(); + + $this->assertTrue($child->exists); + $this->assertFalse($child->relationLoaded('tenants')); + $this->assertTrue($child->tenants->isEmpty()); + } + #[Test] public function throwsAnExceptionIfTheresNoTenantAndTheTenantIsNotOptionalWhenCreating(): void { + sprout()->setCurrentTenancy(app(TenancyManager::class)->get()); + $this->expectException(TenantMissing::class); $this->expectExceptionMessage( 'There is no current tenant for tenancy [tenants]' @@ -93,6 +110,15 @@ public function throwsAnExceptionIfTheresNoTenantAndTheTenantIsNotOptionalWhenCr TenantChildren::factory()->create(); } + #[Test] + public function doesNotThrowAnExceptionIfTheresNoTenantAndTheTenantIsNotOptionalWhenCreatingWhenOutsideMultitenantedContext(): void + { + $model = TenantChildren::factory()->create(); + + $this->assertNotNull($model); + $this->assertTrue($model->exists); + } + #[Test] public function doesNothingIfTheresNoTenantAndTheTenantIsOptionalWithInterfaceWhenCreating(): void { @@ -142,7 +168,11 @@ public function automaticallyPopulateTheTenantRelationWhenHydrating(): void { $tenant = TenantModel::factory()->create(); - app(TenancyManager::class)->get()->setTenant($tenant); + $tenancy = app(TenancyManager::class)->get(); + + sprout()->setCurrentTenancy($tenancy); + + $tenancy->setTenant($tenant); $child = TenantChildren::query()->find(TenantChildren::factory()->create()->getKey()); @@ -151,6 +181,16 @@ public function automaticallyPopulateTheTenantRelationWhenHydrating(): void $this->assertNotNull($child->getRelation('tenants')->first(fn (Model $model) => $model->is($tenant))); } + #[Test] + public function doesNotAutomaticallyPopulateTheTenantRelationWhenHydratingWhenOutsideMultitenantedContext(): void + { + + $child = TenantChildren::query()->find(TenantChildren::factory()->create()->getKey()); + + $this->assertTrue($child->exists); + $this->assertFalse($child->relationLoaded('tenants')); + } + #[Test] public function throwsAnExceptionIfTheresNoTenantAndTheTenantIsNotOptionalWhenHydrating(): void { @@ -158,6 +198,8 @@ public function throwsAnExceptionIfTheresNoTenantAndTheTenantIsNotOptionalWhenHy $tenancy = app(TenancyManager::class)->get(); + sprout()->setCurrentTenancy($tenancy); + $tenancy->setTenant($tenant); $child = TenantChildren::factory()->create(); @@ -223,7 +265,10 @@ public function throwsAnExceptionIfTheTenantIsAlreadySetOnTheModelAndItIsDiffere $tenancy = app(TenancyManager::class)->get(); + sprout()->setCurrentTenancy($tenancy); + $tenancy->setTenant($tenant); + $tenancy->addOption(TenancyOptions::throwIfNotRelated()); $child = TenantChildren::factory()->create(); @@ -248,7 +293,10 @@ public function doesNotThrowAnExceptionForTenantMismatchIfNotSetToWhenHydrating( $tenancy = app(TenancyManager::class)->get(); + sprout()->setCurrentTenancy($tenancy); + $tenancy->setTenant($tenant); + $tenancy->removeOption(TenancyOptions::throwIfNotRelated()); $child = TenantChildren::factory()->create(); @@ -270,6 +318,8 @@ public function onlyReturnsModelsForTheCurrentTenant(): void $tenancy = app(TenancyManager::class)->get(); + sprout()->setCurrentTenancy($tenancy); + $tenancy->setTenant($tenant); $original = TenantChildren::factory()->create(); diff --git a/tests/Database/Eloquent/BelongsToTenantTest.php b/tests/Database/Eloquent/BelongsToTenantTest.php index 2b187da..7bb857e 100644 --- a/tests/Database/Eloquent/BelongsToTenantTest.php +++ b/tests/Database/Eloquent/BelongsToTenantTest.php @@ -21,6 +21,7 @@ use Workbench\App\Models\TenantChild; use Workbench\App\Models\TenantChildOptional; use Workbench\App\Models\TenantModel; +use function Sprout\sprout; #[Group('database'), Group('eloquent')] class BelongsToTenantTest extends TestCase @@ -71,7 +72,11 @@ public function automaticallyAssociatesWithTenantWhenCreating(): void { $tenant = TenantModel::factory()->create(); - app(TenancyManager::class)->get()->setTenant($tenant); + $tenancy = app(TenancyManager::class)->get(); + + sprout()->setCurrentTenancy($tenancy); + + $tenancy->setTenant($tenant); $child = TenantChild::factory()->create(); @@ -80,9 +85,21 @@ public function automaticallyAssociatesWithTenantWhenCreating(): void $this->assertTrue($child->tenant->is($tenant)); } + #[Test] + public function doesNotAutomaticallyAssociateWithTenantWhenCreatingWhenOutsideMultitenantedContext(): void + { + $child = TenantChild::factory()->create(); + + $this->assertTrue($child->exists); + $this->assertFalse($child->relationLoaded('tenant')); + $this->assertNull($child->tenant); + } + #[Test] public function throwsAnExceptionIfTheresNoTenantAndTheTenantIsNotOptionalWhenCreating(): void { + sprout()->setCurrentTenancy(app(TenancyManager::class)->get()); + $this->expectException(TenantMissing::class); $this->expectExceptionMessage( 'There is no current tenant for tenancy [tenants]' @@ -91,6 +108,16 @@ public function throwsAnExceptionIfTheresNoTenantAndTheTenantIsNotOptionalWhenCr TenantChild::factory()->create(); } + #[Test] + public function doesNotThrowAnExceptionIfTheresNoTenantAndTheTenantIsNotOptionalWhenCreatingWhenOutsideMultitenantedContext(): void + { + $child = TenantChild::factory()->create(); + + $this->assertTrue($child->exists); + $this->assertFalse($child->relationLoaded('tenant')); + $this->assertNull($child->tenant); + } + #[Test] public function doesNothingIfTheresNoTenantAndTheTenantIsOptionalWithInterfaceWhenCreating(): void { @@ -138,7 +165,10 @@ public function throwsAnExceptionIfTheTenantIsAlreadySetOnTheModelAndItIsDiffere $tenancy = app(TenancyManager::class)->get(); + sprout()->setCurrentTenancy($tenancy); + $tenancy->setTenant($tenant); + $tenancy->addOption(TenancyOptions::throwIfNotRelated()); $this->expectException(TenantMismatch::class); @@ -174,7 +204,11 @@ public function automaticallyPopulateTheTenantRelationWhenHydrating(): void { $tenant = TenantModel::factory()->create(); - app(TenancyManager::class)->get()->setTenant($tenant); + $tenancy = app(TenancyManager::class)->get(); + + sprout()->setCurrentTenancy($tenancy); + + $tenancy->setTenant($tenant); $child = TenantChild::query()->find(TenantChild::factory()->create()->getKey()); @@ -184,6 +218,15 @@ public function automaticallyPopulateTheTenantRelationWhenHydrating(): void $this->assertTrue($child->getRelation('tenant')->is($tenant)); } + #[Test] + public function doesNotAutomaticallyPopulateTheTenantRelationWhenHydratingWhenOutsideMultitenantedCContext(): void + { + $child = TenantChild::query()->find(TenantChild::factory()->create()->getKey()); + + $this->assertTrue($child->exists); + $this->assertFalse($child->relationLoaded('tenant')); + } + #[Test] public function doNotHydrateWhenHydrateTenantRelationIsMissing(): void { @@ -208,6 +251,8 @@ public function throwsAnExceptionIfTheresNoTenantAndTheTenantIsNotOptionalWhenHy $tenancy = app(TenancyManager::class)->get(); + sprout()->setCurrentTenancy($tenancy); + $tenancy->setTenant($tenant); $child = TenantChild::factory()->create(); @@ -273,7 +318,10 @@ public function throwsAnExceptionIfTheTenantIsAlreadySetOnTheModelAndItIsDiffere $tenancy = app(TenancyManager::class)->get(); + sprout()->setCurrentTenancy($tenancy); + $tenancy->setTenant($tenant); + $tenancy->addOption(TenancyOptions::throwIfNotRelated()); $child = TenantChild::factory()->create(); @@ -298,7 +346,10 @@ public function doesNotThrowAnExceptionForTenantMismatchIfNotSetToWhenHydrating( $tenancy = app(TenancyManager::class)->get(); + sprout()->setCurrentTenancy($tenancy); + $tenancy->setTenant($tenant); + $tenancy->removeOption(TenancyOptions::throwIfNotRelated()); $child = TenantChild::factory()->create(); @@ -320,6 +371,8 @@ public function onlyReturnsModelsForTheCurrentTenant(): void $tenancy = app(TenancyManager::class)->get(); + sprout()->setCurrentTenancy($tenancy); + $tenancy->setTenant($tenant); $original = TenantChild::factory()->create();