Skip to content

Commit 8027e81

Browse files
committed
AfterMappingCallback
1 parent 267bb6d commit 8027e81

15 files changed

+563
-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: 100 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,95 @@ 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\Exception\ValueDoesNotMatch;
2334+
use Orisai\ObjectMapper\MappedObject;
2335+
use Orisai\ObjectMapper\Processing\Value;
2336+
use Orisai\ObjectMapper\Rules\ListOf;
2337+
use Orisai\ObjectMapper\Rules\StringValue;
2338+
use Orisai\ObjectMapper\Types\MessageType;
2339+
2340+
#[AfterMapping('afterObject')]
2341+
final class AfterMappingCallbackInput implements MappedObject
2342+
{
2343+
2344+
/** @var list<string> */
2345+
#[ListOf(new StringValue())]
2346+
public array $allowed = [];
2347+
2348+
/** @var list<string> */
2349+
#[ListOf(new StringValue())]
2350+
public array $forbidden = [];
2351+
2352+
private function afterObject(ObjectContext $context): void
2353+
{
2354+
if ($this->allowed !== [] && $this->forbidden !== []) {
2355+
$context->getType()->addError(ValueDoesNotMatch::create(
2356+
new MessageType("Specify either 'allowed' or 'forbidden', not both."),
2357+
Value::none(),
2358+
));
2359+
}
2360+
}
2361+
2362+
}
2363+
```
2364+
</details>
2365+
2366+
<details>
2367+
<summary><code>@Annotations()</code></summary>
2368+
2369+
```php
2370+
use Orisai\ObjectMapper\Callbacks\AfterMapping;
2371+
use Orisai\ObjectMapper\Callbacks\Context\ObjectContext;
2372+
use Orisai\ObjectMapper\Exception\ValueDoesNotMatch;
2373+
use Orisai\ObjectMapper\MappedObject;
2374+
use Orisai\ObjectMapper\Processing\Value;
2375+
use Orisai\ObjectMapper\Rules\ListOf;
2376+
use Orisai\ObjectMapper\Rules\StringValue;
2377+
use Orisai\ObjectMapper\Types\MessageType;
2378+
2379+
/**
2380+
* @AfterMapping("afterObject")
2381+
*/
2382+
final class AfterMappingCallbackInput implements MappedObject
2383+
{
2384+
2385+
/**
2386+
* @var list<string>
2387+
* @ListOf(@StringValue())
2388+
*/
2389+
public array $allowed = [];
2390+
2391+
/**
2392+
* @var list<string>
2393+
* @ListOf(@StringValue())
2394+
*/
2395+
public array $forbidden = [];
2396+
2397+
private function afterObject(ObjectContext $context): void
2398+
{
2399+
if ($this->allowed !== [] && $this->forbidden !== []) {
2400+
$context->getType()->addError(ValueDoesNotMatch::create(
2401+
new MessageType("Specify either 'allowed' or 'forbidden', not both."),
2402+
Value::none(),
2403+
));
2404+
}
2405+
}
2406+
2407+
}
2408+
```
2409+
</details>
2410+
2411+
### Validation field callbacks
23232412

23242413
Modify and check data before and after processing field with its rule
23252414

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

24422531
### Returned value
24432532

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

24462536
<details open>
24472537
<summary><code>#[Attributes()]</code></summary>
@@ -2556,23 +2646,21 @@ final class WithNotReturningCallbackInput implements MappedObject
25562646

25572647
### Callback context
25582648

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

25622652
Mapped object and field contexts
25632653

25642654
```php
25652655
$context->getProcessor(); // Processor
25662656
$context->getOptions(); // Options
2567-
$context->shouldMapDataToObjects(); // bool
2657+
$context->shouldInitializeObjects(); // bool
25682658
$context->getType(); // Type
25692659
```
25702660

25712661
Field context
25722662

25732663
```php
2574-
$context->hasDefaultValue(); // bool
2575-
$context->getDefaultValue(); // mixed|exception
25762664
$context->getFieldName(); // int|string
25772665
$context->getPropertyName(); // string
25782666
```

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)