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

Compare commits

..

4 commits

Author SHA1 Message Date
lukinovec
daa1b907b0 Test that DeleteResourceMapping works with forceDelete() 2025-11-24 15:33:44 +01:00
b6e52035e7
Syncing: move global ID generation logic to an overridable method
Also make all resource syncing-related listener closures static.

Also correct return type for getGlobalIdentifierKey to string|int.
(We intentionally do not support returning null like many other
"get x key" methods would since such a case might break resource
syncing logic. This is also why we use inline getAttribute() in the
creating listener instead of calling the method.)
2025-11-24 02:04:40 +01:00
lukinovec
ade6e4182c Syncing: Add DeleteAllTenantMappings listener 2025-11-22 03:30:30 +01:00
lukinovec
5b15c67d9e Syncing: SyncedResourceDeleted event and DeleteResourceMapping listener
Also move pivot record deletion to that listener and improve tests

The 'tenant pivot records are deleted along with the tenants to which
they belong to' test is failing in this commit -- the listener
for deleting mappings when a *tenant* is deleted is only implemented
in the next commit. The only change done here is to re-add FKs
(necessary for passing *in this commit* in that specific dataset
variant) that were removed from the default test migration as we now
have the the DeleteResourceMapping listener that's enabled by default.
2025-11-22 03:30:30 +01:00
7 changed files with 35 additions and 26 deletions

View file

@ -146,9 +146,7 @@ class TenancyServiceProvider extends ServiceProvider
ResourceSyncing\Events\CentralResourceDetachedFromTenant::class => [ ResourceSyncing\Events\CentralResourceDetachedFromTenant::class => [
ResourceSyncing\Listeners\DeleteResourceInTenant::class, ResourceSyncing\Listeners\DeleteResourceInTenant::class,
], ],
// Fired only when a synced resource is changed in a different DB than the origin DB (to avoid infinite loops)
// Fired only when a synced resource is changed (as a result of syncing)
// in a different DB than DB from which the change originates (to avoid infinite loops)
ResourceSyncing\Events\SyncedResourceSavedInForeignDatabase::class => [], ResourceSyncing\Events\SyncedResourceSavedInForeignDatabase::class => [],
// Storage symlinks // Storage symlinks

View file

@ -13,7 +13,7 @@ class CentralResourceNotAvailableInPivotException extends Exception
parent::__construct( parent::__construct(
'Central resource is not accessible in pivot model. 'Central resource is not accessible in pivot model.
To attach a resource to a tenant, use $centralResource->tenants()->attach($tenant) instead of $tenant->resources()->attach($centralResource) (same for detaching). To attach a resource to a tenant, use $centralResource->tenants()->attach($tenant) instead of $tenant->resources()->attach($centralResource) (same for detaching).
To make this work both ways, you can make your pivot implement PivotWithCentralResource and return the related model in getCentralResourceClass() or extend MorphPivot.' To make this work both ways, you can make your pivot implement PivotWithRelation and return the related model in getRelatedModel() or extend MorphPivot.'
); );
} }
} }

View file

@ -1,11 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\ResourceSyncing;
interface PivotWithCentralResource
{
/** @return class-string<\Illuminate\Database\Eloquent\Model&Syncable> */
public function getCentralResourceClass(): string;
}

View file

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\ResourceSyncing;
use Illuminate\Database\Eloquent\Model;
interface PivotWithRelation
{
/**
* E.g. return $this->users()->getModel().
*/
public function getRelatedModel(): Model;
}

View file

@ -79,9 +79,9 @@ trait TriggerSyncingEvents
*/ */
protected function getResourceClass(): string protected function getResourceClass(): string
{ {
/** @var $this&(Pivot|MorphPivot|((Pivot|MorphPivot)&PivotWithCentralResource)) $this */ /** @var $this&(Pivot|MorphPivot|((Pivot|MorphPivot)&PivotWithRelation)) $this */
if ($this instanceof PivotWithCentralResource) { if ($this instanceof PivotWithRelation) {
return $this->getCentralResourceClass(); return $this->getRelatedModel()::class;
} }
if ($this instanceof MorphPivot) { if ($this instanceof MorphPivot) {

View file

@ -4,13 +4,20 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Tests\Etc\ResourceSyncing; namespace Stancl\Tenancy\Tests\Etc\ResourceSyncing;
use Stancl\Tenancy\ResourceSyncing\PivotWithCentralResource; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Stancl\Tenancy\ResourceSyncing\PivotWithRelation;
use Stancl\Tenancy\ResourceSyncing\TenantPivot; use Stancl\Tenancy\ResourceSyncing\TenantPivot;
class CustomPivot extends TenantPivot implements PivotWithCentralResource class CustomPivot extends TenantPivot implements PivotWithRelation
{ {
public function getCentralResourceClass(): string public function users(): BelongsToMany
{ {
return CentralUser::class; return $this->belongsToMany(CentralUser::class);
}
public function getRelatedModel(): Model
{
return $this->users()->getModel();
} }
} }

View file

@ -263,7 +263,7 @@ test('attaching central resources to tenants or vice versa creates synced tenant
expect(TenantUser::all())->toHaveCount(0); expect(TenantUser::all())->toHaveCount(0);
}); });
// Attaching resources to tenants requires using a pivot that implements the PivotWithCentralResource interface // Attaching resources to tenants requires using a pivot that implements the PivotWithRelation interface
$tenant->customPivotUsers()->attach($createCentralUser()); $tenant->customPivotUsers()->attach($createCentralUser());
$createCentralUser()->tenants()->attach($tenant); $createCentralUser()->tenants()->attach($tenant);
@ -287,7 +287,7 @@ test('detaching central users from tenants or vice versa force deletes the synce
migrateUsersTableForTenants(); migrateUsersTableForTenants();
if ($attachUserToTenant) { if ($attachUserToTenant) {
// Attaching resources to tenants requires using a pivot that implements the PivotWithCentralResource interface // Attaching resources to tenants requires using a pivot that implements the PivotWithRelation interface
$tenant->customPivotUsers()->attach($centralUser); $tenant->customPivotUsers()->attach($centralUser);
} else { } else {
$centralUser->tenants()->attach($tenant); $centralUser->tenants()->attach($tenant);
@ -298,7 +298,7 @@ test('detaching central users from tenants or vice versa force deletes the synce
}); });
if ($attachUserToTenant) { if ($attachUserToTenant) {
// Detaching resources from tenants requires using a pivot that implements the PivotWithCentralResource interface // Detaching resources from tenants requires using a pivot that implements the PivotWithRelation interface
$tenant->customPivotUsers()->detach($centralUser); $tenant->customPivotUsers()->detach($centralUser);
} else { } else {
$centralUser->tenants()->detach($tenant); $centralUser->tenants()->detach($tenant);
@ -333,7 +333,7 @@ test('detaching central users from tenants or vice versa force deletes the synce
}); });
if ($attachUserToTenant) { if ($attachUserToTenant) {
// Detaching resources from tenants requires using a pivot that implements the PivotWithCentralResource interface // Detaching resources from tenants requires using a pivot that implements the PivotWithRelation interface
$tenant->customPivotUsers()->detach($centralUserWithSoftDeletes); $tenant->customPivotUsers()->detach($centralUserWithSoftDeletes);
} else { } else {
$centralUserWithSoftDeletes->tenants()->detach($tenant); $centralUserWithSoftDeletes->tenants()->detach($tenant);