Skip to content

Commit

Permalink
First changes so that Laravel Data and livewire work better together
Browse files Browse the repository at this point in the history
  • Loading branch information
rubenvanassche committed Feb 19, 2024
1 parent 4135f77 commit a3d6f75
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 0 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
11 changes: 11 additions & 0 deletions src/LaravelDataServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions src/Support/Lazy/LivewireLostLazy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Spatie\LaravelData\Support\Lazy;

use Exception;
use Spatie\LaravelData\Lazy;

class LivewireLostLazy extends Lazy
{
public function __construct(
public string $dataClass,
public string $propertyName
) {
}

public function resolve(): mixed
{
return throw new Exception("Lazy property `{$this->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.");
}
}
119 changes: 119 additions & 0 deletions src/Support/Livewire/LivewireDataSynth.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?php

namespace Spatie\LaravelData\Support\Livewire;

use Livewire\Mechanisms\HandleComponents\ComponentContext;
use Livewire\Mechanisms\HandleComponents\Synthesizers\Synth;
use Spatie\LaravelData\Contracts\BaseData;
use Spatie\LaravelData\Contracts\TransformableData;
use Spatie\LaravelData\Support\DataConfig;
use Spatie\LaravelData\Support\Lazy\LivewireLostLazy;

class LivewireDataSynth extends Synth
{
// TODO:
//
// Think about Lazy, we can always send them down?
// Or only conditional lazy which are true -> 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);

Check failure on line 78 in src/Support/Livewire/LivewireDataSynth.php

View workflow job for this annotation

GitHub Actions / phpstan

Callable callable(mixed): mixed invoked with 2 parameters, 1 required.

Check failure on line 78 in src/Support/Livewire/LivewireDataSynth.php

View workflow job for this annotation

GitHub Actions / phpstan

Callable callable(mixed): mixed invoked with 2 parameters, 1 required.
}

return [
$payload,
['morph' => $morph],
];
}

/**
* @param mixed $value
* @param array $meta
* @param callable(mixed):mixed $hydrateChild
*
* @return BaseData
*/
public function hydrate(

Check failure on line 94 in src/Support/Livewire/LivewireDataSynth.php

View workflow job for this annotation

GitHub Actions / phpstan

PHPDoc tag @param for parameter $value with type mixed is not subtype of native type array.

Check failure on line 94 in src/Support/Livewire/LivewireDataSynth.php

View workflow job for this annotation

GitHub Actions / phpstan

PHPDoc tag @param for parameter $value with type mixed is not subtype of native type array.
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]);

Check failure on line 112 in src/Support/Livewire/LivewireDataSynth.php

View workflow job for this annotation

GitHub Actions / phpstan

Callable callable(mixed): mixed invoked with 2 parameters, 1 required.

Check failure on line 112 in src/Support/Livewire/LivewireDataSynth.php

View workflow job for this annotation

GitHub Actions / phpstan

Callable callable(mixed): mixed invoked with 2 parameters, 1 required.
}

return $dataClass::factory()
->ignoreMagicalMethod('fromLivewire')
->from($payload);
}
}

0 comments on commit a3d6f75

Please sign in to comment.