跳至正文

入门指南

安装

composer require cuyz/valinor

示例

某个应用需要处理来自外部 API 的数据。响应采用 JSON 格式,描述一个讨论主题及其回复。这些输入是否有效尚不确定,而且直接操作原始 JSON 字符串既费力又低效。

{
    "id": 1337,
    "content": "Do you like potatoes?",
    "date": "1957-07-23 13:37:42",
    "answers": [
        {
            "user": "Ella F.",
            "message": "I like potatoes",
            "date": "1957-07-31 15:28:12"
        },
        {
            "user": "Louis A.",
            "message": "And I like tomatoes",
            "date": "1957-08-13 09:05:24"
        }
    ]
}

应用必须确保自己能正确处理这些数据。将输入封装在值对象中有助于做到这一点。


必须使用类来描述所需的数据结构。

final readonly class Thread
{
    public function __construct(
        public int $id,
        public string $content,
        public DateTimeInterface $date,
        /** @var Answer[] */
        public array $answers, 
    ) {}
}

final readonly class Answer
{
    public function __construct(
        public string $user,
        public string $message,
        public DateTimeInterface $date,
    ) {}
}

然后使用映射器将源数据填充到这些对象中。

public function getThread(int $id): Thread
{
    $rawJson = $this->client->request("https://example.com/thread/$id");

    try {   
        return (new \CuyZ\Valinor\MapperBuilder())
            ->mapper()
            ->map(
                Thread::class,
                new \CuyZ\Valinor\Mapper\Source\JsonSource($rawJson)
            );
    } catch (\CuyZ\Valinor\Mapper\MappingError $error) {
        // Do something…
    }
}

映射高级类型

尽管建议将输入映射到值对象,但在某些情况下映射到其他类型可能更容易/更灵活。

例如,可以映射到对象数组:

try {
    $objects = (new \CuyZ\Valinor\MapperBuilder())
        ->mapper()
        ->map(
            'array<' . SomeClass::class . '>',
            [/* … */]
        );
} catch (\CuyZ\Valinor\Mapper\MappingError $error) {
    // Do something…
}

对于简单的用例,可以使用数组形状:

try {
    $array = (new \CuyZ\Valinor\MapperBuilder())
        ->mapper()
        ->map(
            'array{foo: string, bar: int}',
            [/* … */]
        );

    echo $array['foo'];
    echo $array['bar'] * 2;
} catch (\CuyZ\Valinor\Mapper\MappingError $error) {
    // Do something…
}