Skip to content

Commit

Permalink
AfterMappingCallback
Browse files Browse the repository at this point in the history
  • Loading branch information
mabar committed Feb 22, 2025
1 parent 267bb6d commit c469cb3
Show file tree
Hide file tree
Showing 15 changed files with 551 additions and 26 deletions.
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

### Added

- `StringRule`
- `trim` option to remove empty characters from start and end of the string
- Callbacks
- `AfterMappingCallback`
- called after object is fully initialized, for additional validations
- `AfterMapping` annotation/attribute
- Rules
- `StringRule`
- `trim` option to remove empty characters from start and end of the string

### Changed

Expand Down
100 changes: 88 additions & 12 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ of them to type-safe objects.
- [All fields are required](#all-fields-are-required)
- [No fields are required](#no-fields-are-required)
- [Callbacks](#callbacks)
- [Mapped object callbacks](#mapped-object-callbacks)
- [Field callbacks](#field-callbacks)
- [Validation object callbacks](#validation-object-callbacks)
- [After mapping object callbacks](#after-mapping-object-callbacks)
- [Validation field callbacks](#validation-field-callbacks)
- [Returned value](#returned-value)
- [Context](#callback-context)
- [Dependencies](#dependencies)
Expand Down Expand Up @@ -1223,7 +1224,7 @@ use Orisai\ObjectMapper\Rules\MixedValue;
final class ListOfInput implements MappedObject
{

/** @var list<int, mixed> */
/** @var list<mixed> */
#[ListOf(new MixedValue())]
public array $field;

Expand Down Expand Up @@ -1253,7 +1254,7 @@ final class ListOfInput implements MappedObject
{

/**
* @var list<int, mixed>
* @var list<mixed>
* @ListOf(
* @MixedValue(),
* )
Expand Down Expand Up @@ -2224,13 +2225,13 @@ final class WithCallbackInput implements MappedObject
callbacks are called and overwrites any of set values.

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

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

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

### Mapped object callbacks
### Validation object callbacks

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

Expand Down Expand Up @@ -2319,7 +2320,83 @@ final class WithMappedObjectCallbacksInput implements MappedObject
```
</details>

### Field callbacks
### After mapping object callbacks

Validate object after being fully initialized

<details open>
<summary><code>#[Attributes()]</code></summary>

```php
use Orisai\ObjectMapper\Callbacks\AfterMapping;
use Orisai\ObjectMapper\Callbacks\Context\ObjectContext;
use Orisai\ObjectMapper\MappedObject;
use Orisai\ObjectMapper\Rules\ListOf;
use Orisai\ObjectMapper\Rules\StringValue;

#[AfterMapping('afterObject')]
final class AfterMappingCallbackInput implements MappedObject
{

/** @var list<string> */
#[ListOf(new StringValue())]
public array $allowed = [];

/** @var list<string> */
#[ListOf(new StringValue())]
public array $forbidden = [];

private function afterObject(ObjectContext $context): void
{
if ($this->allowed !== [] && $this->forbidden !== []) {
$context->getType()->addError("Specify either 'allowed' or 'forbidden', not both.");
}
}

}
```
</details>

<details>
<summary><code>@Annotations()</code></summary>

```php
use Orisai\ObjectMapper\Callbacks\AfterMapping;
use Orisai\ObjectMapper\Callbacks\Context\ObjectContext;
use Orisai\ObjectMapper\MappedObject;
use Orisai\ObjectMapper\Rules\ListOf;
use Orisai\ObjectMapper\Rules\StringValue;

/**
* @AfterMapping("afterObject")
*/
final class AfterMappingCallbackInput implements MappedObject
{

/**
* @var list<string>
* @ListOf(@StringValue())
*/
public array $allowed = [];

/**
* @var list<string>
* @ListOf(@StringValue())
*/
public array $forbidden = [];

private function afterObject(ObjectContext $context): void
{
if ($this->allowed !== [] && $this->forbidden !== []) {
$context->getType()->addError("Specify either 'allowed' or 'forbidden', not both.");
}
}

}
```
</details>

### Validation field callbacks

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

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

### Returned value

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

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

### Callback context

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

Mapped object and field contexts

```php
$context->getProcessor(); // Processor
$context->getOptions(); // Options
$context->shouldMapDataToObjects(); // bool
$context->shouldInitializeObjects(); // bool
$context->getType(); // Type
```

Field context

```php
$context->hasDefaultValue(); // bool
$context->getDefaultValue(); // mixed|exception
$context->getFieldName(); // int|string
$context->getPropertyName(); // string
```
Expand Down
37 changes: 37 additions & 0 deletions src/Callbacks/AfterMapping.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php declare(strict_types = 1);

namespace Orisai\ObjectMapper\Callbacks;

use Attribute;
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
use Doctrine\Common\Annotations\Annotation\Target;

/**
* @Annotation
* @NamedArgumentConstructor()
* @Target({"CLASS"})
*/
#[Attribute(Attribute::TARGET_CLASS)]
final class AfterMapping implements CallbackDefinition
{

private string $method;

public function __construct(string $method)
{
$this->method = $method;
}

public function getType(): string
{
return AfterMappingCallback::class;
}

public function getArgs(): array
{
return [
AfterMappingCallback::Method => $this->method,
];
}

}
Loading

0 comments on commit c469cb3

Please sign in to comment.