Skip to content

Commit ece40e4

Browse files
committed
update
1 parent d0845aa commit ece40e4

41 files changed

Lines changed: 681 additions & 856 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
3535
"doctrine/dbal": "^4.4.0",
3636
"doctrine/migrations": "^3.3.2",
37-
"patchlevel/hydrator": "^1.8.0",
37+
"patchlevel/hydrator": "^2.0.0",
3838
"patchlevel/worker": "^1.4.0",
3939
"psr/cache": "^2.0.0 || ^3.0.0",
4040
"psr/clock": "^1.0",

composer.lock

Lines changed: 13 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/UPGRADE-4.0.md

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,3 +327,277 @@ and replaced with the following headers:
327327

328328
The `Patchlevel\EventSourcing\Schema\DoctrineSchemaSubscriber` has been removed.
329329
use the `Patchlevel\EventSourcing\Schema\DoctrineSchemaListener` instead.
330+
331+
## Serializer
332+
333+
The library now uses `patchlevel/hydrator` 2.0. The `Patchlevel\Hydrator\MetadataHydrator`
334+
has been removed. Build a hydrator with the `StackHydratorBuilder` and the `CoreExtension` instead.
335+
Upcasting and crypto-shredding are no longer wired through the serializer factories,
336+
you register them on the hydrator as middleware or extension.
337+
338+
before:
339+
340+
```php
341+
use Patchlevel\Hydrator\Hydrator;
342+
use Patchlevel\Hydrator\MetadataHydrator;
343+
344+
$hydrator = new MetadataHydrator();
345+
```
346+
after:
347+
348+
```php
349+
use Patchlevel\Hydrator\CoreExtension;
350+
use Patchlevel\Hydrator\Hydrator;
351+
use Patchlevel\Hydrator\StackHydratorBuilder;
352+
353+
$hydrator = (new StackHydratorBuilder())
354+
->useExtension(new CoreExtension())
355+
->build();
356+
```
357+
358+
### DefaultEventSerializer
359+
360+
`createFromPaths()` no longer accepts an `$upcaster` or a `$cryptographer` argument.
361+
The second argument is now an optional `Hydrator`, a default one is built when it is `null`.
362+
Register upcasting via the `UpcastExtension` and crypto-shredding via the `CryptographyExtension`
363+
on the hydrator you pass in.
364+
365+
before:
366+
367+
```php
368+
use Patchlevel\EventSourcing\Serializer\DefaultEventSerializer;
369+
use Patchlevel\EventSourcing\Serializer\Upcast\Upcaster;
370+
use Patchlevel\Hydrator\Cryptography\PayloadCryptographer;
371+
372+
/**
373+
* @var Upcaster $upcaster
374+
* @var PayloadCryptographer $cryptographer
375+
*/
376+
$serializer = DefaultEventSerializer::createFromPaths(
377+
[__DIR__ . '/Events'],
378+
$upcaster,
379+
$cryptographer,
380+
);
381+
```
382+
after:
383+
384+
```php
385+
use Patchlevel\EventSourcing\Serializer\DefaultEventSerializer;
386+
use Patchlevel\Hydrator\CoreExtension;
387+
use Patchlevel\Hydrator\Extension\Cryptography\BaseCryptographer;
388+
use Patchlevel\Hydrator\Extension\Cryptography\CryptographyExtension;
389+
use Patchlevel\Hydrator\Extension\Cryptography\Store\CipherKeyStore;
390+
use Patchlevel\Hydrator\Extension\Upcast\Upcaster;
391+
use Patchlevel\Hydrator\Extension\Upcast\UpcastExtension;
392+
use Patchlevel\Hydrator\StackHydratorBuilder;
393+
394+
/**
395+
* @var Upcaster $upcaster
396+
* @var CipherKeyStore $cipherKeyStore
397+
*/
398+
$hydrator = (new StackHydratorBuilder())
399+
->useExtension(new CoreExtension())
400+
->useExtension(new CryptographyExtension(BaseCryptographer::createWithOpenssl($cipherKeyStore)))
401+
->useExtension(new UpcastExtension(beforeEncoding: [$upcaster]))
402+
->build();
403+
404+
$serializer = DefaultEventSerializer::createFromPaths(
405+
[__DIR__ . '/Events'],
406+
$hydrator,
407+
);
408+
```
409+
410+
### Upcasting
411+
412+
The event-sourcing upcasting classes have been removed in favor of the hydrator upcast extension:
413+
414+
* `Patchlevel\EventSourcing\Serializer\Upcast\Upcaster`
415+
* `Patchlevel\EventSourcing\Serializer\Upcast\Upcast`
416+
* `Patchlevel\EventSourcing\Serializer\Upcast\UpcasterChain`
417+
418+
Implement `Patchlevel\Hydrator\Extension\Upcast\Upcaster` (or use `CallbackUpcaster`) instead and register
419+
your upcasters with the `UpcastExtension`. The upcaster no longer receives an `Upcast` object, it now
420+
works on the payload array directly and selects the event by the class name from the metadata.
421+
Renaming an event through an upcaster is no longer possible, use event aliases instead.
422+
423+
before:
424+
425+
```php
426+
use Patchlevel\EventSourcing\Serializer\Upcast\Upcast;
427+
use Patchlevel\EventSourcing\Serializer\Upcast\Upcaster;
428+
429+
final class ProfileCreatedEmailLowerCastUpcaster implements Upcaster
430+
{
431+
public function __invoke(Upcast $upcast): Upcast
432+
{
433+
if ($upcast->eventName !== 'profile.created') {
434+
return $upcast;
435+
}
436+
437+
return $upcast->replacePayloadByKey('email', strtolower($upcast->payload['email']));
438+
}
439+
}
440+
```
441+
after:
442+
443+
```php
444+
use Patchlevel\Hydrator\Extension\Upcast\Upcaster;
445+
use Patchlevel\Hydrator\Metadata\ClassMetadata;
446+
447+
final class ProfileCreatedEmailLowerCastUpcaster implements Upcaster
448+
{
449+
/**
450+
* @param ClassMetadata<object> $metadata
451+
* @param array<string, mixed> $data
452+
* @param array<string, mixed> $context
453+
*
454+
* @return array<string, mixed>
455+
*/
456+
public function upcast(ClassMetadata $metadata, array $data, array $context): array
457+
{
458+
if ($metadata->className !== ProfileCreated::class) {
459+
return $data;
460+
}
461+
462+
$data['email'] = strtolower($data['email']);
463+
464+
return $data;
465+
}
466+
}
467+
```
468+
469+
### DefaultHeadersSerializer
470+
471+
The `$hydrator` argument of the constructor, `createFromPaths()` and `createDefault()`
472+
is now an optional `Hydrator` defaulting to `null`. The `MetadataHydrator` default has been removed.
473+
474+
## Snapshots
475+
476+
### DefaultSnapshotStore
477+
478+
The constructor no longer accepts an array of adapters as its first argument,
479+
it now requires an `AdapterRepository`. Pass adapters as an array through `createDefault()` instead.
480+
481+
before:
482+
483+
```php
484+
use Patchlevel\EventSourcing\Snapshot\DefaultSnapshotStore;
485+
486+
$snapshotStore = new DefaultSnapshotStore(['default' => $adapter]);
487+
```
488+
after:
489+
490+
```php
491+
use Patchlevel\EventSourcing\Snapshot\DefaultSnapshotStore;
492+
493+
$snapshotStore = DefaultSnapshotStore::createDefault(['default' => $adapter]);
494+
```
495+
496+
The `$cryptographer` argument of `createDefault()` has been replaced by an optional `Hydrator`.
497+
Build the hydrator with the `CryptographyExtension` like for the event serializer.
498+
499+
before:
500+
501+
```php
502+
use Patchlevel\EventSourcing\Snapshot\DefaultSnapshotStore;
503+
use Patchlevel\Hydrator\Cryptography\PayloadCryptographer;
504+
505+
/** @var PayloadCryptographer $cryptographer */
506+
$snapshotStore = DefaultSnapshotStore::createDefault(
507+
['default' => $adapter],
508+
$cryptographer,
509+
);
510+
```
511+
after:
512+
513+
```php
514+
use Patchlevel\EventSourcing\Snapshot\DefaultSnapshotStore;
515+
use Patchlevel\Hydrator\Hydrator;
516+
517+
/** @var Hydrator $hydrator */
518+
$snapshotStore = DefaultSnapshotStore::createDefault(
519+
['default' => $adapter],
520+
$hydrator,
521+
);
522+
```
523+
524+
## Sensitive Data
525+
526+
The crypto-shredding stack moved to the cryptography extension of `patchlevel/hydrator` 2.0.
527+
528+
### Attributes
529+
530+
The attributes moved namespace and `PersonalData` was renamed to `SensitiveData`:
531+
532+
* `Patchlevel\Hydrator\Attribute\DataSubjectId` -> `Patchlevel\Hydrator\Extension\Cryptography\Attribute\DataSubjectId`
533+
* `Patchlevel\Hydrator\Attribute\PersonalData` -> `Patchlevel\Hydrator\Extension\Cryptography\Attribute\SensitiveData`
534+
535+
before:
536+
537+
```php
538+
use Patchlevel\EventSourcing\Identifier\Uuid;
539+
use Patchlevel\Hydrator\Attribute\DataSubjectId;
540+
use Patchlevel\Hydrator\Attribute\PersonalData;
541+
542+
final class EmailChanged
543+
{
544+
public function __construct(
545+
#[DataSubjectId]
546+
public readonly Uuid $profileId,
547+
#[PersonalData(fallback: 'unknown')]
548+
public readonly string $email,
549+
) {
550+
}
551+
}
552+
```
553+
after:
554+
555+
```php
556+
use Patchlevel\EventSourcing\Identifier\Uuid;
557+
use Patchlevel\Hydrator\Extension\Cryptography\Attribute\DataSubjectId;
558+
use Patchlevel\Hydrator\Extension\Cryptography\Attribute\SensitiveData;
559+
560+
final class EmailChanged
561+
{
562+
public function __construct(
563+
#[DataSubjectId]
564+
public readonly Uuid $profileId,
565+
#[SensitiveData(fallback: 'unknown')]
566+
public readonly string $email,
567+
) {
568+
}
569+
}
570+
```
571+
572+
### DoctrineCipherKeyStore
573+
574+
The legacy `Patchlevel\EventSourcing\Cryptography\DoctrineCipherKeyStore` and
575+
`Patchlevel\EventSourcing\Cryptography\ExtensionDoctrineCipherKeyStore` have been merged into a
576+
single `Patchlevel\EventSourcing\Cryptography\DoctrineCipherKeyStore` that implements the new
577+
`Patchlevel\Hydrator\Extension\Cryptography\Store\CipherKeyStore`. The default table name changed
578+
from `crypto_keys` to `cryptography_keys`, and keys are now stored per id with a subject index.
579+
580+
To erase the data of a subject, call `removeWithSubjectId()`, the `remove()` method now deletes by key id.
581+
582+
before:
583+
584+
```php
585+
$cipherKeyStore->remove($subjectId);
586+
```
587+
after:
588+
589+
```php
590+
$cipherKeyStore->removeWithSubjectId($subjectId);
591+
```
592+
593+
:::danger
594+
The key table layout changed (`crypto_keys` -> `cryptography_keys` with new columns).
595+
Existing keys must be migrated, otherwise stored sensitive data can no longer be decrypted.
596+
:::
597+
598+
### Cryptographer
599+
600+
`Patchlevel\Hydrator\Cryptography\PayloadCryptographer` and its implementations
601+
(`PersonalDataPayloadCryptographer`, `SensitiveDataPayloadCryptographer`) have been removed.
602+
Create a `Patchlevel\Hydrator\Extension\Cryptography\BaseCryptographer` and register it on the
603+
hydrator through the `CryptographyExtension` (see the Serializer section above).

docs/introduction.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ powered by the reliable Doctrine ecosystem and focused on developer experience.
1111
* Automatic [snapshot](snapshots.md)-system to boost your performance
1212
* [Split](split-stream.md) big aggregates into multiple streams
1313
* Versioned and managed lifecycle of [subscriptions](subscription.md) like projections and processors
14-
* Safe usage of [Personal Data](personal-data.md) with crypto-shredding
14+
* Safe usage of [Personal Data](sensitive-data.md) with crypto-shredding
1515
* Smooth [upcasting](upcasting.md) of old events
1616
* Simple setup with [schema management](store.md) and [doctrine migration](store.md)
1717
* Built in [cli commands](cli.md) with [symfony](https://symfony.com/)

docs/normalizer.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ final class HotelCreated
100100
}
101101
```
102102
:::note
103-
If you have personal data, you can use [crypto-shredding](personal-data.md).
103+
If you have personal data, you can use [crypto-shredding](sensitive_data.md).
104104
:::
105105

106106
### Aggregate
@@ -459,4 +459,4 @@ final class DTO
459459
* [How to define aggregates](aggregate.md)
460460
* [How to define events](events.md)
461461
* [How to snapshot aggregates](snapshots.md)
462-
* [How to work with personal data](personal-data.md)
462+
* [How to work with personal data](sensitive-data.md)

0 commit comments

Comments
 (0)