From a3d6f75ab1b9e200486334ef0960cebabce93b13 Mon Sep 17 00:00:00 2001 From: Ruben Van Assche Date: Mon, 19 Feb 2024 12:50:19 +0100 Subject: [PATCH] First changes so that Laravel Data and livewire work better together --- composer.json | 1 + src/LaravelDataServiceProvider.php | 11 ++ src/Support/Lazy/LivewireLostLazy.php | 20 ++++ src/Support/Livewire/LivewireDataSynth.php | 119 +++++++++++++++++++++ 4 files changed, 151 insertions(+) create mode 100644 src/Support/Lazy/LivewireLostLazy.php create mode 100644 src/Support/Livewire/LivewireDataSynth.php diff --git a/composer.json b/composer.json index ac0622a91..65f4066b3 100644 --- a/composer.json +++ b/composer.json @@ -23,6 +23,7 @@ "spatie/php-structure-discoverer": "^2.0" }, "require-dev" : { + "livewire/livewire" : "^3.0", "fakerphp/faker": "^1.14", "friendsofphp/php-cs-fixer": "^3.0", "inertiajs/inertia-laravel": "dev-master#4508fd1", diff --git a/src/LaravelDataServiceProvider.php b/src/LaravelDataServiceProvider.php index f748b0eef..d9b648af2 100644 --- a/src/LaravelDataServiceProvider.php +++ b/src/LaravelDataServiceProvider.php @@ -2,11 +2,13 @@ namespace Spatie\LaravelData; +use Livewire\Livewire; use Spatie\LaravelData\Commands\DataMakeCommand; use Spatie\LaravelData\Commands\DataStructuresCacheCommand; use Spatie\LaravelData\Contracts\BaseData; use Spatie\LaravelData\Support\Caching\DataStructureCache; use Spatie\LaravelData\Support\DataConfig; +use Spatie\LaravelData\Support\Livewire\LivewireDataSynth; use Spatie\LaravelData\Support\VarDumper\VarDumperManager; use Spatie\LaravelPackageTools\Package; use Spatie\LaravelPackageTools\PackageServiceProvider; @@ -50,6 +52,15 @@ function () { fn ($container) => $class::from($container['request']) ); }); + + if(class_exists(Livewire::class)) { + $this->registerLivewireSynths(); + } + } + + protected function registerLivewireSynths(): void + { + Livewire::propertySynthesizer(LivewireDataSynth::class); } public function packageBooted(): void diff --git a/src/Support/Lazy/LivewireLostLazy.php b/src/Support/Lazy/LivewireLostLazy.php new file mode 100644 index 000000000..2e9452419 --- /dev/null +++ b/src/Support/Lazy/LivewireLostLazy.php @@ -0,0 +1,20 @@ +dataClass}::{$this->propertyName}` was lost when the data object was transformed to be used by Livewire. You can include the property and then the correct value will be set when creating the data object from Livewire again."); + } +} diff --git a/src/Support/Livewire/LivewireDataSynth.php b/src/Support/Livewire/LivewireDataSynth.php new file mode 100644 index 000000000..78b306dd1 --- /dev/null +++ b/src/Support/Livewire/LivewireDataSynth.php @@ -0,0 +1,119 @@ + probably a better default + // What if we want to create a new data object and don't have the lazyvalue + // -> we could create a new LiveWireMissingLazy object, which we then use as the lazy value + // -> when resolving it would throw an error saying the value was lost in LiveWire + // + // Problem with computed properties should be sorted out + // + // DataCollections synth from the PR + // + // Do we want livewire as an dependency? + // + // Update docs + // + // Can we test this? + // + // Mapping property names, should we do this? + + + protected DataConfig $dataConfig; + + public static string $key = 'laravel-data-object'; + + public function __construct(ComponentContext $context, $path) + { + $this->dataConfig = app(DataConfig::class); + + parent::__construct($context, $path); + } + + public static function match($target) + { + return $target instanceof BaseData && $target instanceof TransformableData; + } + + public function get(&$target, $key): BaseData + { + return $target->{$key}; + } + + public function set(&$target, $key, $value): void + { + $target->{$key} = $value; + } + + /** + * @param BaseData&TransformableData $target + * @param callable(mixed):mixed $dehydrateChild + * + * @return array + */ + public function dehydrate( + BaseData&TransformableData $target, + callable $dehydrateChild + ): array { + $morph = $this->dataConfig->morphMap->getDataClassAlias($target::class) ?? $target::class; + + $payload = $target->all(); + + ray($payload); + + foreach ($payload as $key => $value) { + $payload[$key] = $dehydrateChild($key, $value); + } + + return [ + $payload, + ['morph' => $morph], + ]; + } + + /** + * @param mixed $value + * @param array $meta + * @param callable(mixed):mixed $hydrateChild + * + * @return BaseData + */ + public function hydrate( + array $value, + array $meta, + callable $hydrateChild + ): BaseData { + $morph = $meta['morph']; + + $dataClass = $this->dataConfig->morphMap->getMorphedDataClass($morph) ?? $morph; + + $payload = []; + + foreach ($this->dataConfig->getDataClass($dataClass)->properties as $name => $property) { + if (array_key_exists($name, $value) === false && $property->type->lazyType) { + $payload[$name] = new LivewireLostLazy($dataClass, $name); + + continue; + } + + $payload[$name] = $hydrateChild($name, $value[$name]); + } + + return $dataClass::factory() + ->ignoreMagicalMethod('fromLivewire') + ->from($payload); + } +}