跳至正文

使用内置规范化器配置器

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

指定日期时间的规范化格式

默认情况下,日期将使用 RFC 3339 格式进行格式化。 NormalizeDateTimeFormat配置器可用于指定要使用的格式。

此类可以用作全局使用的配置器,也可以用作用于特定属性的特性。

作为全局配置器使用

use CuyZ\Valinor\Normalizer\Configurator\NormalizeDateTimeFormat;
use CuyZ\Valinor\Normalizer\Format;
use CuyZ\Valinor\NormalizerBuilder;

$userAsArray = (new NormalizerBuilder())
    ->configureWith(new NormalizeDateTimeFormat(\DateTimeInterface::ATOM))
    ->normalizer(Format::array())
    ->normalize($user);

// [
//     'name' => 'Jane Doe',
//     'createdAt' => '2000-01-01T00:00:00+00:00',
// ]

作为特性用于指定目标

use CuyZ\Valinor\Normalizer\Configurator\NormalizeDateTimeFormat;
use CuyZ\Valinor\Normalizer\Format;
use CuyZ\Valinor\NormalizerBuilder;

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

        #[NormalizeDateTimeFormat(\DateTimeInterface::ATOM)]
        public DateTimeInterface $createdAt,
    ) {}
}

$userAsArray = (new NormalizerBuilder())
    ->normalizer(Format::array())
    ->normalize($user);

// [
//     'name' => 'Jane Doe',
//     'createdAt' => '2000-01-01T00:00:00+00:00',
// ]

转换键名的大小写格式

多个配置器将规范化对象的键转换为与 PHP 代码库中使用的命名约定不同的命名约定。

配置器 结果
NormalizeKeysToSnakeCase first_name
NormalizeKeysToCamelCase firstName
NormalizeKeysToPascalCase FirstName
NormalizeKeysToKebabCase first-name

这些类中的每一个都可以用作全局使用的配置器,也可以用作针对特定类的特性。

作为全局配置器使用

每个标准化对象的键都会转换为目标情况:

use CuyZ\Valinor\Normalizer\Configurator\NormalizeKeysToSnakeCase;
use CuyZ\Valinor\Normalizer\Format;
use CuyZ\Valinor\NormalizerBuilder;

$userAsArray = (new NormalizerBuilder())
    ->configureWith(new NormalizeKeysToSnakeCase())
    ->normalizer(Format::array())
    ->normalize($user);

// [
//     'first_name' => 'John',
//     'last_name' => 'Doe',
// ]

作为特性用于指定目标

当用作特性时,仅转换目标类的键,而其余输出保持不变:

use CuyZ\Valinor\Normalizer\Configurator\NormalizeKeysToSnakeCase;
use CuyZ\Valinor\Normalizer\Format;
use CuyZ\Valinor\NormalizerBuilder;

#[NormalizeKeysToSnakeCase]
final readonly class User
{
    public function __construct(
        public string $firstName,
        public string $lastName,
    ) {}
}

$userAsArray = (new NormalizerBuilder())
    ->normalizer(Format::array())
    ->normalize($user);

// [
//     'first_name' => 'John',
//     'last_name' => 'Doe',
// ]

重命名属性键

数据格式中的属性名称可能与 PHP 代码库中使用的名称不同。 NormalizeKeyTo属性在规范化期间重命名属性的键。

use CuyZ\Valinor\Normalizer\Configurator\NormalizeKeyTo;
use CuyZ\Valinor\Normalizer\Format;
use CuyZ\Valinor\NormalizerBuilder;

final readonly class Address
{
    public function __construct(
        public string $street,
        public string $zipCode,
        #[NormalizeKeyTo('town')]
        public string $city,
    ) {}
}

$addressAsArray = (new NormalizerBuilder())
    ->normalizer(Format::array())
    ->normalize(
        new Address(
            street: '221B Baker Street',
            zipCode: 'NW1 6XE',
            city: 'London', // Key will be renamed to 'town'
        )
    );

// [
//     'street' => '221B Baker Street',
//     'zipCode' => 'NW1 6XE',
//     'town' => 'London',
// ]

展平单个属性对象

当对象拥有单个属性时,将其展平可能很有用,这样标准化结果就不是['someProperty' => 'value']而是简单的'value' 。

NormalizeToSingleValue类可以用作全局使用的配置器,也可以用作用于特定类或属性的特性。

作为全局配置器使用

当用作配置器时,每个具有单个属性的对象都会被展平:

use CuyZ\Valinor\Normalizer\Configurator\NormalizeToSingleValue;
use CuyZ\Valinor\Normalizer\Format;
use CuyZ\Valinor\NormalizerBuilder;

final readonly class Email
{
    public function __construct(
        public string $email,
    ) {}
}

$value = (new NormalizerBuilder())
    ->configureWith(new NormalizeToSingleValue())
    ->normalizer(Format::array())
    ->normalize(new Email('john.doe@example.com'));

// 'john.doe@example.com'

作为特性用于指定目标

当用作特性时,只有目标类或属性被展平,其余输出保持不变:

use CuyZ\Valinor\Normalizer\Configurator\NormalizeToSingleValue;
use CuyZ\Valinor\Normalizer\Format;
use CuyZ\Valinor\NormalizerBuilder;

final readonly class Email
{
    public function __construct(
        public string $email,
    ) {}
}

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

        #[NormalizeToSingleValue]
        public Email $email,
    ) {}
}

$userAsArray = (new NormalizerBuilder())
    ->normalizer(Format::array())
    ->normalize(new User('John Doe', new Email('john.doe@example.com')));

// [
//     'name' => 'John Doe',
//     'email' => 'john.doe@example.com',
// ]

忽略属性

可以从规范化输出中排除属性,例如通过使用IgnoreOnNormalization属性对其进行标记来隐藏密码等敏感数据。

警告

为了使该属性生效,还必须通过configureWith()在构建器上注册IgnoreOnNormalization实例。如果没有它,属性值将被占位符对象替换,该占位符对象在使用时会立即引发异常(例如,当它转换为字符串或编码为 JSON 时),指向丢失的注册。

use CuyZ\Valinor\Normalizer\Configurator\IgnoreOnNormalization;
use CuyZ\Valinor\Normalizer\Format;
use CuyZ\Valinor\NormalizerBuilder;

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

        #[IgnoreOnNormalization]
        public string $password,
    ) {}
}

// Registering the configurator is required for the attribute to take effect.
$userAsArray = (new NormalizerBuilder())
    ->configureWith(new IgnoreOnNormalization())
    ->normalizer(Format::array())
    ->normalize(new User('John Doe', 's3cr3t'));

// ['name' => 'John Doe']

自定义变换器示例

与上面的配置器不同,以下行为不是开箱即用的:它们说明了现实生活中的用例,其逻辑特定于每个应用程序。它们在这里显示为灵感,并且必须使用在NormalizerBuilder上注册的自定义变换器来实现,请参阅扩展规范化器章节。

变换对象

某些对象在规范化期间可能具有自定义行为,例如可能需要重新映射属性。在下面的示例中,变换器将检查对象是否定义了normalize方法,如果存在则使用它。

显示代码示例 - 自定义对象规范化
namespace My\App;

final readonly class Address
{
    public function __construct(
        public string $road,
        public string $zipCode,
        public string $town,
    ) {}

    public function normalize(): array
    {
        return [
            'street' => $this->road,
            'postalCode' => $this->zipCode,
            'city' => $this->town,
        ];
    }
}

(new \CuyZ\Valinor\NormalizerBuilder())
    ->registerTransformer(function (object $object, callable $next) {
        return method_exists($object, 'normalize')
            ? $object->normalize()
            : $next();
    })
    ->normalizer(\CuyZ\Valinor\Normalizer\Format::array())
    ->normalize(
        new \My\App\Address(
            road: '221B Baker Street',
            zipCode: 'NW1 6XE',
            town: 'London',
        ),
    );

// [
//     'street' => '221B Baker Street',
//     'postalCode' => 'NW1 6XE',
//     'city' => 'London',
// ]

版本控制 API

API版本控制可以通过不同的策略和算法来实现。下面的示例显示了对象如何实现接口来指定它们自己的特定版本控制行为。

显示代码示例 - 版本控制对象
namespace My\App;

interface HasVersionedNormalization
{
    public function normalizeWithVersion(string $version): mixed;
}

final readonly class Address implements \My\App\HasVersionedNormalization
{
    public function __construct(
        public string $streetNumber,
        public string $streetName,
        public string $zipCode,
        public string $city,
    ) {}

    public function normalizeWithVersion(string $version): array
    {
        return match (true) {
            version_compare($version, '1.0.0', '<') => [
                // Street number and name are merged in a single property
                'street' => "$this->streetNumber, $this->streetName",
                'zipCode' => $this->zipCode,
                'city' => $this->city,
            ],
            default => get_object_vars($this),
        };
    }
}

function normalizeWithVersion(string $version): mixed
{
    return (new \CuyZ\Valinor\NormalizerBuilder())
        ->registerTransformer(
            fn (\My\App\HasVersionedNormalization $object) => $object->normalizeWithVersion($version)
        )
        ->normalizer(\CuyZ\Valinor\Normalizer\Format::array())
        ->normalize(
            new \My\App\Address(
                streetNumber: '221B',
                streetName: 'Baker Street',
                zipCode: 'NW1 6XE',
                city: 'London',
            )
        );
}

// Version can come for instance from HTTP request headers
$result_v0_4 = normalizeWithVersion('0.4');
$result_v1_8 = normalizeWithVersion('1.8');

// $result_v0_4 === [
//     'street' => '221B, Baker Street',
//     'zipCode' => 'NW1 6XE',
//     'city' => 'London',
// ]
// 
// $result_v1_8 === [
//     'streetNumber' => '221B',
//     'streetName' => 'Baker Street',
//     'zipCode' => 'NW1 6XE',
//     'city' => 'London',
// ]