markhuot / data
数据对象
Requires
- doctrine/annotations: ^1.13
- illuminate/collections: ^8|^9.36
- pestphp/pest: ^1.22
- phpdocumentor/reflection-docblock: ^5.3
- symfony/cache: ^6.0
- symfony/string: ^5|^6.1
- symfony/validator: ^5|^6.1
Requires (Dev)
- phpstan/phpstan: ^1.8
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
将保持为->id
,name
将保持为->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
和->login
的Owner
实例。
对于更高级的映射,您可以使用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();