firemidge/value-objects

与值对象一起工作的便捷方法

v2.6 2023-11-25 23:19 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() : ?floatprotected 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 的异常。
  • 如果您不希望抛出异常但希望重复值简单地被静默忽略(在 fromArraywithValue 中),则重写 protected static function ignoreDuplicateValues() : bool 并返回 true。如果发现重复值,它们只会添加一次到数组中。

areValuesUniqueignoreDuplicateValues 都返回 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 的异常。
  • 如果您不希望抛出异常但希望重复值简单地被静默忽略(在 fromArraywithValue 中),则重写 protected static function ignoreDuplicateValues() : bool 并返回 true。如果发现重复值,它们只会添加一次到数组中。

areValuesUniqueignoreDuplicateValues 都返回 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;
    }
}

它与使用 IsStringArrayEnumTypeIsIntArrayEnumType 非常相似,除了此数组类型中的每个项目都是一个类实例。这意味着可以单独添加项目,而无需将其转换为标量类型,如下面的使用示例所示

用法

$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 的异常。
  • 如果您不希望抛出异常但希望重复值简单地被静默忽略(在 fromArraywithValue 中),则重写 protected static function ignoreDuplicateValues() : bool 并返回 true。如果发现重复值,它们只会添加一次到数组中。

areValuesUniqueignoreDuplicateValues 都返回 true 时,ignoreDuplicateValues 优先。 注意:为了执行这些重复检查,首先将值对象转换为字符串。如果您使用自定义类并希望进行这些检查,请确保您已经实现了 __toString 方法。(如果您在此库中使用任何类型,__toString 已经在它们上面实现了。)

验证

默认情况下,每个元素都会被验证是否为对象以及是否是className()返回的特定类的实例。但是,如果您想执行额外的验证,可以重写protected function validateEach(mixed $value) : void,该函数为每个值单独执行,无论是在实例化时还是在调用withValue时。请注意,此验证也会在调用withoutValuetryWithoutValuecontains之前运行,因此您会在传递完全无效的内容时收到通知,而不是静默地吞没。请确保也调用parent::validateEach($value);,除非您想在重写版本中重复默认验证行为。

从原始值

如果您想从“原始”值(而不是类的实例)实例化您的集合以方便起见(同时在内部将它们转换为相关的实例),可以使用fromRawValues

示例

$sources = Sources::fromRawArray([
    'invitation',
    'promotion',
    'reference',
]);

这适用于将实例转换为实现了fromStringfromIntfromBoolfromFloatfromDoublefromNumber或通过它们的构造函数接受相关参数的实例的转换。请注意,输入类型不会被转换。这意味着如果您传递一个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

当值表示除了stringinteger或特定类的实例(对于这些我们有IsStringArrayEnumTypeIsIntArrayEnumTypeIsClassArrayEnumType分别)之外的其他类型的值的数组,并且存在有效值的列表时,使用此类型。

与其他类型的组合

您可以与此类型与其他任何类型组合,例如,以获取浮点类型数组或整型枚举类型数组等。与使用IsStringEnumTypeIsArrayEnumType组合而不是IsStringArrayEnumType的区别在于,在前者的情况下,每个值都是一个值对象,而在后者中,每个值只是一个标量字符串。当然,您也可以简单地使用更新的IsClassArrayEnumType,它结合了IsArrayEnumTypeIsClassCollectionType,允许您持有值对象的实例。有关更多信息,请参阅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 的异常。
  • 如果您不希望抛出异常但希望重复值简单地被静默忽略(在 fromArraywithValue 中),则重写 protected static function ignoreDuplicateValues() : bool 并返回 true。如果发现重复值,它们只会添加一次到数组中。

areValuesUniqueignoreDuplicateValues 都返回 true 时,ignoreDuplicateValues 优先。 注意:为了执行这些重复检查,首先将值对象转换为字符串。如果您使用自定义类并希望进行这些检查,请确保您已经实现了 __toString 方法。(如果您在此库中使用任何类型,__toString 已经在它们上面实现了。)

验证

您可以通过覆盖protected function validateEach(mixed $value) : void来提供自定义验证,该函数对每个值单独执行,无论是实例化还是调用withValue时。请注意,此验证也会在withoutValuetryWithoutValuecontains之前运行,因此当传递完全无效的内容时,您会收到通知,而不是默默吞没。

示例

/**
 * @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 的异常。
  • 如果您不希望抛出异常但希望重复值简单地被静默忽略(在 fromArraywithValue 中),则重写 protected static function ignoreDuplicateValues() : bool 并返回 true。如果发现重复值,它们只会添加一次到数组中。

areValuesUniqueignoreDuplicateValues 都返回 true 时,ignoreDuplicateValues 优先。

验证

您可以通过覆盖protected function validateEach(mixed $value) : void来提供自定义验证,该函数对每个值单独执行,无论是实例化还是调用withValue时。请注意,此验证也会在withoutValuetryWithoutValuecontains之前运行,因此当传递完全无效的内容时,您会收到通知,而不是默默吞没。

示例

/**
 * @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',
]);

这适用于将实例转换为实现了fromStringfromIntfromBoolfromFloatfromDoublefromNumber或通过它们的构造函数接受相关参数的实例的转换。请注意,输入类型不会被转换。这意味着如果您传递一个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 的异常。
  • 如果您不希望抛出异常但希望重复值简单地被静默忽略(在 fromArraywithValue 中),则重写 protected static function ignoreDuplicateValues() : bool 并返回 true。如果发现重复值,它们只会添加一次到数组中。

areValuesUniqueignoreDuplicateValues 都返回 true 时,ignoreDuplicateValues 优先。

验证

您可以通过覆盖protected function validateEach(mixed $value) : void来提供自定义验证,该函数对每个值单独执行,无论是实例化还是调用withValue时。请注意,此验证也会在withoutValuetryWithoutValuecontains之前运行,因此当传递完全无效的内容时,您会收到通知,而不是默默吞没。

建议至少为值类型设置验证。

值转换

如果您想转换输入值但不失败验证,请覆盖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',
]);