markhuot/data

数据对象

1.0.5 2022-11-03 18:40 UTC

This package is auto-updated.

Last update: 2024-08-30 01:38:24 UTC


README

数据(或值)对象可以用来表示在应用中可能没有严格类别的数据。例如,来自Web请求的POST数据或来自API调用的JSON数据。在这些情况下,你可能需要一个数据类型的强类型版本,但又不想编写所有样板代码来将数据从源转换为数据对象。

此包为您处理所有这些样板代码,并旨在覆盖80%最常见用例,同时允许您在需要更特定转换时扩展包。

在最基本的情况下,此包是将您的源表示(通常是松散类型)转换为目标表示(更严格类型)的转换器。它看起来像这样(真的很像)。

class Repository {
    public int $id;
    public string $name;
    public string $fullName;
    public array $topics;
    public bool $private;
}

此包的目标是在不需要很多(如果有的话)定制的情况下,将此包放在现有的数据对象之上。它基于属性标签,并尽可能通过惯例来推断。

要将源数据转换为数据对象,您将在数据构建器上调用->fill($data)

(new Data(new MyAwesomeObject))->fill($data)->get();

当您调用->fill($data)时,包将使用反射来确定源$data如何映射到Repository对象中的属性。默认情况下没有映射,它将盲目地查找源字段如id并将它们推入->id的目标属性。

当映射不是1:1且您需要更多控制字段命名时,您可以使用MapFrom属性来帮助转换器。

use markhuot\data\attributes\MapFrom;

class Repository {
    public int $id;
    public string $name;

    #[MapFrom('full_name')]
    public string $fullName;
}

这将从源中拉取full_name字段并将其放入目标中的->fullName属性。如果您的转换是蛇形到驼峰形,有一个常用的助手来完成这项工作,

use markhuot\data\attributes\MapFrom;

class Repository {
    public int $id;
    public string $name;

    #[MapFrom(MapFrom::SNAKE)]
    public string $fullName;
}

如果您发现自己多次使用相同的MapFrom注释,您还可以将属性应用于类,以便以相同的方式转换所有属性名称,

use markhuot\data\attributes\MapFrom;

#[MapFrom(MapFrom::SNAKE)]
class Repository {
    public int $id;
    public string $name;
    public string $fullName;
}

例如,所有Repository类的字段在转换过程中都会被转换。id将保持为->idname将保持为->name,但full_name将自动映射到->fullName

嵌套映射

默认情况下,嵌套属性将基于类型提示进行映射。类型提示可以是PHP或docblock,具体取决于您的需求。对于单个对象嵌套,您可以使用原生的PHP类型提示,如下所示,

class Repository {
    public int $id;
    public string $name;
    public string $fullName;
    public Owner $owner;
    public array $topics;
    public bool $private;
}

class Owner {
    public int $id;
    public string $login;
    public string $avatarUrl;
}

这将映射源数据['owner' => ['id' => 1, 'login' => 'foo']]到一个具有->owner属性的Repository对象,该属性正确地设置为具有->id->loginOwner实例。

对于更高级的映射,您可以使用docblock来定义PHP尚不了解的属性。例如,

class Release {
    public int $id;
    public string $tagName;

    /** @var Asset[] */
    public array $assets;
}

class Asset {
    public int $id;
    public string $name;
    public string $url;
}

这将正确读取->assets期望是一个资产数组,并将尝试将源转换为一个对象数组。它将适用于以下源数据,

{
    "id": "1",
    "tag_name": "1.0.0",
    "assets": [
        {
            "id": "1",
            "name": "1.0.0.zip",
            "url" "..."
        },
        {
            "id": "2",
            "name": "1.0.1.zip",
            "url" "..."
        }
    ]
}

验证

因为很多时候严格的类型检查不足以确保数据的正确性,您还可以在->fill()过程之后使用Symfony验证组件来验证数据。

use Symfony\Component\Validator\Constraints as Assert;

class CreateBlogPostData {
    #[Assert\NotNull]
    public int $authorId;
    
    #[Assert\NotBlank]
    public string $title;
    
    #[Assert\Regex('/[a-z0-9_-]+/')]
    public ?string $slug;
}

$data = (new Data(new CreateBlogPostData))->fill($_POST)->validate()->get();