Skip to content

Commit 88ba6b8

Browse files
Merge pull request #923 from spatie/fix/903
Fix an issue where anonymous classes in castables were serialized (#903)
2 parents 23cf3b0 + d162faa commit 88ba6b8

File tree

4 files changed

+80
-2
lines changed

4 files changed

+80
-2
lines changed

src/Attributes/WithCastable.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Attribute;
66
use Spatie\LaravelData\Casts\Cast;
77
use Spatie\LaravelData\Casts\Castable;
8+
use Spatie\LaravelData\Casts\CastableCast;
89
use Spatie\LaravelData\Exceptions\CannotCreateCastAttribute;
910

1011
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY)]
@@ -26,6 +27,9 @@ public function __construct(
2627

2728
public function get(): Cast
2829
{
29-
return $this->castableClass::dataCastUsing(...$this->arguments);
30+
return new CastableCast(
31+
$this->castableClass,
32+
$this->arguments
33+
);
3034
}
3135
}

src/Casts/CastableCast.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Spatie\LaravelData\Casts;
4+
5+
use Spatie\LaravelData\Support\Creation\CreationContext;
6+
use Spatie\LaravelData\Support\DataProperty;
7+
8+
class CastableCast implements Cast
9+
{
10+
protected Cast $cast;
11+
12+
/**
13+
* @param class-string<\Spatie\LaravelData\Casts\Castable> $castableClass
14+
*/
15+
public function __construct(
16+
public string $castableClass,
17+
public array $arguments
18+
) {
19+
}
20+
21+
public function cast(DataProperty $property, mixed $value, array $properties, CreationContext $context): mixed
22+
{
23+
if (! isset($this->cast)) {
24+
$this->cast = $this->castableClass::dataCastUsing(...$this->arguments);
25+
}
26+
27+
return $this->cast->cast($property, $value, $properties, $context);
28+
}
29+
30+
public function __serialize(): array
31+
{
32+
return [
33+
'castableClass' => $this->castableClass,
34+
'arguments' => $this->arguments,
35+
];
36+
}
37+
38+
public function __unserialize(array $data): void
39+
{
40+
$this->castableClass = $data['castableClass'];
41+
$this->arguments = $data['arguments'];
42+
}
43+
}

src/Support/Factories/DataClassFactory.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ public function __construct(
3333
) {
3434
}
3535

36-
3736
public function build(ReflectionClass $reflectionClass): DataClass
3837
{
3938
/** @var class-string<Data> $name */

tests/Support/Caching/CachedDataConfigTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33
use Illuminate\Support\Facades\App;
44
use Illuminate\Support\Facades\Cache;
55
use Mockery\MockInterface;
6+
use Spatie\LaravelData\Attributes\WithCastable;
7+
use Spatie\LaravelData\Data;
68
use Spatie\LaravelData\Support\Caching\CachedDataConfig;
79
use Spatie\LaravelData\Support\Caching\DataStructureCache;
810
use Spatie\LaravelData\Support\DataClass;
911
use Spatie\LaravelData\Support\DataConfig;
1012
use Spatie\LaravelData\Tests\Factories\FakeDataStructureFactory;
13+
use Spatie\LaravelData\Tests\Fakes\Castables\SimpleCastable;
1114
use Spatie\LaravelData\Tests\Fakes\SimpleData;
1215

1316
function ensureDataWillBeCached()
@@ -104,3 +107,32 @@ function (MockInterface $spy) use ($dataClass) {
104107

105108
cache()->get('something-just-to-test-the-mock');
106109
});
110+
111+
it('is possible to cache data classes with castables using anonymous classes', function () {
112+
ensureDataWillBeCached();
113+
114+
$objectDefinition = new class () extends Data {
115+
#[WithCastable(SimpleCastable::class, normalize: true)]
116+
public SimpleCastable $string;
117+
};
118+
119+
$dataClass = app(DataConfig::class)->getDataClass($objectDefinition::class);
120+
121+
expect(isset(invade($dataClass->properties['string']->cast)->cast))->toBeFalse();
122+
123+
$objectDefinition::from(['string' => 'Hello world']);
124+
125+
$dataClass = app(DataConfig::class)->getDataClass($objectDefinition::class);
126+
127+
$reflection = new ReflectionClass(invade($dataClass->properties['string']->cast)->cast);
128+
129+
expect($reflection->isAnonymous())->toBeTrue();
130+
131+
$dataClass->prepareForCache();
132+
133+
app(DataStructureCache::class)->storeDataClass($dataClass);
134+
135+
$newDataClass = app(DataStructureCache::class)->getDataClass($objectDefinition::class);
136+
137+
expect(isset(invade($newDataClass->properties['string']->cast)->cast))->toBeFalse();
138+
});

0 commit comments

Comments
 (0)