Serializing or Normalizing of DataCollection #450
-
AboutFirst of all, thank you so much for the package and the amazing course, it's been an absolute pleasure working with! I've gotten to a point where I'm using Cannot create an instance of "Spatie\LaravelData\DataCollection" from serialized data because its constructor requires the following parameters to be present : "$dataClass"Spatie\EventSourcing\StoredEvents\Exceptions\InvalidStoredEvent: Failed to unserialize an event with class `App\Domain\Resource\Events\ResourceIdentified` on stored event with id `3`. Are you sure that event class exists? at vendor/symfony/serializer/Normalizer/AbstractNormalizer.php:414 at vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:246 at vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:346 at vendor/symfony/serializer/Serializer.php:246 at vendor/symfony/serializer/Normalizer/AbstractNormalizer.php:465 at vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:626 at vendor/symfony/serializer/Normalizer/AbstractNormalizer.php:377 at vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:246 at vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:346 at vendor/symfony/serializer/Serializer.php:246 at vendor/symfony/serializer/Normalizer/AbstractNormalizer.php:465 at vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:626 at vendor/symfony/serializer/Normalizer/AbstractNormalizer.php:377 at vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:246 at vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:346 at vendor/symfony/serializer/Serializer.php:246 at vendor/symfony/serializer/Serializer.php:152 at vendor/spatie/laravel-event-sourcing/src/EventSerializers/JsonEventSerializer.php:42 at vendor/spatie/laravel-event-sourcing/src/StoredEvents/StoredEvent.php:131 at vendor/spatie/laravel-event-sourcing/src/StoredEvents/StoredEvent.php:48 at vendor/spatie/laravel-event-sourcing/src/StoredEvents/Models/EloquentStoredEvent.php:32 at vendor/spatie/laravel-event-sourcing/src/StoredEvents/Repositories/EloquentStoredEventRepository.php:44 at vendor/laravel/framework/src/Illuminate/Collections/LazyCollection.php:790 at vendor/laravel/framework/src/Illuminate/Collections/Traits/EnumeratesValues.php:239 at vendor/spatie/laravel-event-sourcing/src/AggregateRoots/AggregateRoot.php:253 at vendor/spatie/laravel-event-sourcing/src/AggregateRoots/AggregateRoot.php:55 at app/Domain/Resource/Actions/CraftTape.php:30 at vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:36 at vendor/laravel/framework/src/Illuminate/Container/Util.php:41 at vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:93 at vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:35 at vendor/laravel/framework/src/Illuminate/Container/Container.php:662 at vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php:128 at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:144 at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:119 at vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php:132 at vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php:98 at vendor/laravel/framework/src/Illuminate/Foundation/Bus/Dispatchable.php:76 at app/Domain/Resource/Reactors/ResourceIdentifiedReactor.php:16 at vendor/spatie/laravel-event-sourcing/src/EventHandlers/HandlesEvents.php:36 at vendor/laravel/framework/src/Illuminate/Collections/Traits/EnumeratesValues.php:240 at vendor/spatie/laravel-event-sourcing/src/EventHandlers/HandlesEvents.php:35 at vendor/spatie/laravel-event-sourcing/src/Projectionist.php:298 at vendor/spatie/laravel-event-sourcing/src/Projectionist.php:275 at vendor/spatie/laravel-event-sourcing/src/Projectionist.php:253 at vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:355 at vendor/spatie/laravel-event-sourcing/src/StoredEvents/StoredEvent.php:77 at vendor/spatie/laravel-event-sourcing/src/StoredEvents/StoredEvent.php:72 at vendor/spatie/laravel-event-sourcing/src/AggregateRoots/AggregateRoot.php:126 at vendor/laravel/framework/src/Illuminate/Collections/Traits/EnumeratesValues.php:240 at vendor/spatie/laravel-event-sourcing/src/AggregateRoots/AggregateRoot.php:126 at app/Domain/Resource/Actions/InspectResource.php:33 at vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:36 at vendor/laravel/framework/src/Illuminate/Container/Util.php:41 at vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:93 at vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:35 at vendor/laravel/framework/src/Illuminate/Container/Container.php:662 at vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php:128 at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:144 at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:119 at vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php:132 at vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php:98 at vendor/laravel/framework/src/Illuminate/Foundation/Bus/Dispatchable.php:76 at tests/Domain/Resource/Actions/InspectResourceTest.php:23 at vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:176 What I'm tryingI'm now looking into how I can add my own normalizer, although new territory for me. :) @sebastiandedeyne do you by any case already have a DataCollection normalizer written at Spatie? Otherwise I'd gladly contribute mine, once I'm done with it. Would be very handy to have it as a part of the package, as a compatibility layer for My data object looks like this: class ResourceIdentity extends Data
{
public function __construct(
#[DataCollectionOf(ResourceStream::class)]
public DataCollection $streams,
public ResourceFormat $format,
) {
}
} And my stored event looks like the following: class ResourceIdentified extends ShouldBeStored
{
public function __construct(
public string $resourceId,
public string $source,
public ResourceIdentity $identity,
) {
}
} Thank you for the many years of package-making, and can't wait to celebrate the launch with a postcard! 🎉 |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 2 replies
-
Hi Jakob, Unfortunately I don't have time for an in-depth answer now. In one of our client projects we're in the process of using Laravel Data for all stored events with a custom serializer. We should have it complete in January, and I'll attempt to document it somewhere after. But, here's how we're planning to use Laravel Data with this package: 1. Each stored event will be a data object. The data package can also be used with a trait instead of inheritance. use Spatie\EventSourcing\StoredEvents\ShouldBeStored;
use Spatie\LaravelData\Concerns\DataTrait;
class ResourceIdentified extends ShouldBeStored
{
use DataTrait;
public function __construct(
public string $resourceId,
public string $source,
public ResourceIdentity $identity,
) {
}
} 2. A custom serializer for data events. Since we can defer all serialization to Laravel Data, this will be a light class. (This is not real code, just off the top of my head.) class JsonSerializer implements EventSerializer
{
public function serialize(ShouldBeStored $event): string
{
return $event->toJson();
}
public function deserialize(string $eventClass, string $json, int $version, string $metadata = null): ShouldBeStored
{
return $eventClass::from(json_decode($json, true));
} With the right transformers & casts, the data event objects should be serializable/deserializable without too much custom logic. |
Beta Was this translation helpful? Give feedback.
-
Thank you @sebastiandedeyne, that was one hell of an easy fix! ApproachI ended up creating an abstract namespace App\Domain;
use Spatie\EventSourcing\StoredEvents\ShouldBeStored;
use Spatie\LaravelData\Concerns\DataTrait;
use Spatie\LaravelData\Contracts\DataObject;
abstract class Event extends ShouldBeStored implements DataObject
{
use DataTrait;
} This means my event is almost identical, but extends new abstract use App\Domain\Event;
use App\Domain\Resource\DataObjects\ResourceIdentity;
class ResourceIdentified extends Event
{
public function __construct(
public string $resourceId,
public string $source,
public ResourceIdentity $identity,
) {
}
} And finally, the super simple serializer, which is 1-1 with what you posted, except a wee-bit of type hinting: use App\Domain\Event;
use Spatie\EventSourcing\EventSerializers\EventSerializer;
use Spatie\EventSourcing\StoredEvents\ShouldBeStored;
class DataEventSerializer implements EventSerializer
{
public function serialize(Event|ShouldBeStored $event): string
{
return $event->toJson();
}
public function deserialize(string $eventClass, string $json, int $version, ?string $metadata = null): Event|ShouldBeStored
{
return $eventClass::from(json_decode($json, true));
}
} ConclusionIt's a greenfield project, and I've quite strictly followed the conventions set by the What I learned was that implementing I had one DTO that was still a POPO, but after having that extend the Wooh! 🎉
|
Beta Was this translation helpful? Give feedback.
-
Thanks @sebastiandedeyne and @jstoone for the solution, it works great! For anyone looking to implement this without going through all the existing events in your project, it's possible to support both use Spatie\EventSourcing\EventSerializers\JsonEventSerializer;
use Spatie\EventSourcing\StoredEvents\ShouldBeStored;
class DataEventSerializer extends JsonEventSerializer
{
public function serialize(Event|ShouldBeStored $event): string
{
if ($event instanceof Event) {
return $event->toJson();
}
return parent::serialize($event);
}
public function deserialize(string $eventClass, string $json, int $version, ?string $metadata = null): Event|ShouldBeStored
{
if (is_subclass_of($eventClass, Event::class)) {
return $eventClass::from(json_decode($json, true));
}
return parent::deserialize($eventClass, $json, $version, $metadata);
}
} |
Beta Was this translation helpful? Give feedback.
Hi Jakob,
Unfortunately I don't have time for an in-depth answer now. In one of our client projects we're in the process of using Laravel Data for all stored events with a custom serializer. We should have it complete in January, and I'll attempt to document it somewhere after.
But, here's how we're planning to use Laravel Data with this package:
1. Each stored event will be a data object. The data package can also be used with a trait instead of inheritance.