Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add better support for serializing data #735

Merged
merged 1 commit into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Concerns/BaseData.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public function __sleep(): array
->properties
->map(fn (DataProperty $property) => $property->name)
->when($dataClass->appendable, fn (Collection $properties) => $properties->push('_additional'))
->when(property_exists($this, '_dataContext'), fn (Collection $properties) => $properties->push('_dataContext'))
->toArray();
}
}
4 changes: 4 additions & 0 deletions src/Lazy.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ public static function closure(Closure $closure): ClosureLazy

abstract public function resolve(): mixed;

abstract public function __serialize(): array;

abstract public function __unserialize(array $data): void;

public function defaultIncluded(bool $defaultIncluded = true): self
{
$this->defaultIncluded = $defaultIncluded;
Expand Down
17 changes: 17 additions & 0 deletions src/Support/Lazy/ConditionalLazy.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Spatie\LaravelData\Support\Lazy;

use Closure;
use Laravel\SerializableClosure\SerializableClosure;
use Spatie\LaravelData\Lazy;

class ConditionalLazy extends Lazy
Expand All @@ -22,4 +23,20 @@ public function shouldBeIncluded(): bool
{
return (bool) ($this->condition)();
}

public function __serialize(): array
{
return [
'condition' => new SerializableClosure($this->condition),
'value' => new SerializableClosure($this->value),
'defaultIncluded' => $this->defaultIncluded,
];
}

public function __unserialize(array $data): void
{
$this->condition = $data['condition']->getClosure();
$this->value = $data['value']->getClosure();
$this->defaultIncluded = $data['defaultIncluded'];
}
}
15 changes: 15 additions & 0 deletions src/Support/Lazy/DefaultLazy.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Spatie\LaravelData\Support\Lazy;

use Closure;
use Laravel\SerializableClosure\SerializableClosure;
use Spatie\LaravelData\Lazy;

class DefaultLazy extends Lazy
Expand All @@ -16,4 +17,18 @@ public function resolve(): mixed
{
return ($this->value)();
}

public function __serialize(): array
{
return [
'value' => new SerializableClosure($this->value),
'defaultIncluded' => $this->defaultIncluded,
];
}

public function __unserialize(array $data): void
{
$this->value = $data['value']->getClosure();
$this->defaultIncluded = $data['defaultIncluded'];
}
}
16 changes: 16 additions & 0 deletions src/Support/Lazy/LivewireLostLazy.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,20 @@ 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.");
}

public function __serialize(): array
{
return [
'dataClass' => $this->dataClass,
'propertyName' => $this->propertyName,
'defaultIncluded' => $this->defaultIncluded,
];
}

public function __unserialize(array $data): void
{
$this->dataClass = $data['dataClass'];
$this->propertyName = $data['propertyName'];
$this->defaultIncluded = $data['defaultIncluded'];
}
}
19 changes: 19 additions & 0 deletions src/Support/Lazy/RelationalLazy.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Closure;
use Illuminate\Database\Eloquent\Model;
use Laravel\SerializableClosure\SerializableClosure;
use Spatie\LaravelData\Lazy;

class RelationalLazy extends Lazy
Expand All @@ -24,4 +25,22 @@ public function shouldBeIncluded(): bool
{
return $this->model->relationLoaded($this->relation);
}

public function __serialize(): array
{
return [
'relation' => $this->relation,
'model' => $this->model,
'value' => new SerializableClosure($this->value),
'defaultIncluded' => $this->defaultIncluded,
];
}

public function __unserialize(array $data): void
{
$this->relation = $data['relation'];
$this->model = $data['model'];
$this->value = $data['value']->getClosure();
$this->defaultIncluded = $data['defaultIncluded'];
}
}
26 changes: 26 additions & 0 deletions src/Support/Partials/Partial.php
Original file line number Diff line number Diff line change
Expand Up @@ -265,4 +265,30 @@ public function __toString(): string
{
return implode('.', $this->segments)." (current: {$this->pointer})";
}

public function __serialize(): array
{
return [
'segmentCount' => $this->segmentCount,
'endsInAll' => $this->endsInAll,
'segments' => $this->segments,
'condition' => $this->condition
? serialize(new SerializableClosure($this->condition))
: null,
'permanent' => $this->permanent,
'pointer' => $this->pointer,
];
}

public function __unserialize(array $data): void
{
$this->segmentCount = $data['segmentCount'];
$this->endsInAll = $data['endsInAll'];
$this->segments = $data['segments'];
$this->pointer = $data['pointer'];
$this->condition = $data['condition']
? unserialize($data['condition'])->getClosure()
: null;
$this->permanent = $data['permanent'];
}
}
31 changes: 0 additions & 31 deletions tests/DataCollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
use Spatie\LaravelData\Tests\Fakes\LazyData;
use Spatie\LaravelData\Tests\Fakes\SimpleData;

use function Spatie\Snapshots\assertMatchesSnapshot;

it('can filter a collection', function () {
$collection = new DataCollection(SimpleData::class, ['A', 'B']);

Expand Down Expand Up @@ -237,35 +235,6 @@
->toMatchArray($filtered);
});

it('can serialize and unserialize a data collection', function () {
$collection = new DataCollection(SimpleData::class, ['A', 'B']);

$serialized = serialize($collection);

assertMatchesSnapshot($serialized);

$unserialized = unserialize($serialized);

expect($unserialized)->toBeInstanceOf(DataCollection::class);
expect($unserialized)->toEqual(new DataCollection(SimpleData::class, ['A', 'B']));
});

it('during the serialization process some properties are thrown away', function () {
$collection = new DataCollection(SimpleData::class, ['A', 'B']);

$collection->include('test');
$collection->exclude('test');
$collection->only('test');
$collection->except('test');
$collection->wrap('test');

$unserialized = unserialize(serialize($collection));

$invaded = invade($unserialized);

expect($invaded->_dataContext)->toBeNull();
});

it('can use a custom collection extended from collection to collect a collection of data objects', function () {
$collection = SimpleData::collect(new CustomCollection([
['string' => 'A'],
Expand Down
48 changes: 0 additions & 48 deletions tests/DataTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,9 @@
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Dto;
use Spatie\LaravelData\Resource;
use Spatie\LaravelData\Tests\Fakes\SimpleData;
use Spatie\LaravelData\Tests\Fakes\SimpleDto;
use Spatie\LaravelData\Tests\Fakes\SimpleResource;

use function Spatie\Snapshots\assertMatchesSnapshot;

it('also works by using traits and interfaces, skipping the base data class', function () {
$data = new class ('') implements Responsable, AppendableDataContract, BaseDataContract, TransformableDataContract, IncludeableDataContract, ResponsableDataContract, ValidateableDataContract, WrappableDataContract, EmptyDataContract {
use ResponsableData;
Expand Down Expand Up @@ -56,51 +53,6 @@ public static function fromString(string $string): static
});


it('can serialize and unserialize a data object', function () {
$object = SimpleData::from('Hello world');

$serialized = serialize($object);

assertMatchesSnapshot($serialized);

$unserialized = unserialize($serialized);

expect($unserialized)->toBeInstanceOf(SimpleData::class);
expect($unserialized->string)->toEqual('Hello world');
});

it('can serialize and unserialize a data object with additional data', function () {
$object = SimpleData::from('Hello world')->additional([
'int' => 69,
]);

$serialized = serialize($object);

assertMatchesSnapshot($serialized);

$unserialized = unserialize($serialized);

expect($unserialized)->toBeInstanceOf(SimpleData::class);
expect($unserialized->string)->toEqual('Hello world');
expect($unserialized->getAdditionalData())->toEqual(['int' => 69]);
});

it('during the serialization process some properties are thrown away', function () {
$object = SimpleData::from('Hello world');

$object->include('test');
$object->exclude('test');
$object->only('test');
$object->except('test');
$object->wrap('test');

$unserialized = unserialize(serialize($object));

$invaded = invade($unserialized);

expect($invaded->_dataContext)->toBeNull();
});

it('can use data as an DTO', function () {
$dto = SimpleDto::from('Hello World');

Expand Down
85 changes: 85 additions & 0 deletions tests/SerializeableTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php


use Spatie\LaravelData\DataCollection;
use Spatie\LaravelData\Lazy;
use Spatie\LaravelData\Support\Lazy\DefaultLazy;
use Spatie\LaravelData\Tests\Fakes\LazyData;
use Spatie\LaravelData\Tests\Fakes\SimpleData;

use function Spatie\Snapshots\assertMatchesSnapshot;

it('can serialize and unserialize a data object', function () {
$object = SimpleData::from('Hello world');

$serialized = serialize($object);

assertMatchesSnapshot($serialized);

$unserialized = unserialize($serialized);

expect($unserialized)->toBeInstanceOf(SimpleData::class);
expect($unserialized->string)->toEqual('Hello world');
});

it('can serialize and unserialize a data object with additional data', function () {
$object = SimpleData::from('Hello world')->additional([
'int' => 69,
]);

$serialized = serialize($object);

assertMatchesSnapshot($serialized);

$unserialized = unserialize($serialized);

expect($unserialized)->toBeInstanceOf(SimpleData::class);
expect($unserialized->string)->toEqual('Hello world');
expect($unserialized->getAdditionalData())->toEqual(['int' => 69]);
});

it('can serialize and unserialize a data collection', function () {
$collection = new DataCollection(SimpleData::class, ['A', 'B']);

$serialized = serialize($collection);

assertMatchesSnapshot($serialized);

$unserialized = unserialize($serialized);

expect($unserialized)->toBeInstanceOf(DataCollection::class);
expect($unserialized)->toEqual(new DataCollection(SimpleData::class, ['A', 'B']));
});

it('will keep context attached to data when serialized', function () {
$object = LazyData::from('Hello world')->include('name');

$unserialized = unserialize(serialize($object));

expect($unserialized)->toBeInstanceOf(LazyData::class);
expect($unserialized->toArray())->toMatchArray(['name' => 'Hello world']);
});

it('is possible to add partials with closures and serialize them', function () {
$object = LazyData::from('Hello world')->includeWhen(
'name',
fn (LazyData $data) => $data->name instanceof DefaultLazy
);

$unserialized = unserialize(serialize($object));

expect($unserialized)->toBeInstanceOf(LazyData::class);
expect($unserialized->toArray())->toMatchArray(['name' => 'Hello world']);
});

it('is possible to serialize conditional lazy properties', function () {
$object = new LazyData(Lazy::when(
fn () => true,
fn () => 'Hello world'
));

$unserialized = unserialize(serialize($object));

expect($unserialized)->toBeInstanceOf(LazyData::class);
expect($unserialized->toArray())->toMatchArray(['name' => 'Hello world']);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
some_camel_case_property: string;
'some:non:standard:property': string;
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading