funeralzone / valueobjects
PHP 7.1 值对象辅助库。
Requires
- php: ^7.1 || ^8.0
- beberlei/assert: ^2.7 || ^3.1
- chrisharrison/php-array-of: ^1.0
Requires (Dev)
- mockery/mockery: ^1.0
- phpunit/phpunit: ^6.2 || ^9.3
- squizlabs/php_codesniffer: ^3.1
- dev-master
- 0.5
- 0.4.5
- 0.4.4
- 0.4.3
- 0.4.2
- 0.4
- 0.3.4
- 0.3.3
- 0.3.2
- 0.3.1
- 0.3.0
- 0.2.1
- 0.2.0
- 0.1.0
- 0.0.3
- 0.0.2
- 0.0.1
- 0.0.0
- dev-feature/boolean-method-helpers
- dev-feature/ability-to-get-only-non-null-values-from-a-set
- dev-feature/boolean-helpers
- dev-feature/create-null-nullable
- dev-feature/null-composites
- dev-fix/array-casting
- dev-feature/set-to-array
- dev-feature/set-functionality
- dev-feature/nullable-sets
- dev-fix/mockery-tear-down
This package is auto-updated.
Last update: 2024-09-19 01:56:02 UTC
README
要求
需要 PHP >= 7.1
安装
显然是通过 Composer 来安装
composer require funeralzone/valueobjects
扩展
此库仅处理基本值(标量)。我们还发布了一个扩展库,它为更复杂值的实现提供了一个起点。
我们的方法
我们在PHP 中编写更好的值对象方法一文中记录了我们关于 PHP 值对象的哲学。
单个值对象
如果你的 VO 封装了一个值,那么它很可能是标量。我们在 src/Scalars
下提供了一些特性来处理标量。
假设你有一个名为 'User Email' 的领域值。你需要创建一个实现了 ValueObject
接口类的类
final class UserEmail implements ValueObject { ...
你现在需要实现该接口。但是,因为电子邮件本质上可以被认为是字符串的一种特殊类型(在这种情况下是简单的),所以 StringTrait
辅助特性可以为你实现接口的大部分内容
final class UserEmail implements ValueObject { use StringTrait; ...
在我们的例子中,用户的电子邮件有其他领域逻辑,我们可以将其封装在我们的 VO 中。用户电子邮件必须是有效的电子邮件
... public function __construct(string $string) { Assert::that($string)->email(); $this->string = $string; } ...
你可以在示例目录中看到如何实现单个值对象的示例。
枚举
可以通过使用 EnumTrait
轻松地定义枚举。然后,枚举值简单地作为类上的常量列出。
final class Fastening implements ValueObject { use EnumTrait; public const BUTTON = 0; public const CLIP = 1; public const PIN = 2; public const ZIP = 3; }
在处理值对象序列化时,使用常量名称。它们是区分大小写的。因此
$fastening = Fastening::fromNative('BUTTON'); $fastening->toNative(); // Equals to string: 'BUTTON'
在代码中,特性利用魔术方法根据常量名称创建对象,如下所示
$fastening = Fastening::ZIP(); $fastening->toNative(); // Equals 'ZIP'
如果你的 IDE 支持代码补全,并且你想使用命名方法来创建枚举,则可以在枚举类中添加以下 PHPDoc 块
/** * @method static Fastening BUTTON() * @method static Fastening CLIP() * @method static Fastening PIN() * @method static Fastening ZIP() */ final class Fastening implements ValueObject
复合值对象
复合值对象是由其他值组成的更复杂值。
final class Location implements ValueObject { use CompositeTrait; private $latitude; private $longitude; public function __construct(Latitude $latitude, Longitude $longitude) { $this->latitude = $latitude; $this->longitude = $longitude; } public function getLatitude(): Latitude { return $this->latitude; } public function getLongitude(): Longitude { return $this->longitude; } ...
一个 Location
由两个 VO(纬度,经度)组成。我们提供了一个 CompositeTrait
来轻松实现 ValueObject
接口的大部分内容。它通过反射返回一个包含所有类属性的数组来处理 toNative
序列化。
CompositeTrait
不实现 fromNative
。我们留给您来构建您的对象。
... public static function fromNative($native) { return new static( Latitude::fromNative($native['latitude']), Longitude::fromNative($native['longitude']) ); } ...
你可以在示例目录中看到如何实现复合对象的示例。
Nulls,NonNulls 和 Nullables
此包允许您处理可空值对象。
首先创建一个值对象类型。
interface PhoneNumber extends ValueObject { }
实现一个非空版本的价值对象。
final class NonNullPhoneNumber implements PhoneNumber { use StringTrait; }
实现一个空版本的价值对象。
final class NullPhoneNumber implements PhoneNumber { use NullTrait; }
实现一个可空版本的价值对象。
final class NullablePhoneNumber extends Nullable implements PhoneNumber { protected static function nonNullImplementation(): string { return NonNullPhoneNumber::class; } protected static function nullImplementation(): string { return NullPhoneNumber::class; } }
此 '可空' 处理根据原生输入自动创建接口的空或非空版本。例如
$phoneNumber = NullablePhoneNumber::fromNative(null);
上面的 $phoneNumber
将自动使用上面指定的 NullPhoneNumber
实现。
或者
$phoneNumber = NullablePhoneNumber::fromNative('+44 73715525763');
上面的 $phoneNumber
将自动使用上面指定的 NonNullPhoneNumber
实现。
值对象集合
一套值对象应该实现Set
接口。它只是对ValueObject
接口的简单扩展,增加了几个功能。
interface Set extends ValueObject, \IteratorAggregate, \ArrayAccess, \Countable { public function add($set); public function remove($set); public function contains(ValueObject $value): bool; public function toArray(): array; }
add
将另一个集合中的值添加到当前集合中。remove
从当前集合中移除另一个集合中包含的所有值。contains
如果当前集合中存在该值,则返回true
。toArray
返回一个包含所有值对象的简单PHP数组。
Set
接口扩展的其他接口(\IteratorAggregate
、\ArrayAccess
、\Countable
)用于将集合对象作为数组访问。
非空集合
库提供了一个接口的默认实现。
final class SetOfLocations extends NonNullSet implements Set { protected function typeToEnforce(): string { return Location::class; } public static function valuesShouldBeUnique(): bool { return true; } }
需要实现两个抽象方法。
typeToEnforce
应返回您想要创建集合的值对象的类名字符串。valuesShouldBeUnique
应返回一个布尔值,表示您是否想要强制集合唯一。
如果集合设置为唯一,在实例化或通过add
方法添加重复值时,将过滤掉重复项。
空集合和可空集合
与标准值对象类似,有一些结构可以帮助创建可空集合和空集合。有关更多信息,请参阅“空值、非空值和可空值”部分。
NullableSet
是Nullable
的集合等价物。NullSetTrait
是NullTrait
的集合等价物。
集合的用法
迭代、访问和计数
// Iteration $set = new SetOfLocations([$one, $two]); foreach($set as $value) { // TODO: Do something with each value object } // Access $one = $set[0]; $two = $set[1]; //Counting $count = count($set); // Returns 2
add
合并另一个集合。
$set = new SetOfLocations([$one, $two]); $anotherSet = new SetOfLocations([$three]); $mergedSet = $set->add($anotherSet); count($mergedSet) // Equals: 3
remove
通过使用另一个集合作为参考值来从集合中移除值。
$set = new SetOfLocations([$one, $two, $three]); $anotherSet = new SetOfLocations([$one]); $remove = $set->remove($anotherSet); count($remove) // Equals: 2
contains
检查集合是否包含特定的值对象。
$set = new SetOfLocations([$one, $two, $three]); $one = new Location(0); $check = $set->contains($one);