xthiago/id-value-object

对象标识(ID)的值对象(VO)。包括用于持久化的Doctrine DBAL类型。

1.1.0 2023-06-19 04:02 UTC

This package is auto-updated.

Last update: 2024-09-19 07:22:36 UTC


README

PHP 库,用于使处理对象标识(ID)更加容易和有趣!

简而言之:你不应该依赖数据库机制来生成 ID。同样,你不应该在应用程序中操作标量值(通常是 int)。无论何时需要生成 ID,请使用此 ID 值对象。

功能

UUID v4

该库会自动生成 UUID v4 作为 ID。你将不会依赖持久化机制和刷新操作来生成标识。

与 Doctrine 集成

该库包含一个 Doctrine DBAL 类型,允许你将 ID 值对象映射为 Doctrine 实体属性。

需求

此库需要 PHP >= 7.2。

安装

使用 composer 安装此库。

composer install xthiago/id-value-object

Doctrine

为了使用此包提供的 Doctrine 持久化集成,你必须配置 Doctrine DBAL 类型。

独立 Doctrine

你必须在应用程序启动时注册 DBAL 类型,如下所示

<?php

\Doctrine\DBAL\Types\Type::addType(
    'xthiago_id', 
    \Xthiago\ValueObject\Id\Persistence\DoctrineDbalType::class
);

Symfony 框架

如果你使用的是 Symfony,你只需要编辑以下 Doctrine 配置,添加以下内容

doctrine:
  dbal:
    types:
      xthiago_id: Xthiago\ValueObject\Id\Persistence\DoctrineDbalType

使用

基本示例

<?php
namespace YourApp;

use Xthiago\ValueObject\Id\Id;

// Generate a new ID (UUID v4):
$generatedId = Id::generate();
echo $generatedId; // prints something like `b18c7bbe-da70-4c86-8b8f-145abb21a7c7`.

// Create an ID from string:
$parsedId = Id::fromString('Foo');
echo $parsedId; // prints 'Foo'.

// Comparing two instances of Id:
var_dump($generatedId->isEqualTo($parsedId)); // prints: `false`
var_dump($parsedId->isEqualTo(Id::fromString('Foo'))); // prints: `true`   

在实体类中映射 ID(例如 Product)

<?php
namespace YourApp;

use Doctrine\ORM\Mapping as ORM;
use Xthiago\ValueObject\Id\Id;

/**
 * @ORM\Entity()
 */
class Product
{
    /**
     * @ORM\Id
     * @ORM\Column(type="xthiago_id", name="id")
     *
     * @var Id 
     * @psalm-var Id<Product>   
     */
    private $id;
    
    // other attributes goes here.
    
    public function __construct(Id $id) 
    {
        $this->id = $id;
    }
    
    /** @psalm-return Id<Product> */
    public function id(): Id
    {
        return $this->id();
    }
}

创建自定义 ID 类

对于所有实体,你不应该依赖于通用的 Id 类,你可以为每个实体创建特定的值对象。

例如,如果你有一个 Product 实体,你可以按照以下方式创建一个 ProductId 值对象

class ProductId extends Id 
{
}

然后你需要映射这种新类型。你可以按照以下方式扩展 DoctrineDbalType

class ProductIdDbalType extends DoctrineDbalType
{
    public const NAME = 'product_id';

    public function getConcreteIdClass(): string
    {
        return ProductId::class;
    }
}

然后你配置这种新类型(例如 Type::addType(ProductIdDbalType::NAME, ProductIdDbalType::class);)并开始在模型中使用它(这次我将使用 PHP 属性来展示映射)

<?php
namespace YourApp;

use Doctrine\ORM\Mapping as ORM;

#[
    ORM\Entity,
    ORM\Table('product')
]
class Product
{
    public function __construct(
        #[
            ORM\Column(name: 'id', type: ProductIdDbalType::NAME),
            ORM\Id
        ]
        private ProductId $id,
    ) 
    {}
}

Symfony 序列化

此库包含一个类 IdInterfaceNormalizer,能够序列化和反序列化 Id 值对象实例。

要使用此序列化器,你需要将其作为参数传递给 Serializer 类的构造函数

$serializer = new Serializer(
    normalizers: [
        new IdInterfaceNormalizer(), // <---
        // others normalizers...
    ],
    encoders: [new JsonEncoder()]
);

FreezesUuidTrait 工具用于测试

在创建测试时,我们可能希望提前知道生成的 ID 的值。这让我们能够编写更简单的断言。此库提供了一个 trait FreezesUuidTrait,可以用来冻结 UUID v4 生成或设置已知序列。

class MyAwesomeTest extends Testcase
{
    use FreezesUuidTrait;
    
    protected function setUp(): void
    {
        // we could freeze the uuids here :)
    }

    protected function tearDown(): void
    {
        $this->unfreezeUuid(); // <-- This is important to unfreeze the uuid generation.
    }
    
    public function test_fixed_uuid(): void
    {
        // Fixing the uuid value (this can also be set on `setUp()` method):
        $this->freezeUuidV4WithFixedValue('866cc948-b6de-4cdc-8f5e-3b53a58a9f63');
        
        // All generated Id will have the same uuid value.
        $this->assertSame('866cc948-b6de-4cdc-8f5e-3b53a58a9f63', (string) Id::generate());
        $this->assertSame('866cc948-b6de-4cdc-8f5e-3b53a58a9f63', (string) Id::generate());
        $this->assertSame('866cc948-b6de-4cdc-8f5e-3b53a58a9f63', (string) Id::generate());
    }
    
    public function test_fixed_uuid_sequence(): void
    {
        // Fixing the uuid value with a known sequence:
        $this->freezeUuidV4WithKnownSequence(
            'e2d5d0fd-a719-4da7-976d-b5cd184fa615'
            '4c98d212-19ed-4c41-9ea2-4b2d48d7410d'
            '2acfaf05-25c3-4db9-9fea-5d71a0c3f909'
        );
        
        // Each new Id instance will assume one uuid from the known sequence:
        $this->assertSame('e2d5d0fd-a719-4da7-976d-b5cd184fa615', (string) Id::generate());
        $this->assertSame('4c98d212-19ed-4c41-9ea2-4b2d48d7410d', (string) Id::generate());
        $this->assertSame('2acfaf05-25c3-4db9-9fea-5d71a0c3f909', (string) Id::generate());
        
        // If we call again Id::generate(), it will throw RuntimeException because there is no remaining uuid in the 
        // available list.
        $this->assertException(RunTimeException::class);
        Id::generate();
        $this->fail('The expected exception was not thrown.');
    }    
}

贡献

欢迎提交拉取请求。对于重大更改,请先打开一个问题来讨论你想要进行的更改。

请确保根据需要更新测试。

许可证

MIT