11# Extensions
22
33The ` StackHydrator ` is assembled from small building blocks: middlewares that
4- wrap the hydration process, [ guessers] ( guesser.md ) that resolve normalizers and
5- metadata enrichers that add information to the class metadata. An extension
6- bundles such building blocks so they can be registered with a single call.
4+ wrap the hydration process, [ guessers] ( guesser.md ) that resolve normalizers,
5+ metadata enrichers that add information to the class metadata and transformer
6+ factories that map the data to the object. An extension bundles such building
7+ blocks so they can be registered with a single call.
78
89## Using extensions
910
1011Extensions are registered on the ` StackHydratorBuilder ` with ` useExtension ` .
11- The ` CoreExtension ` provides the default behaviour and should (almost) always
12- be there.
12+ The default behaviour, the property mapping and the
13+ [ built-in guesser] ( guesser.md#built-in-guesser ) , is always there, you don't
14+ have to register anything for it.
1315
1416``` php
15- use Patchlevel\Hydrator\CoreExtension;
1617use Patchlevel\Hydrator\Extension\Lifecycle\LifecycleExtension;
1718use Patchlevel\Hydrator\StackHydratorBuilder;
1819
1920$hydrator = (new StackHydratorBuilder())
20- ->useExtension(new CoreExtension())
2121 ->useExtension(new LifecycleExtension())
2222 ->build();
2323```
2424## Built-in extensions
2525
26- The library ships with four extensions out of the box:
26+ The library ships with three extensions out of the box:
2727
2828| Extension | Purpose |
2929| --- | --- |
30- | ` CoreExtension ` | The default behaviour, the ` TransformMiddleware ` and the ` BuiltInGuesser ` . |
3130| ` LifecycleExtension ` | [ Lifecycle hooks] ( lifecycle-hooks.md ) , run code before and after the extract and hydrate process. |
3231| ` CryptographyExtension ` | [ Cryptography] ( cryptography.md ) , encrypt and decrypt sensitive data with crypto-shredding. |
3332| ` UpcastExtension ` | [ Upcasting] ( upcasting.md ) , reshape outdated stored data while it is hydrated. |
@@ -36,8 +35,9 @@ The library ships with four extensions out of the box:
3635
3736A middleware wraps the hydration and extraction process, similar to HTTP
3837middlewares. It can modify the incoming data, the outgoing array or the object
39- itself, and passes the call on to the rest of the stack with ` $next ` . The innermost
40- middleware is the ` TransformMiddleware ` , which does the actual property mapping.
38+ itself, and passes the call on to the rest of the stack with ` $next ` . After the last
39+ middleware the [ transformer] ( #transformer-factories ) does the actual property
40+ mapping.
4141
4242``` php
4343use Patchlevel\Hydrator\Metadata\ClassMetadata;
@@ -60,8 +60,7 @@ final class RemoveNullValuesMiddleware implements Middleware
6060}
6161```
6262Middlewares are added with a priority, higher priorities run first (outermost).
63- The ` TransformMiddleware ` from the ` CoreExtension ` has priority ` -64 ` , so it
64- always runs last.
63+ The transformation always runs last, after all middlewares.
6564
6665``` php
6766$builder->addMiddleware(new RemoveNullValuesMiddleware(), 0);
@@ -113,9 +112,9 @@ upcaster is a `CallbackUpcaster` or carries an
113112[ ` #[UpcasterFor] ` ] ( upcasting.md#writing-an-upcaster ) attribute, it is also left
114113out while hydrating classes none of them target.
115114
116- ::: warning
117- At least one middleware has to run. If every middleware skips a class, an
118- ` AllMiddlewaresSkipped ` exception is thrown .
115+ ::: tip
116+ If every middleware skips a class, the hydrator calls the transformer directly
117+ without building the middleware stack .
119118:::
120119
121120## Metadata enricher
@@ -152,6 +151,39 @@ extras) can be [cached](caching.md), everything you store in `extras` must be
152151serializable.
153152:::
154153
154+ ## Transformer factories
155+
156+ At the end of the stack a ` ClassTransformer ` turns the array into the object
157+ and back. By default this is the ` ReflectionTransformer ` , which sets and reads
158+ the properties with reflection. A transformer factory can provide another
159+ transformer for a class, for example generated code.
160+
161+ ``` php
162+ use Patchlevel\Hydrator\Metadata\ClassMetadata;
163+ use Patchlevel\Hydrator\Transformer\ClassTransformer;
164+ use Patchlevel\Hydrator\Transformer\ClassTransformerFactory;
165+
166+ final class MoneyTransformerFactory implements ClassTransformerFactory
167+ {
168+ public function create(ClassMetadata $metadata): ClassTransformer|null
169+ {
170+ if ($metadata->className !== Money::class) {
171+ return null;
172+ }
173+
174+ return new MoneyTransformer();
175+ }
176+ }
177+ ```
178+ ``` php
179+ $builder->addTransformerFactory(new MoneyTransformerFactory());
180+ ```
181+ The factories are asked once per class, the first transformer wins and is
182+ cached by the hydrator. Factories also accept a priority, a factory with a
183+ higher priority is asked first. The ` ReflectionTransformerFactory ` is always
184+ asked last, so every class without its own transformer is transformed with
185+ reflection.
186+
155187## Writing your own extension
156188
157189An extension implements the ` Extension ` interface and configures the builder.
0 commit comments