diezz/yaml-to-object-mapper

Yaml到对象映射器

0.1.1 2024-02-11 19:15 UTC

This package is auto-updated.

Last update: 2024-09-19 00:00:48 UTC


README

Scrutinizer Code Quality Build Status Code Coverage

Yaml到对象映射器

架起YAML与PHP对象之间的桥梁

利用Yaml到对象映射器库,轻松将您的YAML配置转换为强大的PHP对象。通过高效映射、强大的验证和灵活的变量处理,同时利用PHP 8属性的便利性,提升开发体验。

主要功能

  • 无缝映射:轻松将分层YAML数据映射到深层嵌套的PHP对象,节省您繁琐的手动配置。
  • 全面验证:通过内置的验证机制,如#[Required]属性、类型提示和自定义规则,确保数据完整性。
  • 声明式DSL:自定义字段和验证的属性配置,减少样板代码,提高可读性。
  • 变量处理:在YAML文件中直接使用动态变量,如${env:DB_PASSWORD},简化配置管理。
  • 自定义参数解析器:通过为变量创建自定义逻辑,获得最大的灵活性,以满足您的特定需求。
  • 内置变量:享受一系列预定义变量,如now、self和format,以简化YAML配置中的常见任务。
  • PHP 8集成:利用PHP 8属性的力量,编写更干净、更简洁的代码,提升您的开发体验。

安装

composer require diezz/yaml-to-object-mapper

基本用法

我们有一个简单的config.yml文件

name: object mapper
connection:
  host: localhost
  port: 3202
  username: test
  password: password

我们希望将其映射到类的对象中

class Config {
    public string $name;
    public ConnectionSettings $connection
}

class ConnectionSettings {
    public string $host;
    public string $port;
    public string $username;
    public string $password;
}

$config = Mapper::make()->mapFromFile(Config::class, 'config.yml');

在目标类属性中设置值

在目标类属性中设置值 映射器会自动将映射的值分配给目标类中相应的属性。以下是需要注意的几点

属性访问

  • 公共属性:直接赋值以实现无缝更新。
  • 非公共属性:需要相应的setter方法。

setter命名

  • 默认方法:使用与属性名称相同的setter前缀命名(例如,setName用于name属性)。
  • 自定义命名:使用属性上的#[Setter('yourSetterName')]属性覆盖默认值(例如,#[Setter('updateName')])。

扩展用法

使用默认值解析器动态字段映射

厌倦了在YAML中编写冗余的属性名称和嵌套列表?默认值解析器可以自动推断字段名称和结构,简化配置。

示例

而不是这个冗长的YAML

tables:
  - name: users
    columns:
      username: varchar(255)
      email: varchar(255)
      password: varchar(255)

  - name: orders
    columns: 
      id: int
      user_id: int
      price: float
use Diezz\YamlToObjectMapper\Attributes\Collection;

class DatabaseSchema {
    #[Collection(class: Table::class)]
    public array $tables;
}

class Table {
    public string $name;
    public array $columns;
}

想象一下这个简洁性

tables:
  users:
    username: varchar(255)
    email: varchar(255)
    password: varchar(255)

  orders:
    id: int
    user_id: int
    price: float
class Table {
    #[DefaultValueResolver(resolver: DefaultValueResolver::PARENT_KEY)]
    public string $name;
    
    #[DefaultValueResolver(resolver: DefaultValueResolver::NESTED_LIST)]
    public array $columns;
}

工作原理

  • [DefaultValueResolver(resolver: DefaultValueResolver::PARENT_KEY)]:自动将每个表对象的name字段设置为YAML列表中的键。
  • [DefaultValueResolver(resolver: DefaultValueResolver::NESTED_LIST)]:将每个YAML列表项视为对象的属性,并根据键创建关联数组。

验证

默认情况下,映射器会检查yml文件中是否存在必需的字段。有多种方式定义映射器如何定义哪个字段是必需的

  1. 最明显的方式是使用#[Required]属性标记必需字段。
  2. 如果类字段没有标记为必需属性,映射器会检查字段的类型提示。它可以是PHP 7类型提示或PHPdoc注释。可空属性或具有默认值的属性被视为非必需的或反之亦然。

使用库集成的验证机制,保持您配置的一致性和准确性。它提供了一种灵活的方法来定义必填字段和强制数据类型。

工作原理

  • 使用 #[Required] 显式标记:使用 #[Required] 属性清楚地指明必填字段。这提供了对验证预期的最直接控制。
  • 类型提示和文档注释
    • PHP 7 类型提示:具有显式类型提示的字段(例如,public string $name)被视为必填项。
    • 文档注释:在其文档注释中具有类型提示的字段(例如,@var string)也被视为必填项。
  • 默认值和可为空类型
    • 具有默认值的字段:在其声明中分配了默认值的字段不是必填的,因为它们有一个回退值。
    • 可为空类型:可为空的字段(在类型声明中使用问号或 |null)不是必填的,因为它们可以接受 null 作为有效值。
use Diezz\YamlToObjectMapper\Attributes\Required;

class Model {
    /**
     * Explicitly required field
     */
    #[Required]
    public $value0;

    /**
     * Required due to string type hint
     */
    public string $value1;

    /**
     * Required based on type hint in doc comment
     *
     * @var string
     */
    public $value2;

    /**
     * Not required due to default value
     */
    public string $value3 = 'value3';

    /**
     * Not required due to nullable type hint
     */
    public ?string $value4;

    /**
     * Not required due to nullable type hint in doc comment
     *
     * @var string|null
     */
    public $value5;
}

使用变量处理进行动态配置

利用动态变量处理,充分释放您的 YAML 配置的潜力。库支持各种内置变量,甚至允许您为高级场景创建自定义逻辑。

内置变量

  • self - 访问同一配置对象内的值,启用自引用和跨属性依赖。 ${self::connection.host}
  • substring - 提取字符串的一部分,提供字符串操作功能 ${substring:${self:name}:7}
  • now - 获取当前日期和时间,启用动态时间戳和时间相关的配置 ${now}
  • format - 根据指定的模式格式化日期和时间,确保一致的日期/时间表示。 ${format:${now}:Y-M-D}
  • env - 读取环境变量,安全地将配置与特定环境设置集成 ${env:DB_PASSWORD}

语法

${varName:firstArgument:secondArgument}

另一个变量可以作为另一个变量的参数传递,例如

${varName1:${varName2:argument1}:argument2}

自定义参数解析器

为了获得更大的灵活性,创建自定义参数解析器以处理独特的变量处理需求。

步骤

  • 创建自定义解析器类:扩展 CustomArgumentResolver 类并实现 doResolve 方法来定义变量的逻辑。
  • 注册解析器:使用 Mapper::registerCustomArgumentResolver 方法将解析器与变量名关联。
  • 在 YAML 中使用变量:在您的 YAML 配置中使用已注册的变量,按需传递参数。

在变量处理顶部有一个 ArgumentResolver。您可以创建自己的 ArgumentResolvers,这为您提供了在 yaml 文件中处理自定义变量的能力。要这样做,创建一个扩展 CustomArgumentResolver 的类。CustomArgumentResolver 实现的构造函数应接受 ArgumentResolver,它是变量的参数解析器。构造函数中参数的数量应等于变量支持的参数数量。

class SumArgumentResolver extends CustomArgumentResolver
{
    private array $arguments;

    public function __construct(...$arguments)
    {
        $this->arguments = $arguments;
    }

    protected function doResolve($context = null): int
    {
        $sum = 0;
        foreach ($this->arguments as $iValue) {
            $sum += $iValue->resolve($context);
        }

        return $sum;
    }
}

$mapper = Mapper::make();
//Register the resolver
$mapper->registerCustomArgumentResolver('sum', SumArgumentResolver::class);
$result = $mapper->map($file, Output::class);

现在您可以在 yaml 文件中使用它,例如

price:
  item1: 100
  item2: 200
total: ${sum:${self:price.item1}:${self:price.item2}}