firemidge / value-objects
与值对象一起工作的便捷方法
Requires
- php: ^8.1
- ext-mbstring: *
Requires (Dev)
- ext-gd: *
- phpunit/phpunit: ^9
- dev-master
- v2.6
- v2.5
- v2.4
- v2.3
- v2.2
- v2.1
- v2.0.1
- v2.0
- v1.1
- v1.0
- dev-feature/add-default-ready-to-use-classes
- dev-feature/class-array-enum-type
- dev-feature/add-methods-mathematical-fromrawarray-etc
- dev-feature/strict-check-on-array-types
- dev-feature/add-methods-is-equal-to-and-with-values
- dev-dev/improve-code-coverage
- dev-feature/added-class-collection-type-and-more
- dev-fix/all-method-not-static
- dev-feature/upgrade-to-php-8.1
- dev-feature/added-new-value-types
This package is auto-updated.
Last update: 2024-09-23 12:38:59 UTC
README
此库提供创建值对象的便捷方法。
您可以使用以下表格来决定哪种类型最适合您。 "单个值" 表示对象将保存单个值,而 "值数组" 表示对象可以保存多个值。
质量控制
以下表格在每个代码更新时都会更新,并且是在 PhpUnit(单元测试工具)和 Infection(突变测试工具)的帮助下生成的。
IsStringEnumType
当有一组固定有效值,且您的对象代表单个值时使用此类型。
如果有一组固定有效值,但您的对象代表值数组,则使用 IsStringArrayEnumType
。
示例
class Season { use IsStringEnumType; public const SPRING = 'spring'; public const SUMMER = 'summer'; public const AUTUMN = 'autumn'; public const WINTER = 'winter'; public static function all() : array { return [ self::SPRING, self::SUMMER, self::AUTUMN, self::WINTER, ]; } }
用法
$spring = Season::fromString(Season::SPRING);
IsIntEnumType
当有一组固定有效值,且您的对象代表单个值时使用此类型。
如果有一组固定有效值,但您的对象代表值数组,则使用 IsIntArrayEnumType
。
示例
class Status { use IsIntEnumType; public const INFORMATION = 1; public const SUCCESS = 2; public const REDIRECTION = 3; public const CLIENT_ERROR = 4; public const SERVER_ERROR = 5; public static function all() : array { return [ self::INFORMATION, self::SUCCESS, self::REDIRECTION, self::CLIENT_ERROR, self::SERVER_ERROR, ]; } }
用法
$success = Status::fromInt(Status::SUCCESS);
IsEmailType
当值代表单个电子邮件地址时使用此类型。此特性在底层使用 IsStringType
,但执行标准电子邮件验证。
示例
class Email { use IsEmailType; }
用法
$email = Email::fromString('hello@there.co.uk');
IsStringType
当值代表单个字符串值,但没有固定有效值集时使用此类型。
如果您期望电子邮件地址,则可以使用执行格式验证检查的 IsEmailType
特性。
验证
要提供自定义验证,重写 protected function validate(string $value) : void
。
如果您只想验证字符串的长度,可以在 validate
方法内部调用 validateLength(string $value, ?int $minLength = null, ?int $maxLength = null) : void
。
字符串转换
如果您想要转换输入值但不使验证失败,则重写 protected function transform(string $value) : string
。
如果您在 transform
内部想要调用,有 3 个便捷方法可用
trimAndLowerCase(string $value)
trimAndUpperCase(string $value)
trimAndCapitalise(string $value)
示例
class ProductName { use IsStringType; protected function transform(string $value) : string { return $this->trimAndCapitalise($value); } protected function validate(string $value) : void { $this->validateLength($value, 2, 50); } }
用法
// $productName will be 'Orange juice' $productName = ProductName::fromString(' orange juice');
IsIntType
当值代表单个整数值,但没有固定有效值列表,或无法列出每个有效值时使用此类型。
验证
您可以通过重写 protected function validate(int $value) : void
提供自定义验证规则。默认情况下,它将验证该值是否为正整数。
如果您只想验证一个值是否在某个最小值和最大值之间,重写 protected static function minValidValue() : ?int
和 protected static function maxValidValue() : ?int
。从任一返回 NULL
表示没有最小值或最大值的限制。
示例
class Percentage { use IsIntType; protected static function minValidValue() : ?int { return 0; } protected static function maxValidValue() : ?int { return 100; } }
另一个示例,对于没有任何限制的值
class Balance { use IsIntType; protected static function minValidValue() : ?int { return null; } }
另一个示例,对于没有上限但可能永远不会低于 5 的值
class Investment { use IsIntType; protected static function minValidValue() : ?int { return 5; } // It is not necessary to add this in as this is the default. protected static function maxValidValue() : ?int { return null; } }
另一个示例,仅允许奇数值
class OddIntType { use IsIntType; protected function validate(int $value) : void { if ($value % 2 === 0) { throw new InvalidValue(sprintf('Only odd values allowed. Value provided: %d', $value)); } } }
用法
$percentage = Percentage::fromInt(78);
IsFloatType
当值代表单个浮点值时使用此类型。
验证
您可以通过重写 protected function validate(float $value) : void
来提供自定义验证规则。默认情况下,它只会验证浮点数是否大于0,但您可以通过重写 minValidValue
来允许无限值。
如果您只想验证值是否在某个最小值和最大值之间,则重写 protected static function minValidValue() : ?float
和 protected static function maxValidValue() : ?float
。从任一返回 NULL
表示分别没有最小值或最大值的限制。
示例,允许值为0到100之间,并且自动截断小数点后的第3位。
class Percentage { use IsFloatType; protected static function minValidValue() : ?float { return 0; } protected static function maxValidValue() : ?float { return 100; } protected function transform(float $value) : float { return round($value, 2); } }
用法
// $percentage will be 78.58 $percentage = Percentage::fromFloat(78.578);
IsIntStringMapType
当值表示一个可以映射到整数和字符串之间的单个值时,使用此类型。
当您例如将值存储在数据库中作为整数(用于更快的索引)时,这可能很有用,但将其转换为字符串用于公共API(以提高可读性)。
示例
class Season { use IsIntStringMapType; protected static function provideMap() : array { return [ 1 => 'spring', 2 => 'summer', 3 => 'autumn', 4 => 'winter', ]; } }
用法
// Returns 'summer' $label = (Season::fromInt(2))->toString(); // Returns 4 $intValue = (Season::fromString('winter'))->toInt();
IsIntArrayEnumType
当值表示一个整数值的数组,其中每个值必须是固定列表中的一个值时使用此类型。
例如,在构建过滤器时很有用,允许选择要包含在结果中的状态或ID(或其他)数量。
唯一值
如果每个值只能在该对象中出现一次,您有两个选择
- 如果您希望在添加重复值时抛出异常(无论是通过
fromArray
还是withValue
),则重写protected static function areValuesUnique() : bool
并返回true
。将抛出类型为DuplicateValue
的异常。 - 如果您不希望抛出异常但希望重复值简单地被静默忽略(在
fromArray
和withValue
中),则重写protected static function ignoreDuplicateValues() : bool
并返回true
。如果发现重复值,它们只会添加一次到数组中。
当 areValuesUnique
和 ignoreDuplicateValues
都返回 true
时,ignoreDuplicateValues
优先。
示例
class Statuses { use IsIntArrayEnumType; public const INFORMATION = 1; public const SUCCESS = 2; public const REDIRECTION = 3; public const CLIENT_ERROR = 4; public const SERVER_ERROR = 5; public static function all() : array { return [ self::INFORMATION, self::SUCCESS, self::REDIRECTION, self::CLIENT_ERROR, self::SERVER_ERROR, ]; } protected static function areValuesUnique() : bool { return true; } }
用法
$statusesToInclude = Statuses::fromArray([Statuses::INFORMATION, Statuses::SUCCESS]); $allStatuses = Statuses::withAll(); $statuses = (Statuses::fromArray([])) ->withValue(Statuses::SUCCESS) ->withValue(Statuses::SERVER_ERROR) ->withoutValue(Statuses::SUCCESS); // The difference between tryWithoutValue and withoutValue is that the try method // will throw an exception if you are trying to remove a value that did not previously // exist, whereas withoutValue will simply ignore it. $statusesWithoutSuccess = $statuses->tryWithoutValue(Statuses::SUCCESS); $containsSuccess = $statusesToInclude->contains(Statuses::SUCCESS);
IsStringArrayEnumType
当值表示一个字符串值的数组,其中每个值必须是固定列表中的一个值时使用此类型。
例如,在构建过滤器时很有用,允许选择要包含在结果中的字段数量。
唯一值
如果每个值只能在该对象中出现一次,您有两个选择
- 如果您希望在添加重复值时抛出异常(无论是通过
fromArray
还是withValue
),则重写protected static function areValuesUnique() : bool
并返回true
。将抛出类型为DuplicateValue
的异常。 - 如果您不希望抛出异常但希望重复值简单地被静默忽略(在
fromArray
和withValue
中),则重写protected static function ignoreDuplicateValues() : bool
并返回true
。如果发现重复值,它们只会添加一次到数组中。
当 areValuesUnique
和 ignoreDuplicateValues
都返回 true
时,ignoreDuplicateValues
优先。
示例
class UserFieldList { use IsStringArrayEnumType; public const NAME = 'name'; public const EMAIL = 'email'; public const STATUS = 'status'; public const FRIEND_LIST = 'friendList'; protected static function all() : array { return [ self::NAME, self::EMAIL, self::STATUS, self::FRIEND_LIST, ]; } }
用法
$fields = $fieldsFromRequest === null ? UserFieldList::withAll() : UserFieldList::fromArray($fieldsFromRequest); $fields = UserFieldList::fromArray([UserFieldList::NAME, UserFieldList::EMAIL]); $allFields = UserFieldList::withAll(); $fields = (UserFieldList::fromArray([])) ->withValue(UserFieldList::FRIEND_LIST) ->withValue(UserFieldList::STATUS) ->withoutValue(Statuses::FRIEND_LIST); $containsFriendList = $statusesToInclude->contains(UserFieldList::FRIEND_LIST);
IsClassArrayEnumType
当值表示一个类实例的数组,并且有一个有效值的列表时使用此类型。这意味着类实例代表枚举类型。
示例
class Sources { use IsClassArrayEnumType; protected static function className() : string { return Source::class; } }
它与使用 IsStringArrayEnumType
或 IsIntArrayEnumType
非常相似,除了此数组类型中的每个项目都是一个类实例。这意味着可以单独添加项目,而无需将其转换为标量类型,如下面的使用示例所示
用法
$source = Source::fromString('invitation'); $sources = Sources::empty(); $sources = $sources->withValue($source);
因为实现 IsClassArrayEnumType
的类包含对象,所以可以在返回的元素上执行方法调用,如下面的示例所示
用法
$sources = Sources::withAll(); // $sources now holds an array with ALL possible Source values. // Compare the first element that was added to $sources: $sources->first()->isEqualTo(Source::invitation()); // Find a specific value. Returns `null` if the element does not exist in $sources. $sourceOrNull = $sources->find(fn(Source $src) => $src->isEqualTo(Source::invitation())); // You can also perform a pre-check whether a specific value exists in the instance of `IsClassArrayEnumType`: $containsInvitation = $sources->contains(Source::invitation());
唯一值
默认情况下,可以将相同的值多次添加到同一实例中。要控制此行为,请参阅下面的示例
class Sources { // Other code here... /** * This method is linked to ignoreDuplicateValues() - therefore, it is important what both of them do * in order to determine the eventual behaviour. * * Returning `true` here causes a `DuplicateValue` exception to be thrown when duplicate values are added, * either via `fromArray` or `withValue` - UNLESS you also return `true` from `ignoreDuplicateValues()`. * * Returning `false` here and from `ignoreDuplicateValues()` means the same values can be * added multiple times. * * Default: Returns `false` unless overridden. */ protected static function areValuesUnique() : bool { return true; } /** * Returning `true` here means that when something attempts to add the same value to an instance * more than once, any duplicate values will be silently ignored (no exceptions thrown) - this * is the behaviour regardless of what `areValuesUnique` returns. * * Default: Returns `false` unless overridden. */ protected static function ignoreDuplicateValues() : bool { return true; } }
如果每个值只能在该对象中出现一次,您有两个选择
- 如果您希望在添加重复值时抛出异常(无论是通过
fromArray
还是withValue
),则重写protected static function areValuesUnique() : bool
并返回true
。将抛出类型为DuplicateValue
的异常。 - 如果您不希望抛出异常但希望重复值简单地被静默忽略(在
fromArray
和withValue
中),则重写protected static function ignoreDuplicateValues() : bool
并返回true
。如果发现重复值,它们只会添加一次到数组中。
当 areValuesUnique
和 ignoreDuplicateValues
都返回 true
时,ignoreDuplicateValues
优先。 注意:为了执行这些重复检查,首先将值对象转换为字符串。如果您使用自定义类并希望进行这些检查,请确保您已经实现了 __toString
方法。(如果您在此库中使用任何类型,__toString
已经在它们上面实现了。)
验证
默认情况下,每个元素都会被验证是否为对象以及是否是className()
返回的特定类的实例。但是,如果您想执行额外的验证,可以重写protected function validateEach(mixed $value) : void
,该函数为每个值单独执行,无论是在实例化时还是在调用withValue
时。请注意,此验证也会在调用withoutValue
、tryWithoutValue
和contains
之前运行,因此您会在传递完全无效的内容时收到通知,而不是静默地吞没。请确保也调用parent::validateEach($value);
,除非您想在重写版本中重复默认验证行为。
从原始值
如果您想从“原始”值(而不是类的实例)实例化您的集合以方便起见(同时在内部将它们转换为相关的实例),可以使用fromRawValues
。
示例
$sources = Sources::fromRawArray([ 'invitation', 'promotion', 'reference', ]);
这适用于将实例转换为实现了fromString
、fromInt
、fromBool
、fromFloat
、fromDouble
、fromNumber
或通过它们的构造函数接受相关参数的实例的转换。请注意,输入类型不会被转换。这意味着如果您传递一个string
,只有fromString
工厂方法将被尝试。如果上述任何一种都不存在或失败,特性将尝试将值传递到目标类的构造函数中。(如果这也失败,将抛出ConversionError
。)
自定义转换
如果您想使用fromRawValues
方法,但目标类既没有前面提到的任何方法,也没有任何实例化方式,您有三个选项
1) 提供自定义回调
如果您只需要进行一次自定义转换,可以直接向fromRawValues
方法提供一个回调。
示例
$months = CustomEnumArray::fromRawArray([ 'January', 'May', 'July', ], fn($v) => CustomClass::fromMonth($v)));
2) 重写convertFromRaw
如果您在类上多次使用自定义转换,您可以选择重写protected static function convertFromRaw(mixed $value) : object
,以便在每次调用fromRawValues
时自动使用您的自定义转换器。
示例
class CustomEnumArray { use IsClassCollectionType { IsClassCollectionType::convertFromRaw as private _convertFromRaw; } protected static function className() : string { return CustomClass::class; } protected static function convertFromRaw(mixed $value) : object { try { return static::_convertFromRaw($value); } catch (ConversionError) { return CustomClass::fromMonth($value); } } }
3) 实现您自己的工厂方法
由于一切只是一个特性,当然您可以选择简单地创建自己的,并替换fromRawValues
。如果您想为您的自己的方法保持相同的名称并更改签名,只需将特性的方法别名并使其为私有。
用法
$months = Months::fromArray([ Month::fromString('December'), Month::fromString('August'), Month::fromString('October'), ]); // Alternative way of instantiating the enum collection, if the values // passed can be converted to the target class. $months = Months::fromRawArray([ 'December', 'August', 'October', ]; // Returns 3 $numberOfMonths = $months->count(); // Returns `true`, although strings are passed, as long as `Month` // implements the `__toString` method (e.g. via the trait `IsStringType`). $emailsMatch = $emails->isEqualTo([ 'December', 'August', 'October', ]);
IsArrayEnumType
当值表示除了string
、integer
或特定类的实例(对于这些我们有IsStringArrayEnumType
、IsIntArrayEnumType
和IsClassArrayEnumType
分别)之外的其他类型的值的数组,并且存在有效值的列表时,使用此类型。
与其他类型的组合
您可以与此类型与其他任何类型组合,例如,以获取浮点类型数组或整型枚举类型数组等。与使用IsStringEnumType
和IsArrayEnumType
组合而不是IsStringArrayEnumType
的区别在于,在前者的情况下,每个值都是一个值对象,而在后者中,每个值只是一个标量字符串。当然,您也可以简单地使用更新的IsClassArrayEnumType
,它结合了IsArrayEnumType
和IsClassCollectionType
,允许您持有值对象的实例。有关更多信息,请参阅IsClassArrayEnumType
。
唯一值
默认情况下,可以将相同的值多次添加到同一实例中。要控制此行为,请参阅下面的示例
class Sources { // Other code here... /** * This method is linked to ignoreDuplicateValues() - therefore, it is important what both of them do * in order to determine the eventual behaviour. * * Returning `true` here causes a `DuplicateValue` exception to be thrown when duplicate values are added, * either via `fromArray` or `withValue` - UNLESS you also return `true` from `ignoreDuplicateValues()`. * * Returning `false` here and from `ignoreDuplicateValues()` means the same values can be * added multiple times. * * Default: Returns `false` unless overridden. */ protected static function areValuesUnique() : bool { return true; } /** * Returning `true` here means that when something attempts to add the same value to an instance * more than once, any duplicate values will be silently ignored (no exceptions thrown) - this * is the behaviour regardless of what `areValuesUnique` returns. * * Default: Returns `false` unless overridden. */ protected static function ignoreDuplicateValues() : bool { return true; } }
如果每个值只能在该对象中出现一次,您有两个选择
- 如果您希望在添加重复值时抛出异常(无论是通过
fromArray
还是withValue
),则重写protected static function areValuesUnique() : bool
并返回true
。将抛出类型为DuplicateValue
的异常。 - 如果您不希望抛出异常但希望重复值简单地被静默忽略(在
fromArray
和withValue
中),则重写protected static function ignoreDuplicateValues() : bool
并返回true
。如果发现重复值,它们只会添加一次到数组中。
当 areValuesUnique
和 ignoreDuplicateValues
都返回 true
时,ignoreDuplicateValues
优先。 注意:为了执行这些重复检查,首先将值对象转换为字符串。如果您使用自定义类并希望进行这些检查,请确保您已经实现了 __toString
方法。(如果您在此库中使用任何类型,__toString
已经在它们上面实现了。)
验证
您可以通过覆盖protected function validateEach(mixed $value) : void
来提供自定义验证,该函数对每个值单独执行,无论是实例化还是调用withValue
时。请注意,此验证也会在withoutValue
、tryWithoutValue
和contains
之前运行,因此当传递完全无效的内容时,您会收到通知,而不是默默吞没。
示例
/** * @method static withValue(Status $addedValue) * @method static tryWithoutValue(Status $value) * @method static contains(Status $value) */ class StatusList { use IsArrayEnumType; protected static function all() : array { return array_map(function($value) { return Status::fromInt($value); }, Status::all()); } protected function validateEach(mixed $value) : void { if (! is_object($value) || (! $value instanceof Status)) { throw InvalidValue::notInstanceOf($value, Status::class); } } protected static function areValuesUnique() : bool { return true; } protected static function ignoreDuplicateValues() : bool { return true; } }
请注意,上述示例仅用于演示目的 - 所有的上述功能都是通过使用IsClassArrayEnumType
开箱即用的。
用法
$statuses = StatusList::fromArray([Status::SUCCESS, Status::REDIRECTION]); $allStatuses = StatusList::withAll(); // $duplicateStatusesIgnored will only contain Status::SUCCESS once. // [ Status::SUCCESS, Status::REDIRECTION ] // This is because of `ignoreDuplicateValues` returning true. $duplicateStatusesIgnored = StatusList::fromArray([ Status::SUCCESS, Status::REDIRECTION, Status::SUCCESS, ]) // $newStatuses will only contain one instance of Status::REDIRECTION. // This is because of `ignoreDuplicateValues` returning true. $newStatuses = $statuses->withValue(Status::REDIRECTION);
IsClassCollectionType
当值表示一个值数组,其中每个值都必须是类的实例且没有有限的有效值列表时,请使用此类型。如果存在有效值的列表,请使用IsArrayEnumType
。如果值不是类的实例,请使用IsCollectionType
。
唯一值
如果每个值只能在该对象中出现一次,您有两个选择
- 如果您希望在添加重复值时抛出异常(无论是通过
fromArray
还是withValue
),则重写protected static function areValuesUnique() : bool
并返回true
。将抛出类型为DuplicateValue
的异常。 - 如果您不希望抛出异常但希望重复值简单地被静默忽略(在
fromArray
和withValue
中),则重写protected static function ignoreDuplicateValues() : bool
并返回true
。如果发现重复值,它们只会添加一次到数组中。
当 areValuesUnique
和 ignoreDuplicateValues
都返回 true
时,ignoreDuplicateValues
优先。
验证
您可以通过覆盖protected function validateEach(mixed $value) : void
来提供自定义验证,该函数对每个值单独执行,无论是实例化还是调用withValue
时。请注意,此验证也会在withoutValue
、tryWithoutValue
和contains
之前运行,因此当传递完全无效的内容时,您会收到通知,而不是默默吞没。
示例
/** * @method static withValue(Email $addedValue) * @method static tryWithoutValue(Email $value) * @method static contains(Email $value) */ class EmailCollection { use IsClassCollectionType, CanBeConvertedToStringArray; protected static function className() : string { return Email::class; } }
从原始值
如果您想从“原始”值(而不是类的实例)实例化您的集合以方便起见(同时在内部将它们转换为相关的实例),可以使用fromRawValues
。
示例
$emails = EmailCollection::fromRawArray([ 'hello@there.co.uk', 'lorem@ipsum.it', 'bass@player.at', ]);
这适用于将实例转换为实现了fromString
、fromInt
、fromBool
、fromFloat
、fromDouble
、fromNumber
或通过它们的构造函数接受相关参数的实例的转换。请注意,输入类型不会被转换。这意味着如果您传递一个string
,只有fromString
工厂方法将被尝试。如果上述任何一种都不存在或失败,特性将尝试将值传递到目标类的构造函数中。(如果这也失败,将抛出ConversionError
。)
自定义转换
如果您想使用fromRawValues
方法,但目标类既没有前面提到的任何方法,也没有任何实例化方式,您有三个选项
1) 提供自定义回调
如果您只需要进行一次自定义转换,可以直接向fromRawValues
方法提供一个回调。
示例
$emails = CustomCollection::fromRawArray([ 'hello@there.co.uk', 'lorem@ipsum.it', 'bass@player.at', ], fn($v) => CustomClass::fromDomain(substr($v, strrpos($v, '.') + 1)));
2) 重写convertFromRaw
如果您在类上多次使用自定义转换,您可以选择重写protected static function convertFromRaw(mixed $value) : object
,以便在每次调用fromRawValues
时自动使用您的自定义转换器。
示例
class CustomCollection { use IsClassCollectionType { IsClassCollectionType::convertFromRaw as private _convertFromRaw; } protected static function className() : string { return CustomClass::class; } protected static function convertFromRaw(mixed $value) : object { try { return static::_convertFromRaw($value); } catch (ConversionError) { return CustomClass::fromDomain(substr($value, strrpos($value, '.')+1)); } } }
3) 实现您自己的工厂方法
由于一切只是一个特性,当然您可以选择简单地创建自己的,并替换fromRawValues
。如果您想为您的自己的方法保持相同的名称并更改签名,只需将特性的方法别名并使其为私有。
用法
$emails = EmailCollection::fromArray([ Email::fromString('hello@there.co.uk'), Email::fromString('lorem@ipsum.it'), Email::fromString('bass@player.at'), ]); // Alternative way of instantiating the collection, if the values // passed can be converted to the target class. $emails = EmailCollection::fromRawArray([ 'hello@there.co.uk', 'lorem@ipsum.it', 'bass@player.at', ]; // Returns ['hello@there.co.uk', 'lorem@ipsum.it', 'bass@player.at'] // This method is provided by the trait `CanBeConvertedToStringArray` $emailsAsStrings = $emails->toStringArray(); // Returns 3 $numberOfEmails = $emails->count(); // Returns `true`, even though strings are passed. This is because `Email` // implements the `__toString` method (via the trait `IsStringType`). $emailsMatch = $emails->isEqualTo([ 'hello@there.co.uk', 'lorem@ipsum.it', 'bass@player.at', ]);
IsCollectionType
当值表示一个值数组且没有有限的有效值列表时,请使用此类型。如果存在有效值的列表,请使用IsArrayEnumType
(或任何更具体的变体,例如,如果适用,则使用IsStringArrayEnumType
)。
与其他类型的组合
您可以将此类型与其他任何类型组合,例如,以获取浮点类型数组、电子邮件地址数组等。如果您需要每个值都是类的实例,则考虑使用IsClassCollectionType
。
唯一值
如果每个值只能在该对象中出现一次,您有两个选择
- 如果您希望在添加重复值时抛出异常(无论是通过
fromArray
还是withValue
),则重写protected static function areValuesUnique() : bool
并返回true
。将抛出类型为DuplicateValue
的异常。 - 如果您不希望抛出异常但希望重复值简单地被静默忽略(在
fromArray
和withValue
中),则重写protected static function ignoreDuplicateValues() : bool
并返回true
。如果发现重复值,它们只会添加一次到数组中。
当 areValuesUnique
和 ignoreDuplicateValues
都返回 true
时,ignoreDuplicateValues
优先。
验证
您可以通过覆盖protected function validateEach(mixed $value) : void
来提供自定义验证,该函数对每个值单独执行,无论是实例化还是调用withValue
时。请注意,此验证也会在withoutValue
、tryWithoutValue
和contains
之前运行,因此当传递完全无效的内容时,您会收到通知,而不是默默吞没。
建议至少为值类型设置验证。
值转换
如果您想转换输入值但不失败验证,请覆盖protected function transformEach($value)
。
通过使用特性CanTransformStrings
,您将获得3个便利方法,您可以在transform
内部调用这些方法。
trimAndLowerCase(string $value)
trimAndUpperCase(string $value)
trimAndCapitalise(string $value)
示例
/** * @method static withValue(string $addedValue) * @method static tryWithoutValue(string $value) * @method static contains(string $value) */ class ProductNameCollection { use IsCollectionType; use CanTransformStrings; protected function validateEach(mixed $value) : void { if (! is_string($value)) { throw InvalidValue::invalidType($value, 'string'); } } /** * @param mixed $value * @return mixed */ protected function transformEach($value) { if (! is_string($value)) { return $value; } return $this->trimAndCapitalise($value); } }
用法
// $productNames will be an instance of ProductNameCollection // with these values: [ 'Orange juice', 'Soap', 'Shampoo' ] $productNames = ProductNameCollection::fromArray([ ' orange juice', 'soap ', 'SHAMPOO', ]);