跳至正文

使用内置映射器配置器

该库提供了一组开箱即用的映射器配置器,可用于应用常见的映射行为:

限制键名的大小写格式

将输入数据映射到对象或指定结构的数组时,四个配置器限制接受哪种键名的大小写格式。如果键与预期大小写不匹配,则会引发映射错误。

例如,这有助于在 API 的输入中强制执行一致的命名约定,以确保 JSON 负载仅包含camelCase 、 snake_case 、 PascalCase或kebab-case键。

可用的配置器:

配置器 示例
new RestrictKeysToCamelCase() firstName
new RestrictKeysToPascalCase() FirstName
new RestrictKeysToSnakeCase() first_name
new RestrictKeysToKebabCase() first-name
$user = (new \CuyZ\Valinor\MapperBuilder())
    ->configureWith(
        new \CuyZ\Valinor\Mapper\Configurator\RestrictKeysToCamelCase()
    )
    ->mapper()
    ->map(\My\App\User::class, [
        'firstName' => 'John', // Ok
        'last_name' => 'Doe',  // Error
    ]);

转换键名的大小写格式

有两个配置器可用于在将输入数据的键映射到对象属性或指定结构的数组键之前转换它们。这允许接受具有与 PHP 代码库中使用的命名约定不同的命名约定的数据。

MapKeysToCamelCase

转换
first_name → firstName
FirstName → firstName
first-name → firstName
$user = (new \CuyZ\Valinor\MapperBuilder())
    ->configureWith(
        new \CuyZ\Valinor\Mapper\Configurator\MapKeysToCamelCase()
    )
    ->mapper()
    ->map(\My\App\User::class, [
        'first_name' => 'John', // mapped to `$firstName`
        'last_name' => 'Doe',   // mapped to `$lastName`
    ]);

MapKeysToSnakeCase

转换
firstName → first_name
FirstName → first_name
first-name → first_name
$user = (new \CuyZ\Valinor\MapperBuilder())
    ->configureWith(
        new \CuyZ\Valinor\Mapper\Configurator\MapKeysToSnakeCase()
    )
    ->mapper()
    ->map(\My\App\User::class, [
        'firstName' => 'John', // mapped to `$first_name`
        'lastName' => 'Doe',   // mapped to `$last_name`
    ]);

这些配置器可以与限制配置器结合使用,以便在一个步骤中验证和转换键。必须在转换之前注册限制配置器,以便在原始输入键上运行验证:

$user = (new \CuyZ\Valinor\MapperBuilder())
    ->configureWith(
        new \CuyZ\Valinor\Mapper\Configurator\RestrictKeysToSnakeCase(),
        new \CuyZ\Valinor\Mapper\Configurator\MapKeysToCamelCase(),
    )
    ->mapper()
    ->map(\My\App\User::class, [
        'first_name' => 'John',
        'last_name' => 'Doe',
    ]);

从特定键映射属性

MapFromKey属性从特定的源键提供类属性或构造函数/方法参数,而不是将其与属性名称进行匹配。当源数据使用与其应映射到的属性名称不同的键时,这非常有用。

use CuyZ\Valinor\Mapper\Configurator\MapFromKey;
use CuyZ\Valinor\MapperBuilder;

final readonly class Person
{
    public function __construct(
        public string $name,

        #[MapFromKey('zipCode')]
        public string $postalCode,
    ) {}
}

$person = (new MapperBuilder())
    ->mapper()
    ->map(Person::class, [
        'name' => 'John Doe',
        'zipCode' => '75001', // mapped to `$postalCode`
    ]);

给定的键按原样使用:它不受使用registerKeyConverter()注册的键转换器的影响,并且不再接受属性名称,仅从给定的键读取源。

注意

两个属性不能从同一个源键映射;这样做会导致配置错误并在映射期间引发异常。

对于全局重命名或声明自定义键映射特性,请参阅转换源键章节。

转换标量值

多个配置器在映射之前将标量值转换为特定类型。当输入数据携带与目标类型不同的表示形式的值时,例如在表单提交、CSV 文件或 JSON 有效负载中编码为字符串的数字或布尔值,这非常有用。

注意

标量值转换也可以全局启用,请参阅有关MapperBuilder::allowScalarValueCasting()的文档。

MapAsBool

将字符串和整数表示形式转换为实数bool 。默认情况下, 1 、 '1'和'true'会转换为true , 0 、 '0'和'false'会转换为false 。可以通过给出应转换为true和false值来自定义接受的表示。

应用于具有#[MapAsBool]属性的单个属性:

use CuyZ\Valinor\Mapper\Configurator\MapAsBool;
use CuyZ\Valinor\MapperBuilder;

final readonly class User
{
    public function __construct(
        public string $name,

        #[MapAsBool(true: ['on', 'yes'], false: ['off', 'no'])]
        public bool $isActive,
    ) {}
}

$user = (new MapperBuilder())
    ->mapper()
    ->map(User::class, [
        'name' => 'John Doe',
        'isActive' => 'on', // mapped to `true`
    ]);

或者全局应用来转换每个布尔值:

use CuyZ\Valinor\MapperBuilder;

$isActive = (new MapperBuilder())
    ->allowCastingToBoolean()
    ->mapper()
    ->map('bool', 'true'); // mapped to `true`

MapAsInt

将整数的字符串表示形式转换为实数int 。任何不是有效整数表示的值都保持不变并移交给映射器。

应用于具有#[MapAsInt]属性的单个属性:

use CuyZ\Valinor\Mapper\Configurator\MapAsInt;
use CuyZ\Valinor\MapperBuilder;

final readonly class User
{
    public function __construct(
        public string $name,

        #[MapAsInt]
        public int $age,
    ) {}
}

$user = (new MapperBuilder())
    ->mapper()
    ->map(User::class, [
        'name' => 'John Doe',
        'age' => '42', // mapped to `42`
    ]);

或者全局应用来转换每个整数值:

use CuyZ\Valinor\MapperBuilder;

$age = (new MapperBuilder())
    ->allowCastingToInteger()
    ->mapper()
    ->map('int', '42'); // mapped to `42`

MapAsFloat

将数字的字符串表示形式转换为实数float 。任何不是有效数字表示的值都将保持不变并移交给映射器。

应用于具有#[MapAsFloat]属性的单个属性:

use CuyZ\Valinor\Mapper\Configurator\MapAsFloat;
use CuyZ\Valinor\MapperBuilder;

final readonly class Product
{
    public function __construct(
        public string $name,

        #[MapAsFloat]
        public float $price,
    ) {}
}

$product = (new MapperBuilder())
    ->mapper()
    ->map(Product::class, [
        'name' => 'Coffee',
        'price' => '4.50', // mapped to `4.5`
    ]);

或者全局应用来转换每个浮点值:

use CuyZ\Valinor\MapperBuilder;

$price = (new MapperBuilder())
    ->allowCastingToFloat()
    ->mapper()
    ->map('float', '4.50'); // mapped to `4.5`

MapAsString

将整数或浮点数转换为string 。当输入数据携带必须作为字符串处理的数字(例如标识符或邮政编码)时,这非常有用。

应用于具有#[MapAsString]属性的单个属性:

use CuyZ\Valinor\Mapper\Configurator\MapAsString;
use CuyZ\Valinor\MapperBuilder;

final readonly class User
{
    public function __construct(
        public string $name,

        #[MapAsString]
        public string $id,
    ) {}
}

$user = (new MapperBuilder())
    ->mapper()
    ->map(User::class, [
        'name' => 'John Doe',
        'id' => 42, // mapped to `'42'`
    ]);

或者全局应用来转换每个字符串值:

use CuyZ\Valinor\MapperBuilder;

$id = (new MapperBuilder())
    ->allowCastingToString()
    ->mapper()
    ->map('string', 42); // mapped to `'42'`

从格式映射日期

MapToDateTimeFromFormat配置器在映射之前使用给定的日期格式解析输入字符串。当输入数据携带映射器无法识别的特定格式的日期时,这非常有用。

该格式必须遵循DateTimeImmutable::createFromFormat()支持的语法。与给定格式不匹配的值会引发映射错误。

use CuyZ\Valinor\Mapper\Configurator\MapToDateTimeFromFormat;
use CuyZ\Valinor\MapperBuilder;
use DateTimeInterface;

final readonly class Event
{
    public function __construct(
        public string $name,

        #[MapToDateTimeFromFormat('d/m/Y')]
        public DateTimeInterface $date,
    ) {}
}

$event = (new MapperBuilder())
    ->mapper()
    ->map(Event::class, [
        'name' => 'Release of legendary album',
        'date' => '08/11/1971', // mapped to a `DateTimeImmutable`
    ]);

将字符串分解为列表

MapExplodedStringToList配置器在映射之前使用给定的分隔符将字符串分解为列表。当输入数据将列表作为单个分隔字符串(例如来自 CSV 文件或查询参数的逗号分隔值)时,这非常有用。

然后将生成的列表映射到目标类型,以便可以进一步转换项目,例如转换为list<int> 。

use CuyZ\Valinor\Mapper\Configurator\MapExplodedStringToList;
use CuyZ\Valinor\MapperBuilder;

final readonly class Product
{
    public function __construct(
        public string $name,

        /** @var list<string> */
        #[MapExplodedStringToList(separator: ',')]
        public array $sizes,
    ) {}
}

$product = (new MapperBuilder())
    ->mapper()
    ->map(Product::class, [
        'name' => 'T-Shirt',
        'sizes' => 'XS,S,M,L,XL', // mapped to `['XS', 'S', 'M', 'L', 'XL']`
    ]);

将数组映射到列表

MapArrayToList配置器在映射之前丢弃数组的键并将其值映射到列表。当输入数据是关联数组或具有丢失或无序索引的稀疏列表(应作为顺序列表处理)时,这非常有用。

应用于具有#[MapArrayToList]属性的单个属性:

use CuyZ\Valinor\Mapper\Configurator\MapArrayToList;
use CuyZ\Valinor\MapperBuilder;

final readonly class Basket
{
    public function __construct(
        /** @var list<string> */
        #[MapArrayToList]
        public array $products,
    ) {}
}

$basket = (new MapperBuilder())
    ->mapper()
    ->map(Basket::class, [
        'a' => 'Coffee',
        'b' => 'Tea',
    ]); // mapped to `['Coffee', 'Tea']`

要为每个列表全局启用相同的行为,请使用内置的allowNonSequentialList()设置:

use CuyZ\Valinor\MapperBuilder;

$products = (new MapperBuilder())
    ->allowNonSequentialList()
    ->mapper()
    ->map('list<string>', [
        'a' => 'Coffee',
        'b' => 'Tea',
    ]); // mapped to `['Coffee', 'Tea']`

解码 JSON 字符串

MapFromJson配置器解码 JSON 字符串并将结果传递给映射器。当输入数据携带嵌套结构作为编码的 JSON 字符串时(例如存储在数据库中的列或表单提交中的字段),这非常有用。

然后将解码后的值映射到目标类型,因此通常的验证和错误报告仍然适用。无效的 JSON 字符串会引发映射错误。

use CuyZ\Valinor\Mapper\Configurator\MapFromJson;
use CuyZ\Valinor\MapperBuilder;

final readonly class User
{
    public function __construct(
        public string $name,

        /** @var list<string> */
        #[MapFromJson]
        public array $roles,
    ) {}
}

$user = (new MapperBuilder())
    ->mapper()
    ->map(User::class, [
        'name' => 'John Doe',
        'roles' => '["admin", "editor"]', // mapped to `['admin', 'editor']`
    ]);