Skip to content

Commit c469cb3

Browse files
committed
AfterMappingCallback
1 parent 267bb6d commit c469cb3

15 files changed

+551
-26
lines changed

CHANGELOG.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
99

1010
### Added
1111

12-
- `StringRule`
13-
- `trim` option to remove empty characters from start and end of the string
12+
- Callbacks
13+
- `AfterMappingCallback`
14+
- called after object is fully initialized, for additional validations
15+
- `AfterMapping` annotation/attribute
16+
- Rules
17+
- `StringRule`
18+
- `trim` option to remove empty characters from start and end of the string
1419

1520
### Changed
1621

docs/README.md

Lines changed: 88 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ of them to type-safe objects.
4343
- [All fields are required](#all-fields-are-required)
4444
- [No fields are required](#no-fields-are-required)
4545
- [Callbacks](#callbacks)
46-
- [Mapped object callbacks](#mapped-object-callbacks)
47-
- [Field callbacks](#field-callbacks)
46+
- [Validation object callbacks](#validation-object-callbacks)
47+
- [After mapping object callbacks](#after-mapping-object-callbacks)
48+
- [Validation field callbacks](#validation-field-callbacks)
4849
- [Returned value](#returned-value)
4950
- [Context](#callback-context)
5051
- [Dependencies](#dependencies)
@@ -1223,7 +1224,7 @@ use Orisai\ObjectMapper\Rules\MixedValue;
12231224
final class ListOfInput implements MappedObject
12241225
{
12251226

1226-
/** @var list<int, mixed> */
1227+
/** @var list<mixed> */
12271228
#[ListOf(new MixedValue())]
12281229
public array $field;
12291230

@@ -1253,7 +1254,7 @@ final class ListOfInput implements MappedObject
12531254
{
12541255

12551256
/**
1256-
* @var list<int, mixed>
1257+
* @var list<mixed>
12571258
* @ListOf(
12581259
* @MixedValue(),
12591260
* )
@@ -2224,13 +2225,13 @@ final class WithCallbackInput implements MappedObject
22242225
callbacks are called and overwrites any of set values.
22252226

22262227
In all callbacks are used [field names](#mapping-field-names-to-properties), not property names.
2227-
In [field callbacks](#field-callbacks), current field name can be accessed via [context](#callback-context).
2228+
In [field callbacks](#validation-field-callbacks), current field name can be accessed via [context](#callback-context).
22282229

22292230
Callbacks can be both static and non-static, object mapper initializes object to call non-static callbacks when needed.
22302231

22312232
Callbacks can have any visibility - public, protected or private.
22322233

2233-
### Mapped object callbacks
2234+
### Validation object callbacks
22342235

22352236
Modify and check data before and after processing fields with their rules
22362237

@@ -2319,7 +2320,83 @@ final class WithMappedObjectCallbacksInput implements MappedObject
23192320
```
23202321
</details>
23212322

2322-
### Field callbacks
2323+
### After mapping object callbacks
2324+
2325+
Validate object after being fully initialized
2326+
2327+
<details open>
2328+
<summary><code>#[Attributes()]</code></summary>
2329+
2330+
```php
2331+
use Orisai\ObjectMapper\Callbacks\AfterMapping;
2332+
use Orisai\ObjectMapper\Callbacks\Context\ObjectContext;
2333+
use Orisai\ObjectMapper\MappedObject;
2334+
use Orisai\ObjectMapper\Rules\ListOf;
2335+
use Orisai\ObjectMapper\Rules\StringValue;
2336+
2337+
#[AfterMapping('afterObject')]
2338+
final class AfterMappingCallbackInput implements MappedObject
2339+
{
2340+
2341+
/** @var list<string> */
2342+
#[ListOf(new StringValue())]
2343+
public array $allowed = [];
2344+
2345+
/** @var list<string> */
2346+
#[ListOf(new StringValue())]
2347+
public array $forbidden = [];
2348+
2349+
private function afterObject(ObjectContext $context): void
2350+
{
2351+
if ($this->allowed !== [] && $this->forbidden !== []) {
2352+
$context->getType()->addError("Specify either 'allowed' or 'forbidden', not both.");
2353+
}
2354+
}
2355+
2356+
}
2357+
```
2358+
</details>
2359+
2360+
<details>
2361+
<summary><code>@Annotations()</code></summary>
2362+
2363+
```php
2364+
use Orisai\ObjectMapper\Callbacks\AfterMapping;
2365+
use Orisai\ObjectMapper\Callbacks\Context\ObjectContext;
2366+
use Orisai\ObjectMapper\MappedObject;
2367+
use Orisai\ObjectMapper\Rules\ListOf;
2368+
use Orisai\ObjectMapper\Rules\StringValue;
2369+
2370+
/**
2371+
* @AfterMapping("afterObject")
2372+
*/
2373+
final class AfterMappingCallbackInput implements MappedObject
2374+
{
2375+
2376+
/**
2377+
* @var list<string>
2378+
* @ListOf(@StringValue())
2379+
*/
2380+
public array $allowed = [];
2381+
2382+
/**
2383+
* @var list<string>
2384+
* @ListOf(@StringValue())
2385+
*/
2386+
public array $forbidden = [];
2387+
2388+
private function afterObject(ObjectContext $context): void
2389+
{
2390+
if ($this->allowed !== [] && $this->forbidden !== []) {
2391+
$context->getType()->addError("Specify either 'allowed' or 'forbidden', not both.");
2392+
}
2393+
}
2394+
2395+
}
2396+
```
2397+
</details>
2398+
2399+
### Validation field callbacks
23232400

23242401
Modify and check data before and after processing field with its rule
23252402

@@ -2441,7 +2518,8 @@ $input = $processor->process(['field' => 'new value'], WithNotInvokedCallbackInp
24412518

24422519
### Returned value
24432520

2444-
Callbacks are by default expected to return a value:
2521+
[Validation object callbacks](#validation-object-callbacks) and
2522+
[validation field callbacks](#validation-field-callbacks) are by default expected to return a value:
24452523

24462524
<details open>
24472525
<summary><code>#[Attributes()]</code></summary>
@@ -2556,23 +2634,21 @@ final class WithNotReturningCallbackInput implements MappedObject
25562634

25572635
### Callback context
25582636

2559-
Both [mapped object callbacks](#mapped-object-callbacks) and [field callbacks](#field-callbacks) have additional context
2637+
[Validation object callbacks](#validation-object-callbacks) and [validation field callbacks](#validation-field-callbacks) have additional context
25602638
available as a second parameter, for extended processing:
25612639

25622640
Mapped object and field contexts
25632641

25642642
```php
25652643
$context->getProcessor(); // Processor
25662644
$context->getOptions(); // Options
2567-
$context->shouldMapDataToObjects(); // bool
2645+
$context->shouldInitializeObjects(); // bool
25682646
$context->getType(); // Type
25692647
```
25702648

25712649
Field context
25722650

25732651
```php
2574-
$context->hasDefaultValue(); // bool
2575-
$context->getDefaultValue(); // mixed|exception
25762652
$context->getFieldName(); // int|string
25772653
$context->getPropertyName(); // string
25782654
```

src/Callbacks/AfterMapping.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Orisai\ObjectMapper\Callbacks;
4+
5+
use Attribute;
6+
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
7+
use Doctrine\Common\Annotations\Annotation\Target;
8+
9+
/**
10+
* @Annotation
11+
* @NamedArgumentConstructor()
12+
* @Target({"CLASS"})
13+
*/
14+
#[Attribute(Attribute::TARGET_CLASS)]
15+
final class AfterMapping implements CallbackDefinition
16+
{
17+
18+
private string $method;
19+
20+
public function __construct(string $method)
21+
{
22+
$this->method = $method;
23+
}
24+
25+
public function getType(): string
26+
{
27+
return AfterMappingCallback::class;
28+
}
29+
30+
public function getArgs(): array
31+
{
32+
return [
33+
AfterMappingCallback::Method => $this->method,
34+
];
35+
}
36+
37+
}

0 commit comments

Comments
 (0)