codebot/phpdto

用于生成 PHP 数据传输对象的 CLI 工具。

0.3.0 2024-03-23 20:31 UTC

This package is auto-updated.

Last update: 2024-09-23 21:32:29 UTC


README

Latest Stable Version Build Status Total Downloads License

关于

用于生成 PHP 数据传输对象的 CLI 工具。

此实用程序可以基于 JSON 模式生成 PHP 8 DTO 类。

安装

使用 composer 安装此包

composer require --dev codebot/phpdto 0.3.*

请注意,此包所需的最低 PHP 版本是 8.0。

初始化

vendor/bin/phpdto init

当前工作目录是从您调用 phpdto 命令的目录。

此命令将初始化 phpdto 并在您的当前工作目录中创建 phpdto.json 配置文件和 phpdto_patterns 目录。

配置

phpdto.json 配置文件包含以下变量

PHP_DTO_PATTERNS_DIR - 用于存储 DTO 的 JSON 模式的目录。

PHP_DTO_NAMESPACE - 生成 DTO 类的命名空间。

PHP_DTO_CLASS_POSTFIX - DTO 类的后缀,例如 Item(没有后缀),ItemDto(后缀是 "Dto")。

这些变量存储为环境变量。

使用方法

要生成 DTO 类,您必须创建一个模式,它是一个包含有关生成类信息的 JSON 文件。

DTO JSON 模式

DTO 模式的示例

{
  "class": "item",
  "namespace_postfix": "",
  "props": {
    "id": "int",
    "count": "?int",
    "name": "string",
    "description": "?string",
    "is_active": "bool"
  }
}

class

将结合 phpdto.json 配置文件中指定的 PHP_DTO_CLASS_POSTFIX 的类名。

因此,如果类名是 item,并且类后缀配置值是 Dto,则生成的类名将是 ItemDto

namespace_postfix

将结合 phpdto.json 配置文件中指定的 PHP_DTO_NAMESPACE 的生成 DTO 类命名空间后缀。

因此,如果命名空间后缀是 \User,并且默认 DTO 命名空间是 App\Dto,则生成的类命名空间将是 App\Dto\User

您可以留空命名空间后缀。

props

此对象包含有关 DTO 类属性和方法的信息。键将被转换为类属性。值包含有关获取器返回类型的信息。

"description" : "?string" - 由于此对,将添加 $_description 属性到 DTO 类,其中包含 private ?string $_description 属性和 getDescription(): ?string 方法,该方法期望返回类型 "string" 并允许 null。

生成 DTO

假设您已经在 phpdto_patterns 文件夹中创建了一个名为 item.json 的模式。

运行 vendor/bin/phpdto -f=item 以生成 DTO 类。它将存储在 phpdto.json 配置文件中指定的命名空间下,结合模式中指定的命名空间后缀。

假设您正在从 "DTO JSON 模式" 部分中显示的模式生成 DTO 类,那么您将生成以下类。

<?php

namespace App\Dto;

use PhpDto\Dto;
use PhpDto\DtoSerialize;
use PhpDto\ToArray;

class ItemDto extends Dto
{
	use DtoSerialize, ToArray;

	private int $_id;
	private ?int $_count;
	private string $_name;
	private ?string $_description;
	private bool $_isActive;

	public function __construct( array $item )
	{
	    $this->_id = $item['id'];
	    $this->_count = $item['count'];
	    $this->_name = $item['name'];
	    $this->_description = $item['description'];
	    $this->_isActive = $item['is_active'];
	}

	public function getId(): int
	{
	    return $this->_id;
	}

	public function getCount(): ?int
	{
	    return $this->_count;
	}

	public function getName(): string
	{
	    return $this->_name;
	}

	public function getDescription(): ?string
	{
	    return $this->_description;
	}

	public function getIsActive(): bool
	{
	    return $this->_isActive;
	}
}
如何使用

有两种映射方法

静态函数 mapArray(array $items, bool $shouldSerialize = false): array

静态函数 mapSingle(array $item, bool $shouldSerialize = false): Dto|stdClass

当您需要映射多维数组时使用 mapArray,否则使用 mapSingle

映射示例

单例
$itemData = [
    'id' => 1,
    'name' => 'Dummy Item',
    'description' => 'Some dummy description.',
    'count' => 10,
    'is_active' => true,
    'meta' => [
        'meta_title' => 'Dummy meta title',
        'meta_description' => 'Dummy meta description',
    ],
    'tags' => 'TagOne, TagTwo, TagThree'
];

$item = ItemDto::mapSingle( $itemData );

// Now you are able to access DTO properties via getters

$item->getId();
$item->Name();
$item->getDescription();
$item->getCount();
$item->getIsActive();
多维
$itemData = [
    [
        'id' => 1,
            'name' => 'Dummy Item',
            'description' => 'Some dummy description.',
            'count' => 10,
            'is_available' => true, 
            'meta' => [
                'meta_title' => 'Dummy meta title',
                'meta_description' => 'Dummy meta description',
            ],
            'tags' => 'TagOne, TagTwo, TagThree'
    ],
    // more items
];

$items = ItemDto::mapArray( $itemData ); // array of instance of ItemDto class

foreach( $items as $item )
{
    $item->getId();
    // ... 
}

考虑根据要映射的数组的数据结构重构生成的 DTO 类的构造函数。

有时您可能希望将 DTO 作为对象使用,以便可以将它们传递到 AJAX 响应或其他所需位置。

映射方法的双参数是决定数据是否应序列化的标志。

ItemDto::mapSingle( $itemData, true ) - 这将返回一个序列化对象的 DTO

{
  "id": 1
  "name": "Dummy Item"
  "description": "Some dummy description."
  "count": 10
  "isActive": true
}

对于 mapArray 方法也是如此。

DTO 模拟器

您可以使用 PhpDto\Services\DtoFaker 类轻松地为您的 DTO 生成假数据。

$fakeData = DtoFaker::fakeSingle( ItemDto::class ); // array that contains fake data for ItemDto
$item = ItemDto::mapSingle( $fakeData );

现在您的项目看起来像这样

{
    "id": 993
    "count": 340
    "name": "2R9ifLLxfG965wikJWrr"
    "description": "MluADBj2rwmAjBC6ZyH4"
    "isActive": false
}

所有值都是随机生成的,甚至是 isActive 字段的布尔值。

您可以通过 DtoFaker::fakeArray 方法伪造多维数组。

以下示例中,我们想要伪造 10 个项目的数据。

$fakeData = DtoFaker::fakeArray( ItemDto::class, 10 );
$items = ItemDto::mapArray( $fakeData );

Dto::fakeArray 方法的第二个参数是生成项目的数量。

Dto::fakeSingleDto::fakeArray 方法使用 PHP 反射 API 获取关于属性和获取器的信息。

或者,您可以使用 Dto::fakeSingeFromPatternDto::fakeArrayFromPattern 方法。您必须传递它们完整的 json 模式路径
Dto::fakeArrayFromPattern('/full/path/to/pattern.json').

ToArray 特性和 toArray(): array 方法。

当您需要将 DTO 对象转换为数组时,可以使用 toArray 方法。

<?php

namespace App\Dto;

use PhpDto\Dto;
use PhpDto\ToArray;

class MockDto extends Dto
{
    use ToArray;
    
    private ?string $_name;
    private int $_count;
    private bool $_isTrue;
    
    public function __construct( array $data )
    {
        $this->_name   = $data['name'];
        $this->_count  = $data['count'];
        $this->_isTrue = $data['is_true'];
    }
    
    public function getName(): ?string
    {
        return $this->_name;
    }
    
    public function getCount(): int
    {
        return $this->_count;
    }

    public function getIsTrue(): bool
    {
        return $this->_isTrue;
    }
}

$mockData = [
    'name' => 'Mock name',
	'count'   => 4,
	'is_true' => true
];

$dto = new MockDto($mockData);

$arr = $dto->toArray();

var_dump($arr);

输出将如下所示

array(3) {
  'name' =>
  string(9) "Mock name"
  'count' =>
  int(4)
  'is_true' =>
  bool(true)
}

toArray 方法接受两个参数:toSnakeCaseincludeNulls

如果您想保持数组键的格式与类字段相同,可以传递 toSnakeCase: false 参数:$arr = $dto->toArray(toSnakeCase: false);

如果您想包含具有 null 值的键,可以传递 includeNulls: true 参数:$arr = $dto->toArray(includeNulls: true);

输出将是

array(3) {
  'name' =>
  string(9) "Mock name"
  'count' =>
  int(4)
  'isTrue' =>
  bool(true)
}

所以现在 isTrue 键是驼峰式。