diff --git a/composer.json b/composer.json index f7adfce3..cdc7d0e8 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,8 @@ "illuminate/support": "^6.0|^7.0", "facade/ignition-contracts": "^1.0", "ramsey/uuid": "^3.7|^4.0", - "stancl/jobpipeline": "^1.0" + "stancl/jobpipeline": "^1.0", + "stancl/virtualcolumn": "^1.0" }, "require-dev": { "vlucas/phpdotenv": "^3.3|^4.0", diff --git a/src/Database/Concerns/HasDataColumn.php b/src/Database/Concerns/HasDataColumn.php index 704870de..cf67b832 100644 --- a/src/Database/Concerns/HasDataColumn.php +++ b/src/Database/Concerns/HasDataColumn.php @@ -4,127 +4,12 @@ declare(strict_types=1); namespace Stancl\Tenancy\Database\Concerns; +use Stancl\VirtualColumn\VirtualColumn; + /** - * This trait lets you add a "data" column functionality to any Eloquent model. - * It serializes attributes which don't exist as columns on the model's table - * into a JSON column named data (customizable by overriding getDataColumn). + * Extends VirtualColumn for backwards compatibility. This trait will be removed in v4. */ trait HasDataColumn { - public static $priorityListeners = []; - - /** - * We need this property, because both created & saved event listeners - * decode the data (to take precedence before other created & saved) - * listeners, but we don't want the dadta to be decoded twice. - * - * @var string - */ - public $dataEncodingStatus = 'decoded'; - - public static function bootHasDataColumn() - { - $encode = function (self $model) { - if ($model->dataEncodingStatus === 'encoded') { - return; - } - - foreach ($model->getAttributes() as $key => $value) { - if (! in_array($key, static::getCustomColumns())) { - $current = $model->getAttribute(static::getDataColumn()) ?? []; - - $model->setAttribute(static::getDataColumn(), array_merge($current, [ - $key => $value, - ])); - - unset($model->attributes[$key]); - } - } - - $model->dataEncodingStatus = 'encoded'; - }; - - $decode = function (self $model) { - if ($model->dataEncodingStatus === 'decoded') { - return; - } - - foreach ($model->getAttribute(static::getDataColumn()) ?? [] as $key => $value) { - $model->setAttribute($key, $value); - } - - $model->setAttribute(static::getDataColumn(), null); - - $model->dataEncodingStatus = 'decoded'; - }; - - static::registerPriorityListener('retrieved', function ($model) use ($decode) { - // We always decode after model retrieval. - $model->dataEncodingStatus = 'encoded'; - - $decode($model); - }); - - static::registerPriorityListener('saving', $encode); - static::registerPriorityListener('creating', $encode); - static::registerPriorityListener('updating', $encode); - - static::registerPriorityListener('saved', $decode); - static::registerPriorityListener('created', $decode); - static::registerPriorityListener('updated', $decode); - } - - protected function fireModelEvent($event, $halt = true) - { - $this->runPriorityListeners($event, $halt); - - return parent::fireModelEvent($event, $halt); - } - - public function runPriorityListeners($event, $halt = true) - { - $listeners = static::$priorityListeners[$event] ?? []; - - if (! $event) { - return; - } - - foreach ($listeners as $listener) { - if (is_string($listener)) { - $listener = app($listener); - $handle = [$listener, 'handle']; - } else { - $handle = $listener; - } - - $handle($this); - } - } - - public static function registerPriorityListener(string $event, callable $callback) - { - static::$priorityListeners[$event][] = $callback; - } - - public function getCasts() - { - return array_merge(parent::getCasts(), [ - static::getDataColumn() => 'array', - ]); - } - - /** - * Get the name of the column that stores additional data. - */ - public static function getDataColumn(): string - { - return 'data'; - } - - public static function getCustomColumns(): array - { - return [ - 'id', - ]; - } + use VirtualColumn; } diff --git a/tests/TenantModelTest.php b/tests/TenantModelTest.php index 7ff4ca1f..e29fcafa 100644 --- a/tests/TenantModelTest.php +++ b/tests/TenantModelTest.php @@ -50,44 +50,6 @@ class TenantModelTest extends TestCase $this->assertSame(null, app(Contracts\Tenant::class)); } - /** @test */ - public function keys_which_dont_have_their_own_column_go_into_data_json_column() - { - $tenant = Tenant::create([ - 'foo' => 'bar', - ]); - - // Test that model works correctly - $this->assertSame('bar', $tenant->foo); - $this->assertSame(null, $tenant->data); - - // Low level test to assert database structure - $this->assertSame(['foo' => 'bar'], json_decode(DB::table('tenants')->where('id', $tenant->id)->first()->data, true)); - $this->assertSame(null, DB::table('tenants')->where('id', $tenant->id)->first()->foo ?? null); - - // Model has the correct structure when retrieved - $tenant = Tenant::first(); - $this->assertSame('bar', $tenant->foo); - $this->assertSame(null, $tenant->data); - - // Model can be updated - $tenant->update([ - 'foo' => 'baz', - 'abc' => 'xyz', - ]); - - $this->assertSame('baz', $tenant->foo); - $this->assertSame('xyz', $tenant->abc); - $this->assertSame(null, $tenant->data); - - // Model can be retrieved after update & is structure correctly - $tenant = Tenant::first(); - - $this->assertSame('baz', $tenant->foo); - $this->assertSame('xyz', $tenant->abc); - $this->assertSame(null, $tenant->data); - } - /** @test */ public function id_is_generated_when_no_id_is_supplied() { @@ -170,12 +132,6 @@ class TenantModelTest extends TestCase $this->assertSame(2, Tenant::count()); } - /** @test */ - public function data_is_never_encoded_or_decoded_twice() - { - // todo. tests for registerPriorityListener - } - /** @test */ public function the_model_uses_TenantCollection() {