othercodes/

ddd-value-object

此包已被废弃,不再维护。作者建议使用complex-heart/domain-model包代替。

一个小型库,可轻松应用值对象模式。

v1.1.0 2020-10-16 13:26 UTC

This package is auto-updated.

Last update: 2021-06-16 09:08:00 UTC


README

Tests License codecov

一个小型库,可轻松管理值对象模式。

安装

使用以下命令通过 composer 进行安装

composer require othercode/ddd-value-object

这将自动获取最新版本并配置 composer.json 文件。

或者,您可以创建以下 composer.json 文件并运行 composer install 以进行安装。

{
    "require": {
        "othercode/ddd-value-object": "*"
    }
}

用法

构建值对象非常简单,您只需扩展 ValueObject 类。接下来,通过使用 initialize 方法在构造函数中初始化值。最后,添加不变规则为受保护方法,使用前缀 invariant(此前缀可以自定义)并使用 checkInvariants 方法执行它们。

<?php 

declare(strict_types=1);

class Speed extends OtherCode\DDDValueObject\ValueObject
{
    public const KILOMETERS_PER_HOUR = 'km/h';
    public const MILES_PER_HOUR = 'm/h';

    public function __construct(int $amount, string $magnitude)
    {
        $this->initialize([
            'amount' => $amount,
            'magnitude' => $magnitude
        ]);

        $this->checkInvariants();
    }

    protected function invariantSpeedMustBeGreaterThanZero(): bool
    {
        return $this->amount() > 0;
    }

    protected function invariantMagnitudeMustBeValid(): bool
    {
        return in_array($this->magnitude(), [
            self::KILOMETERS_PER_HOUR,
            self::MILES_PER_HOUR
        ]);
    }

    public function amount(): int
    {
        return $this->get('amount');
    }

    public function magnitude(): string
    {
        return $this->get('magnitude');
    }

    public function increase(Speed $speed): self
    {
        if ($speed->magnitude() !== $this->magnitude()) {
            throw new InvalidArgumentException('The given magnitude is not valid.');
        }

        return new self($this->amount() + $speed->amount(), $this->magnitude());
    }

    public function __toString(): string
    {
        return $this->amount() . $this->magnitude();
    }
}

相等性

值相等性通过序列化对象并使用 sha256 算法对其进行哈希计算得出。或者,您可以通过重写 equalityHash 来计算对象的适当哈希值。此哈希值用于检查值对象是否相等。

<?php

declare(strict_types=1);

class Speed extends OtherCode\DDDValueObject\ValueObject
{
// ...
    public function equalityHash(): string
    {
        return md5(sprintf('$s %s', $this->amount(), $this->magnitude());
    }
// ...
}

不可变性

不可变属性阻止对值的任何修改尝试,这将导致异常

$s = new Speed(120, 'km/h');
$s->amount = 123;

// PHP Fatal error:  Uncaught OtherCode\DDDValueObject\Exceptions\ImmutableValueException: Illegal attempt to change immutable value.

您可以通过重写 immutabilityException 属性来自定义将被抛出的异常。同样,对于错误消息,您只需要重写 immutabilityMessages 属性。

<?php

declare(strict_types=1);

class Speed extends OtherCode\DDDValueObject\ValueObject
{
// ...
    protected string $immutabilityException = SomeCustomException::class;

    protected array $immutabilityMessages = [
        'default' => 'You shall not update this value!.',
    ];
// ...
}

不变性

不变性方法必须返回一个布尔值,true 如果成功,否则 false。如果违反任何不变性,您将获得一个异常

<?php
$s = new Speed(-1, 'm/s');

// PHP Fatal error:  Uncaught InvalidArgumentException: Unable to create Speed value object due: 
// invariant speed must be greater than zero
// invariant magnitude must be valid

默认情况下,不变性名称会被解析并用作违反不变性时的错误消息,但您可以轻松自定义,只需在不变性中抛出自定义消息的异常而不是返回 false

<?php

declare(strict_types=1);

class Speed extends OtherCode\DDDValueObject\ValueObject
{
// ...
    protected function invariantSpeedMustBeGreaterThanZero(): bool
    {
        if($this->amount() < 0) {
            throw new InvalidArgumentException('The given speed value is not valid');
        }

        return true;
    }
// ...
}

$s = new Speed(-1, 'm/s');

// PHP Fatal error:  Uncaught InvalidArgumentException: Unable to create Speed value object due: 
// The given speed value is not valid
// invariant magnitude must be valid

此外,您还可以通过传递一个自定义函数到 checkInvariants 方法来完全自定义如何管理不变性违反。

<?php 

declare(strict_types=1);

class Speed extends OtherCode\DDDValueObject\ValueObject
{
// ...
    public function __construct(int $amount, string $magnitude)
    {
        $this->initialize([
            'amount' => $amount,
            'magnitude' => $magnitude
        ]);

        $this->checkInvariants(function (array $violations) {
           throw new RuntimeException("Epic fail due:\n-" . implode("\n-", $violations) . "\n");
       });
    }

    protected function invariantSpeedMustBeGreaterThanZero(): bool
    {
        if($this->amount() < 0) {
            throw new InvalidArgumentException('The given speed value is not valid');
        }

        return true;
    }
// ...
}

$s = new Speed(-120, 'clicks/s');

// PHP Fatal error:  Uncaught RuntimeException: Epic fail due:
// - The given speed value is not valid
// - invariant magnitude must be valid