You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -202,6 +202,38 @@ final readonly class AddressSerializer implements Serializer
202
202
203
203
Of course, Tempest provides casters and serializers for the most common data types, including arrays, booleans, dates, enumerations, integers and value objects.
204
204
205
+
### Registering casters and serializers globally
206
+
207
+
You may register casters and serializers globally, so you don't have to specify them for every property. This is useful for value objects that are used frequently. To do so, you may implement the {`\Tempest\Mapper\DynamicCaster`} or {`\Tempest\Mapper\DynamicSerializer`} interface, which require an `accepts` method:
208
+
209
+
```php app/AddressSerializer.php
210
+
use Tempest\Mapper\Serializer;
211
+
use Tempest\Mapper\DynamicSerializer;
212
+
213
+
final readonly class AddressSerializer implements Serializer, DynamicSerializer
214
+
{
215
+
public static function accepts(PropertyReflector|TypeReflector $input): bool
216
+
{
217
+
$type = $input instanceof PropertyReflector
218
+
? $input->getType()
219
+
: $input;
220
+
221
+
return $type->matches(Address::class);
222
+
}
223
+
224
+
public function serialize(mixed $input): array|string
225
+
{
226
+
if (! $input instanceof Address) {
227
+
throw new CannotSerializeValue(Address::class);
228
+
}
229
+
230
+
return $input->toArray();
231
+
}
232
+
}
233
+
```
234
+
235
+
Dynamic serializers and casters will automatically be discovered by Tempest.
236
+
205
237
### Specifying casters or serializers for properties
206
238
207
239
You may use a specific caster or serializer for a property by using the {b`#[Tempest\Mapper\CastWith]`} or {b`#[Tempest\Mapper\SerializeWith]`} attribute, respectively.
@@ -218,21 +250,147 @@ final class User
218
250
219
251
You may of course use {b`#[Tempest\Mapper\CastWith]`} and {b`#[Tempest\Mapper\SerializeWith]`} together.
220
252
221
-
### Registering casters and serializers globally
253
+
##Mapping contexts
222
254
223
-
You may register casters and serializers globally, so you don't have to specify them for every property. This is useful for value objects that are used frequently.
255
+
Contexts allow you to use different casters, serializers, and mappers depending on the situation. For example, you might want to serialize dates differently for an API response versus database storage, or apply different validation rules for different contexts.
256
+
257
+
### Using contexts
258
+
259
+
You may specify a context when mapping by using the `in()` method. Contexts can be provided as a string, an enum, or a {b`\Tempest\Mapper\Context`} object.
final readonly class ApiDateSerializer implements Serializer, DynamicSerializer
281
+
{
282
+
public static function accepts(PropertyReflector|TypeReflector $input): bool
283
+
{
284
+
$type = $input instanceof PropertyReflector
285
+
? $input->getType()
286
+
: $input;
287
+
288
+
return $type->matches(DateTime::class);
289
+
}
290
+
291
+
public function serialize(mixed $input): string
292
+
{
293
+
return $input->format(FormatPattern::ISO8601);
294
+
}
295
+
}
236
296
```
237
297
238
-
If you're looking for the right place where to put this logic, [provider classes](/docs/extra-topics/package-development#provider-classes) is our recommendation.
298
+
This serializer will only be used when mapping with `->in(SerializationContext::API)`. Without a context specified, or in other contexts, the default serializers will be used.
299
+
300
+
### Injecting context into casters and serializers
301
+
302
+
You may inject the current context into your caster or serializer constructor to adapt behavior dynamically. Note that the context property has to be named `$context`. You may also inject any other dependency from the container.
303
+
304
+
```php
305
+
use Tempest\Mapper\Context;
306
+
use Tempest\Mapper\Serializer;
307
+
308
+
#[Context(DatabaseContext::class)]
309
+
final class BooleanSerializer implements Serializer, DynamicSerializer
310
+
{
311
+
public function __construct(
312
+
private DatabaseContext $context,
313
+
) {}
314
+
315
+
public static function accepts(PropertyReflector|TypeReflector $type): bool
Sometimes, a caster or serializer needs to be configured based on the property it's applied to. For example, an enum caster needs to know which enum class to use, or an object caster needs to know the target type.
337
+
338
+
Implement the {b`\Tempest\Mapper\ConfigurableCaster`} or {b`\Tempest\Mapper\ConfigurableSerializer`} interface to create casters/serializers that are configured per property:
339
+
340
+
```php
341
+
use Tempest\Mapper\Caster;
342
+
use Tempest\Mapper\ConfigurableCaster;
343
+
use Tempest\Mapper\Context;
344
+
use Tempest\Mapper\DynamicCaster;
345
+
use Tempest\Reflection\PropertyReflector;
346
+
347
+
final readonly class EnumCaster implements Caster, DynamicCaster, ConfigurableCaster
348
+
{
349
+
/**
350
+
* @param class-string<UnitEnum> $enum
351
+
*/
352
+
public function __construct(
353
+
private string $enum,
354
+
) {}
355
+
356
+
public static function accepts(PropertyReflector|TypeReflector $input): bool
357
+
{
358
+
$type = $input instanceof PropertyReflector
359
+
? $input->getType()
360
+
: $input;
361
+
362
+
return $type->matches(UnitEnum::class);
363
+
}
364
+
365
+
public static function configure(PropertyReflector $property, Context $context): self
366
+
{
367
+
// Create a new instance configured for this specific property
368
+
return new self(enum: $property->getType()->getName());
369
+
}
370
+
371
+
public function cast(mixed $input): ?object
372
+
{
373
+
if ($input === null) {
374
+
return null;
375
+
}
376
+
377
+
// Use the configured enum class
378
+
return $this->enum::from($input);
379
+
}
380
+
}
381
+
```
382
+
383
+
The `configure()` method receives the property being mapped and the current context, allowing you to create a caster instance tailored to that specific property.
384
+
385
+
Note that `ConfigurableSerializer::configure()` can receive either a `PropertyReflector`, `TypeReflector`, or `string`, depending on whether it's being used for property mapping or value serialization.
386
+
387
+
### When to use configurable casters and serializers
388
+
389
+
Use configurable casters and serializers when:
390
+
391
+
- The caster/serializer behavior depends on the specific property type (e.g., enum class, object class)
392
+
- You need access to property attributes or metadata
393
+
- Different properties of the same base type require different handling
394
+
- You want to avoid creating many similar caster/serializer classes
395
+
396
+
For simple, static behavior that doesn't depend on property information, regular casters and serializers are sufficient.
0 commit comments