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

Compare commits

..

4 commits

Author SHA1 Message Date
66fdf96c1c
[MINOR BC BREAK] Syncing: PivotWithRelation -> PivotWithCentralResource
The old names of the class and method were misleading. We don't
actually need any relation. And we don't even need a model instance
as we were returning previously -- the only use of that method was
in TriggerSyncingEvents which would immediately use ::class on the
returned value. Therefore, all we are asking for in this interface
is just the central resource class.
2025-11-25 04:29:46 +01:00
e7f460090f
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-25 04:15:38 +01:00
lukinovec
628f357f32 Syncing: Add DeleteAllTenantMappings listener 2025-11-25 04:15:38 +01:00
lukinovec
4474671255 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-25 04:15:24 +01:00
7 changed files with 26 additions and 35 deletions

View file

@ -146,7 +146,9 @@ 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 PivotWithRelation and return the related model in getRelatedModel() or extend MorphPivot.' To make this work both ways, you can make your pivot implement PivotWithCentralResource and return the related model in getCentralResourceClass() or extend MorphPivot.'
); );
} }
} }

View file

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

@ -1,15 +0,0 @@
<?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)&PivotWithRelation)) $this */ /** @var $this&(Pivot|MorphPivot|((Pivot|MorphPivot)&PivotWithCentralResource)) $this */
if ($this instanceof PivotWithRelation) { if ($this instanceof PivotWithCentralResource) {
return $this->getRelatedModel()::class; return $this->getCentralResourceClass();
} }
if ($this instanceof MorphPivot) { if ($this instanceof MorphPivot) {

View file

@ -4,20 +4,13 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Tests\Etc\ResourceSyncing; namespace Stancl\Tenancy\Tests\Etc\ResourceSyncing;
use Illuminate\Database\Eloquent\Model; use Stancl\Tenancy\ResourceSyncing\PivotWithCentralResource;
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 PivotWithRelation class CustomPivot extends TenantPivot implements PivotWithCentralResource
{ {
public function users(): BelongsToMany public function getCentralResourceClass(): string
{ {
return $this->belongsToMany(CentralUser::class); return 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 PivotWithRelation interface // Attaching resources to tenants requires using a pivot that implements the PivotWithCentralResource 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 PivotWithRelation interface // Attaching resources to tenants requires using a pivot that implements the PivotWithCentralResource 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 PivotWithRelation interface // Detaching resources from tenants requires using a pivot that implements the PivotWithCentralResource 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 PivotWithRelation interface // Detaching resources from tenants requires using a pivot that implements the PivotWithCentralResource interface
$tenant->customPivotUsers()->detach($centralUserWithSoftDeletes); $tenant->customPivotUsers()->detach($centralUserWithSoftDeletes);
} else { } else {
$centralUserWithSoftDeletes->tenants()->detach($tenant); $centralUserWithSoftDeletes->tenants()->detach($tenant);