2021-11-28 17:43:02 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
namespace CuyZ\Valinor\Tests\Integration\Mapping;
|
|
|
|
|
|
|
|
use CuyZ\Valinor\Mapper\MappingError;
|
|
|
|
use CuyZ\Valinor\Mapper\Tree\Node;
|
|
|
|
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message\FakeErrorMessage;
|
|
|
|
use CuyZ\Valinor\Tests\Integration\IntegrationTest;
|
|
|
|
use CuyZ\Valinor\Tests\Integration\Mapping\Fixture\SimpleObject;
|
|
|
|
|
|
|
|
final class VisitorMappingTest extends IntegrationTest
|
|
|
|
{
|
|
|
|
public function test_visitors_are_called_during_mapping(): void
|
|
|
|
{
|
|
|
|
$visits = [];
|
|
|
|
$error = new FakeErrorMessage();
|
|
|
|
|
|
|
|
try {
|
|
|
|
$this->mapperBuilder
|
|
|
|
->visit(function (Node $node) use (&$visits): void {
|
|
|
|
if ($node->isRoot()) {
|
|
|
|
$visits[] = '#1';
|
|
|
|
}
|
|
|
|
})
|
|
|
|
->visit(function (Node $node) use (&$visits): void {
|
|
|
|
if ($node->isRoot()) {
|
|
|
|
$visits[] = '#2';
|
|
|
|
}
|
|
|
|
})
|
|
|
|
->visit(function () use ($error): void {
|
|
|
|
throw $error;
|
|
|
|
})
|
|
|
|
->mapper()
|
|
|
|
->map(SimpleObject::class, ['value' => 'foo']);
|
|
|
|
} catch (MappingError $exception) {
|
feat!: add access to root node when error occurs during mapping
When an error occurs during mapping, the root instance of `Node` can now
be accessed from the exception. This recursive object allows retrieving
all needed information through the whole mapping tree: path, values,
types and messages, including the issues that caused the exception.
It can be used like the following:
```php
try {
(new \CuyZ\Valinor\MapperBuilder())
->mapper()
->map(SomeClass::class, [/* ... */]);
} catch (\CuyZ\Valinor\Mapper\MappingError $error) {
// Do something with `$error->node()`
// See README for more information
}
```
This change removes the method `MappingError::describe()` which provided
a flattened view of messages of all the errors that were encountered
during the mapping. The same behaviour can still be retrieved, see the
example below:
```php
use CuyZ\Valinor\Mapper\Tree\Message\Message;
use CuyZ\Valinor\Mapper\Tree\Node;
/**
* @implements \IteratorAggregate<string, array<\Throwable&Message>>
*/
final class MappingErrorList implements \IteratorAggregate
{
private Node $node;
public function __construct(Node $node)
{
$this->node = $node;
}
/**
* @return \Traversable<string, array<\Throwable&Message>>
*/
public function getIterator(): \Traversable
{
yield from $this->errors($this->node);
}
/**
* @return \Traversable<string, array<\Throwable&Message>>
*/
private function errors(Node $node): \Traversable
{
$errors = array_filter(
$node->messages(),
static fn (Message $m) => $m instanceof \Throwable
);
if (! empty($errors)) {
yield $node->path() => array_values($errors);
}
foreach ($node->children() as $child) {
yield from $this->errors($child);
}
}
}
try {
(new \CuyZ\Valinor\MapperBuilder())
->mapper()
->map(SomeClass::class, [/* ... */]);
} catch (\CuyZ\Valinor\Mapper\MappingError $error) {
$errors = iterator_to_array(new MappingErrorList($error->node()));
}
```
The class `CannotMapObject` is deleted, as it does not provide any
value; this means that `MappingError` which was previously an interface
becomes a class.
2021-12-16 00:00:45 +01:00
|
|
|
self::assertSame($error, $exception->node()->messages()[0]);
|
2021-11-28 17:43:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
self::assertSame(['#1', '#2'], $visits);
|
|
|
|
}
|
|
|
|
}
|