mirror of
https://github.com/archtechx/tenancy.git
synced 2025-12-12 23:34:03 +00:00
Make tenants relationship name configurable using the getTenantsRelationshipName()) method in SyncMaster
This commit is contained in:
parent
62624275cc
commit
fc809ba55f
7 changed files with 87 additions and 20 deletions
|
|
@ -19,13 +19,15 @@ class DeleteResourcesInTenants extends QueueableListener
|
||||||
$centralResource = $event->centralResource;
|
$centralResource = $event->centralResource;
|
||||||
$forceDelete = $event->forceDelete;
|
$forceDelete = $event->forceDelete;
|
||||||
|
|
||||||
tenancy()->runForMultiple($centralResource->tenants()->cursor(), function () use ($centralResource, $forceDelete) {
|
$relationshipName = $centralResource->getTenantsRelationshipName();
|
||||||
|
|
||||||
|
tenancy()->runForMultiple($centralResource->{$relationshipName}()->cursor(), function () use ($centralResource, $forceDelete, $relationshipName) {
|
||||||
$this->deleteSyncedResource($centralResource, $forceDelete);
|
$this->deleteSyncedResource($centralResource, $forceDelete);
|
||||||
|
|
||||||
// Delete pivot records if the central resource doesn't use soft deletes
|
// Delete pivot records if the central resource doesn't use soft deletes
|
||||||
// or the central resource was deleted using forceDelete()
|
// or the central resource was deleted using forceDelete()
|
||||||
if ($forceDelete || ! in_array(SoftDeletes::class, class_uses_recursive($centralResource::class), true)) {
|
if ($forceDelete || ! in_array(SoftDeletes::class, class_uses_recursive($centralResource::class), true)) {
|
||||||
$centralResource->tenants()->detach(tenant());
|
$centralResource->{$relationshipName}()->detach(tenant());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,9 @@ class RestoreResourcesInTenants extends QueueableListener
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tenancy()->runForMultiple($centralResource->tenants()->cursor(), function () use ($centralResource) {
|
$relationshipName = $centralResource->getTenantsRelationshipName();
|
||||||
|
|
||||||
|
tenancy()->runForMultiple($centralResource->{$relationshipName}()->cursor(), function () use ($centralResource) {
|
||||||
$tenantResourceClass = $centralResource->getTenantModelName();
|
$tenantResourceClass = $centralResource->getTenantModelName();
|
||||||
/**
|
/**
|
||||||
* @var Syncable $centralResource
|
* @var Syncable $centralResource
|
||||||
|
|
|
||||||
|
|
@ -63,13 +63,14 @@ class UpdateOrCreateSyncedResource extends QueueableListener
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var Tenant&Model&SyncMaster $centralModel */
|
/** @var Tenant&Model&SyncMaster $centralModel */
|
||||||
|
$relationshipName = $centralModel->getTenantsRelationshipName();
|
||||||
|
|
||||||
// Since this model is "dirty" (taken by reference from the event), it might have the tenants
|
// Since this model is "dirty" (taken by reference from the event), it might have the tenants
|
||||||
// relationship already loaded and cached. For this reason, we refresh the relationship.
|
// relationship already loaded and cached. For this reason, we refresh the relationship.
|
||||||
$centralModel->load('tenants');
|
$centralModel->load($relationshipName);
|
||||||
|
|
||||||
/** @var TenantCollection $tenants */
|
/** @var TenantCollection $tenants */
|
||||||
$tenants = $centralModel->tenants;
|
$tenants = $centralModel->{$relationshipName};
|
||||||
|
|
||||||
return $tenants;
|
return $tenants;
|
||||||
}
|
}
|
||||||
|
|
@ -100,21 +101,22 @@ class UpdateOrCreateSyncedResource extends QueueableListener
|
||||||
return ((string) $model->pivot->getAttribute(Tenancy::tenantKeyColumn())) === ((string) $tenant->getTenantKey());
|
return ((string) $model->pivot->getAttribute(Tenancy::tenantKeyColumn())) === ((string) $tenant->getTenantKey());
|
||||||
};
|
};
|
||||||
|
|
||||||
$mappingExists = $centralModel->tenants->contains($currentTenantMapping);
|
$relationshipName = $centralModel->getTenantsRelationshipName();
|
||||||
|
$mappingExists = $centralModel->{$relationshipName}->contains($currentTenantMapping);
|
||||||
|
|
||||||
if (! $mappingExists) {
|
if (! $mappingExists) {
|
||||||
// Here we should call TenantPivot, but we call general Pivot, so that this works
|
// Here we should call TenantPivot, but we call general Pivot, so that this works
|
||||||
// even if people use their own pivot model that is not based on our TenantPivot
|
// even if people use their own pivot model that is not based on our TenantPivot
|
||||||
Pivot::withoutEvents(function () use ($centralModel, $event) {
|
Pivot::withoutEvents(function () use ($centralModel, $event, $relationshipName) {
|
||||||
/** @var TenantWithDatabase */
|
/** @var TenantWithDatabase */
|
||||||
$tenant = $event->tenant;
|
$tenant = $event->tenant;
|
||||||
|
|
||||||
$centralModel->tenants()->attach($tenant->getTenantKey());
|
$centralModel->{$relationshipName}()->attach($tenant->getTenantKey());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var TenantCollection $tenants */
|
/** @var TenantCollection $tenants */
|
||||||
$tenants = $centralModel->tenants->filter(function ($model) use ($currentTenantMapping) {
|
$tenants = $centralModel->{$relationshipName}->filter(function ($model) use ($currentTenantMapping) {
|
||||||
// Remove the mapping for the current tenant.
|
// Remove the mapping for the current tenant.
|
||||||
return ! $currentTenantMapping($model);
|
return ! $currentTenantMapping($model);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -105,12 +105,6 @@ trait ResourceSyncing
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tenants(): BelongsToMany
|
|
||||||
{
|
|
||||||
return $this->morphToMany(config('tenancy.models.tenant'), 'tenant_resources', 'tenant_resources', 'resource_global_id', 'tenant_id', $this->getGlobalIdentifierKeyName())
|
|
||||||
->using(TenantMorphPivot::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getGlobalIdentifierKeyName(): string
|
public function getGlobalIdentifierKeyName(): string
|
||||||
{
|
{
|
||||||
return 'global_id';
|
return 'global_id';
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ namespace Stancl\Tenancy\ResourceSyncing;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
|
||||||
use Stancl\Tenancy\Database\Contracts\TenantWithDatabase;
|
use Stancl\Tenancy\Database\Contracts\TenantWithDatabase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -14,13 +13,16 @@ use Stancl\Tenancy\Database\Contracts\TenantWithDatabase;
|
||||||
*/
|
*/
|
||||||
interface SyncMaster extends Syncable
|
interface SyncMaster extends Syncable
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @return BelongsToMany<TenantWithDatabase&Model, self&Model>
|
|
||||||
*/
|
|
||||||
public function tenants(): BelongsToMany;
|
|
||||||
|
|
||||||
public function getTenantModelName(): string;
|
public function getTenantModelName(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should return the name of the relationship to the tenants table (e.g. 'tenants').
|
||||||
|
*
|
||||||
|
* In the class where this interface is implemented, the relationship method also has to be defined.
|
||||||
|
*/
|
||||||
|
public function getTenantsRelationshipName(): string;
|
||||||
|
|
||||||
public function triggerDetachEvent(TenantWithDatabase&Model $tenant): void;
|
public function triggerDetachEvent(TenantWithDatabase&Model $tenant): void;
|
||||||
|
|
||||||
public function triggerAttachEvent(TenantWithDatabase&Model $tenant): void;
|
public function triggerAttachEvent(TenantWithDatabase&Model $tenant): void;
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,13 @@ use Illuminate\Database\Eloquent\Model;
|
||||||
use Stancl\Tenancy\Database\Concerns\CentralConnection;
|
use Stancl\Tenancy\Database\Concerns\CentralConnection;
|
||||||
use Stancl\Tenancy\ResourceSyncing\ResourceSyncing;
|
use Stancl\Tenancy\ResourceSyncing\ResourceSyncing;
|
||||||
use Stancl\Tenancy\ResourceSyncing\SyncMaster;
|
use Stancl\Tenancy\ResourceSyncing\SyncMaster;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
|
use Stancl\Tenancy\ResourceSyncing\TenantMorphPivot;
|
||||||
|
|
||||||
class CentralUser extends Model implements SyncMaster
|
class CentralUser extends Model implements SyncMaster
|
||||||
{
|
{
|
||||||
use ResourceSyncing, CentralConnection;
|
use ResourceSyncing, CentralConnection;
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
public $timestamps = false;
|
public $timestamps = false;
|
||||||
|
|
@ -29,6 +32,19 @@ class CentralUser extends Model implements SyncMaster
|
||||||
return TenantUser::class;
|
return TenantUser::class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getTenantsRelationshipName(): string
|
||||||
|
{
|
||||||
|
return 'tenants';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function tenants(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->morphToMany(config('tenancy.models.tenant'), 'tenant_resources', 'tenant_resources', 'resource_global_id', 'tenant_id', $this->getGlobalIdentifierKeyName())
|
||||||
|
->using(TenantMorphPivot::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function shouldSync(): bool
|
public function shouldSync(): bool
|
||||||
{
|
{
|
||||||
return static::$shouldSync;
|
return static::$shouldSync;
|
||||||
|
|
|
||||||
|
|
@ -1265,6 +1265,30 @@ test('global scopes on syncable models can break resource syncing', function ()
|
||||||
expect($tenant1->run(fn () => TenantUser::first()->name))->toBe('tenant2 user');
|
expect($tenant1->run(fn () => TenantUser::first()->name))->toBe('tenant2 user');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('tenants relationship name can be customized', function () {
|
||||||
|
$tenant = Tenant::create();
|
||||||
|
migrateUsersTableForTenants();
|
||||||
|
|
||||||
|
$tenant->run(function () {
|
||||||
|
expect(TenantUser::count())->toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Model with a custom tenants relationship ('organizations')
|
||||||
|
$centralUserWithCustomTenants = CentralUserWithCustomTenantsRelationship::create([
|
||||||
|
'global_id' => 'tenant_user',
|
||||||
|
'name' => 'Tenant user',
|
||||||
|
'email' => 'tenant@user',
|
||||||
|
'password' => 'secret',
|
||||||
|
'role' => 'tester',
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
$centralUserWithCustomTenants->organizations()->attach($tenant);
|
||||||
|
|
||||||
|
$tenant->run(function () {
|
||||||
|
expect(TenantUser::firstWhere('name', 'Tenant user'))->not()->toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
/**
|
/**
|
||||||
* Create two tenants and run migrations for those tenants.
|
* Create two tenants and run migrations for those tenants.
|
||||||
*
|
*
|
||||||
|
|
@ -1322,6 +1346,20 @@ class CentralUser extends BaseCentralUser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CentralUserWithCustomTenantsRelationship extends BaseCentralUser
|
||||||
|
{
|
||||||
|
public function getTenantsRelationshipName(): string
|
||||||
|
{
|
||||||
|
return 'organizations';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function organizations(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Tenant::class, 'tenant_users', 'global_user_id', 'tenant_id', 'global_id')
|
||||||
|
->using(TenantPivot::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class TenantUser extends BaseTenantUser
|
class TenantUser extends BaseTenantUser
|
||||||
{
|
{
|
||||||
public function getCentralModelName(): string
|
public function getCentralModelName(): string
|
||||||
|
|
@ -1402,6 +1440,17 @@ class CentralCompany extends Model implements SyncMaster
|
||||||
|
|
||||||
public $table = 'companies';
|
public $table = 'companies';
|
||||||
|
|
||||||
|
public function getTenantsRelationshipName(): string
|
||||||
|
{
|
||||||
|
return 'tenants';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tenants(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->morphToMany(config('tenancy.models.tenant'), 'tenant_resources', 'tenant_resources', 'resource_global_id', 'tenant_id', $this->getGlobalIdentifierKeyName())
|
||||||
|
->using(TenantMorphPivot::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function getTenantModelName(): string
|
public function getTenantModelName(): string
|
||||||
{
|
{
|
||||||
return TenantCompany::class;
|
return TenantCompany::class;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue