1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2026-02-05 14:14:03 +00:00
This commit is contained in:
Abrar Ahmad 2022-08-16 15:27:34 +05:00
parent e756b54b31
commit 3e514461ca
5 changed files with 260 additions and 22 deletions

View file

@ -16,5 +16,5 @@ interface Syncable
public function triggerSyncEvent();
public function getCreateAttributeNames(): array;
public function getResourceCreationAttributes(): array|null;
}

View file

@ -32,4 +32,9 @@ trait ResourceSyncing
/** @var Syncable $this */
event(new SyncedResourceSaved($this, tenant()));
}
public function getResourceCreationAttributes(): array|null
{
return null;
}
}

View file

@ -6,6 +6,8 @@ namespace Stancl\Tenancy\Listeners;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Pivot;
use Illuminate\Support\Arr;
use Stancl\Tenancy\Contracts\Syncable;
use Stancl\Tenancy\Contracts\SyncMaster;
use Stancl\Tenancy\Events\SyncedResourceChangedInForeignDatabase;
use Stancl\Tenancy\Events\SyncedResourceSaved;
@ -58,7 +60,7 @@ class UpdateSyncedResource extends QueueableListener
event(new SyncedResourceChangedInForeignDatabase($event->model, null));
} else {
// If the resource doesn't exist at all in the central DB,we create
$centralModel = $event->model->getCentralModelName()::create($event->model->only($event->model->getCreateAttributeNames()));
$centralModel = $event->model->getCentralModelName()::create($this->getResourceCreationAttributes($event->model));
event(new SyncedResourceChangedInForeignDatabase($event->model, null));
}
});
@ -111,11 +113,24 @@ class UpdateSyncedResource extends QueueableListener
$localModel->update($syncedAttributes);
} else {
// When creating, we use all columns, not just the synced ones.
$localModel = $localModelClass::create($eventModel->getAttributes());
$localModel = $localModelClass::create($this->getResourceCreationAttributes($eventModel));
}
event(new SyncedResourceChangedInForeignDatabase($localModel, $tenant));
});
});
}
function getResourceCreationAttributes(Syncable $model): array
{
$attributes = $model->getAttributes();
if ($model->getResourceCreationAttributes()) {
// If array is key-value, We assume default values are provided
// if array is plain values, fetch attributes from model
$attributes = Arr::isAssoc($model->getResourceCreationAttributes()) ? $model->getResourceCreationAttributes() : $model->only($model->getResourceCreationAttributes());
}
return $attributes;
}
}

View file

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddExtraColumnToUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('foo');
});
}
public function down()
{
}
}

View file

@ -47,6 +47,7 @@ beforeEach(function () {
UpdateSyncedResource::$shouldQueue = false; // global state cleanup
Event::listen(SyncedResourceSaved::class, UpdateSyncedResource::class);
// run migrations on central connection
pest()->artisan('migrate', [
'--path' => [
__DIR__ . '/Etc/synced_resource_migrations',
@ -126,10 +127,96 @@ test('only the synced columns are updated in the central db', function () {
], ResourceUser::first()->getAttributes());
});
test('creating the resource in tenant database creates it in central database with provided attribute', function () {
test('creating the resource in tenant database creates it in central database ad 1:1 copy when create attributes are null', function () {
// Assert no user exists in central DB
expect(ResourceUser::all())->toHaveCount(0);
$tenant = ResourceTenant::create();
migrateTenantsResource();
tenancy()->initialize($tenant);
// Create the user in tenant DB
ResourceUser::create([
'global_id' => 'acme',
'name' => 'John Doe',
'email' => 'john@localhost',
'password' => 'secret',
'role' => 'commenter', // unsynced
]);
tenancy()->end();
// Assert central user was created without `code` property
expect(CentralUser::first()->toArray())->toEqual(ResourceUser::first()->toArray());
});
test('creating the resource in tenant database creates it in central database with provided default attributes values', function () {
// override method in ResourceUser class to return attribute values
class ResourceUserWithDefaultValues extends ResourceUser {
public function getResourceCreationAttributes(): array
{
// Attributes default values when creating resources from tenant to central DB
return
[
'global_id' => 'abc-123',
'name' => 'John',
'password' => 'password',
'email' => 'john@demo',
'role' => 'admin',
];
}
}
// Assert no user exists in central DB
expect(ResourceUserWithDefaultValues::all())->toHaveCount(0);
$tenant = ResourceTenant::create();
migrateTenantsResource();
tenancy()->initialize($tenant);
// Create the user in tenant DB
ResourceUserWithDefaultValues::create([
'global_id' => 'acme',
'name' => 'John Doe',
'email' => 'john@localhost',
'password' => 'secret',
'role' => 'commenter', // unsynced
]);
tenancy()->end();
expect(CentralUser::first()->global_id)->toBe('abc-123');
expect(CentralUser::first()->name)->toBe('John');
expect(CentralUser::first()->password)->toBe('password');
expect(CentralUser::first()->email)->toBe('john@demo');
expect(CentralUser::first()->role)->toBe('admin');
});
test('creating the resource in tenant database creates it in central database with provided attributes names', function () {
// override method in ResourceUser class to return attribute names
class ResourceUserWithAttributeNames extends ResourceUser {
public function getResourceCreationAttributes(): array
{
// Attributes used when creating resources from tenant to central DB
// Notice here we are not adding "code" filed because it does
// not exist in central model
return
[
'global_id',
'name',
'password',
'email',
'role'
];
}
}
// Assert no user exists in central DB
expect(ResourceUserWithAttributeNames::all())->toHaveCount(0);
$tenant = ResourceTenant::create();
pest()->artisan('tenants:migrate', [
'--path' => __DIR__ . '/Etc/synced_resource_migrations/custom',
@ -139,7 +226,7 @@ test('creating the resource in tenant database creates it in central database wi
tenancy()->initialize($tenant);
// Create the user in tenant DB
ResourceUser::create([
ResourceUserWithAttributeNames::create([
'global_id' => 'acme',
'name' => 'John Doe',
'email' => 'john@localhost',
@ -155,6 +242,128 @@ test('creating the resource in tenant database creates it in central database wi
expect(CentralUser::first()->code)->toBeNull();
});
test('creating the resource in central database creates it in tenant database with 1:1 copy when create attributes are null', function () {
$centralUser = CentralUser::create([
'global_id' => 'acme',
'name' => 'John Doe',
'email' => 'john@localhost',
'password' => 'secret',
'role' => 'commenter', // unsynced
]);
$tenant = ResourceTenant::create([
'id' => 't1',
]);
migrateTenantsResource();
$tenant->run(function () {
expect(ResourceUser::all())->toHaveCount(0);
});
$centralUser->tenants()->attach('t1');
$tenant->run(function () {
expect(ResourceUser::all())->toHaveCount(1);
expect(ResourceUser::first()->global_id)->toBe('acme');
});
});
test('creating the resource in central database creates it in tenant database with provided default attributes values', function () {
// override method in ResourceUser class to return attribute values
class CentralUserWithDefaultValues extends CentralUser {
public function getResourceCreationAttributes(): array
{
// Attributes default values when creating resources from central to tenant model
return
[
'global_id' => 'abc-123',
'name' => 'John',
'password' => 'password',
'email' => 'john@demo',
'role' => 'admin',
];
}
}
$centralUser = CentralUserWithDefaultValues::create([
'global_id' => 'acme',
'name' => 'John Doe',
'email' => 'john@localhost',
'password' => 'secret',
'role' => 'commenter', // unsynced
]);
$tenant = ResourceTenant::create([
'id' => 't1',
]);
migrateTenantsResource();
$tenant->run(function () {
expect(ResourceUser::all())->toHaveCount(0);
});
$centralUser->tenants()->attach('t1');
$tenant->run(function () {
expect(ResourceUser::all())->toHaveCount(1);
expect(ResourceUser::first()->global_id)->toBe('abc-123');
expect(ResourceUser::first()->name)->toBe('John');
expect(ResourceUser::first()->password)->toBe('password');
expect(ResourceUser::first()->email)->toBe('john@demo');
expect(ResourceUser::first()->role)->toBe('admin');
});
});
test('creating the resource in central database creates it in tenant database with provided attributes names', function () {
// override method in ResourceUser class to return attribute values
class CentralUserWithAttributeNames extends CentralUser {
public function getResourceCreationAttributes(): array
{
// Attributes used when creating resources from central to tenant DB
return
[
'global_id',
'name',
'password',
'email',
'role',
];
}
}
// migrate extra column in central DB
pest()->artisan('migrate', [
'--path' => __DIR__ . '/Etc/synced_resource_migrations/users_extra',
'--realpath' => true,
])->assertExitCode(0);
$centralUser = CentralUserWithAttributeNames::create([
'global_id' => 'acme',
'name' => 'John Doe',
'email' => 'john@localhost',
'password' => 'secret',
'role' => 'commenter',
'foo' => 'bar', // foo does not exist in resource
]);
$tenant = ResourceTenant::create([
'id' => 't1',
]);
migrateTenantsResource();
$tenant->run(function () {
expect(ResourceUser::all())->toHaveCount(0);
});
$centralUser->tenants()->attach('t1');
$tenant->run(function () {
expect(ResourceUser::all())->toHaveCount(1);
expect(ResourceUser::first()->global_id)->toBe('acme');
expect(ResourceUser::first()->foo)->toBeNull(); // assert foo is not copied from the central to tenant model
});
});
test('creating the resource in tenant database creates it in central database and creates the mapping', function () {
creatingResourceInTenantDatabaseCreatesAndMapInCentralDatabase();
});
@ -627,11 +836,6 @@ class CentralUser extends Model implements SyncMaster
'email',
];
}
public function getCreateAttributeNames(): array
{
return [];
}
}
class ResourceUser extends Model implements Syncable
@ -667,16 +871,4 @@ class ResourceUser extends Model implements Syncable
'email',
];
}
public function getCreateAttributeNames(): array
{
// Attributes used when creating resources from tenant to central DB
return [
'global_id',
'name',
'password',
'email',
'role'
];
}
}