mirror of
https://github.com/archtechx/tenancy.git
synced 2026-02-05 14:14:03 +00:00
wip
This commit is contained in:
parent
e756b54b31
commit
3e514461ca
5 changed files with 260 additions and 22 deletions
|
|
@ -16,5 +16,5 @@ interface Syncable
|
|||
|
||||
public function triggerSyncEvent();
|
||||
|
||||
public function getCreateAttributeNames(): array;
|
||||
public function getResourceCreationAttributes(): array|null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,4 +32,9 @@ trait ResourceSyncing
|
|||
/** @var Syncable $this */
|
||||
event(new SyncedResourceSaved($this, tenant()));
|
||||
}
|
||||
|
||||
public function getResourceCreationAttributes(): array|null
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue