跳至正文

推断接口的实现类型

当映射器遇到一个接口时,它需要了解将使用哪个实现。这可以通过为接口注册构造函数来完成,但它可能有限制。处理此问题的更有效方法是根据提供给映射器的数据推断实现。这可以使用infer()方法来完成。

给予此方法的回调必须返回实现该接口的类的名称。回调可以需要任何参数;它们将使用给定的源正确映射。

如果回调可以返回多个类名,则需要提供返回签名以及可以返回的所有类字符串的列表。

$mapper = (new \CuyZ\Valinor\MapperBuilder())
    ->infer(UuidInterface::class, fn () => MyUuid::class)
    ->infer(
        SomeInterface::class, 
        /** @return class-string<FirstImplementation|SecondImplementation> */
        fn (string $type) => match($type) {
            'first' => FirstImplementation::class,
            'second' => SecondImplementation::class,
            default => throw new DomainException("Unhandled type `$type`.")
        }
    )->mapper();

// Will return an instance of `FirstImplementation`
$mapper->map(SomeInterface::class, [
    'type' => 'first',
    'uuid' => 'a6868d61-acba-406d-bcff-30ecd8c0ceb6',
    'someString' => 'foo',
]);

// Will return an instance of `SecondImplementation`
$mapper->map(SomeInterface::class, [
    'type' => 'second',
    'uuid' => 'a6868d61-acba-406d-bcff-30ecd8c0ceb6',
    'someInt' => 42,
]);

interface SomeInterface {}

final readonly class FirstImplementation implements SomeInterface
{
    public UuidInterface $uuid;

    public string $someString;
}

final readonly class SecondImplementation implements SomeInterface
{
    public UuidInterface $uuid;

    public int $someInt;
}

推断类

相同的机制可以应用于推断抽象类或父类。

抽象类的示例:

abstract readonly class SomeAbstractClass
{
    public string $foo;

    public string $bar;
}

final readonly class SomeChildClass extends SomeAbstractClass
{
    public string $baz;
}

$result = (new \CuyZ\Valinor\MapperBuilder())
    ->infer(
        SomeAbstractClass::class, 
        fn () => SomeChildClass::class
    )
    ->mapper()
    ->map(SomeAbstractClass::class, [
        'foo' => 'foo',
        'bar' => 'bar',
        'baz' => 'baz',
    ]);

assert($result instanceof SomeChildClass);
assert($result->foo === 'foo');
assert($result->bar === 'bar');
assert($result->baz === 'baz');

继承示例:

readonly class SomeParentClass
{
    public string $foo;

    public string $bar;
}

final readonly class SomeChildClass extends SomeParentClass
{
    public string $baz;
}

$result = (new \CuyZ\Valinor\MapperBuilder())
    ->infer(
        SomeParentClass::class, 
        fn () => SomeChildClass::class
    )
    ->mapper()
    ->map(SomeParentClass::class, [
        'foo' => 'foo',
        'bar' => 'bar',
        'baz' => 'baz',
    ]);

assert($result instanceof SomeChildClass);
assert($result->foo === 'foo');
assert($result->bar === 'bar');
assert($result->baz === 'baz');