1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace Yiisoft\Validator\Rule; |
||
6 | |||
7 | use Attribute; |
||
8 | use Closure; |
||
9 | use InvalidArgumentException; |
||
10 | use Yiisoft\Validator\Rule\Trait\SkipOnEmptyTrait; |
||
11 | use Yiisoft\Validator\Rule\Trait\SkipOnErrorTrait; |
||
12 | use Yiisoft\Validator\Rule\Trait\WhenTrait; |
||
13 | use Yiisoft\Validator\DumpedRuleInterface; |
||
14 | use Yiisoft\Validator\SkipOnEmptyInterface; |
||
15 | use Yiisoft\Validator\SkipOnErrorInterface; |
||
16 | use Yiisoft\Validator\WhenInterface; |
||
17 | |||
18 | use function count; |
||
0 ignored issues
–
show
|
|||
19 | |||
20 | /** |
||
21 | * Defines validation options to check that a minimum number of specified attributes are filled. |
||
22 | * |
||
23 | * Both arrays and objects with public properties are supported as validated values. |
||
24 | * |
||
25 | * @see AtLeastHandler |
||
26 | * |
||
27 | * @psalm-import-type WhenType from WhenInterface |
||
28 | 3 | */ |
|
29 | #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)] |
||
30 | final class AtLeast implements DumpedRuleInterface, SkipOnErrorInterface, WhenInterface, SkipOnEmptyInterface |
||
31 | { |
||
32 | use SkipOnEmptyTrait; |
||
33 | use SkipOnErrorTrait; |
||
34 | use WhenTrait; |
||
35 | |||
36 | /** |
||
37 | * @param string[] $attributes The list of required attributes that will be checked. |
||
38 | * @param int $min The minimum required quantity of filled attributes to pass the validation. Defaults to 1. |
||
39 | * @param string $incorrectInputMessage A message used when the input is incorrect. |
||
40 | * |
||
41 | * You may use the following placeholders in the message: |
||
42 | * |
||
43 | * - `{attribute}`: the translated label of the attribute being validated. |
||
44 | * - `{type}`: the type of the value being validated. |
||
45 | * @param string $message A message used when the value is not valid. |
||
46 | * |
||
47 | * You may use the following placeholders in the message: |
||
48 | * |
||
49 | * - `{attribute}`: the translated label of the attribute being validated. |
||
50 | * - `{min}`: the minimum number of attribute values that was not met. |
||
51 | * @param bool|callable|null $skipOnEmpty Whether to skip this rule if the value validated is empty. |
||
52 | * See {@see SkipOnEmptyInterface}. |
||
53 | * @param bool $skipOnError Whether to skip this rule if any of the previous rules gave an error. |
||
54 | 1 | * See {@see SkipOnErrorInterface}. |
|
55 | * @param Closure|null $when A callable to define a condition for applying the rule. |
||
56 | 1 | * See {@see WhenInterface}. |
|
57 | * |
||
58 | * @psalm-param WhenType $when |
||
59 | */ |
||
60 | public function __construct( |
||
61 | private array $attributes, |
||
62 | 15 | private int $min = 1, |
|
63 | private string $incorrectInputMessage = 'The value must be an array or an object.', |
||
64 | 15 | private string $message = 'At least {min, number} {min, plural, one{attribute} other{attributes}} from this ' . |
|
65 | 'list must be filled: {attributes}.', |
||
66 | private mixed $skipOnEmpty = null, |
||
67 | 15 | private bool $skipOnError = false, |
|
68 | private Closure|null $when = null |
||
69 | 15 | ) { |
|
70 | if ($min > count($this->attributes)) { |
||
71 | throw new InvalidArgumentException('$min must be no greater than amount of $attributes.'); |
||
72 | 4 | } |
|
73 | } |
||
74 | 4 | ||
75 | public function getName(): string |
||
76 | { |
||
77 | 7 | return self::class; |
|
78 | } |
||
79 | 7 | ||
80 | /** |
||
81 | * Get the list of required attributes that will be checked. |
||
82 | 2 | * |
|
83 | * @return string[] The list of attributes. |
||
84 | * |
||
85 | 2 | * @see $attributes |
|
86 | 2 | */ |
|
87 | public function getAttributes(): array |
||
88 | 2 | { |
|
89 | return $this->attributes; |
||
90 | } |
||
91 | |||
92 | 2 | /** |
|
93 | 2 | * Get the minimum required quantity of filled attributes to pass the validation. |
|
94 | * |
||
95 | 2 | * @return int Minimum require quantity. |
|
96 | 2 | * |
|
97 | * @see $min |
||
98 | */ |
||
99 | public function getMin(): int |
||
100 | 19 | { |
|
101 | return $this->min; |
||
102 | 19 | } |
|
103 | |||
104 | /** |
||
105 | * Get the message used when the input is incorrect. |
||
106 | * |
||
107 | * @return string Error message. |
||
108 | * |
||
109 | * @see $incorrectInputMessage |
||
110 | */ |
||
111 | public function getIncorrectInputMessage(): string |
||
112 | { |
||
113 | return $this->incorrectInputMessage; |
||
114 | } |
||
115 | |||
116 | /** |
||
117 | * Get the message used when the value is not valid. |
||
118 | * |
||
119 | * @return string Error message. |
||
120 | * |
||
121 | * @see $message |
||
122 | */ |
||
123 | public function getMessage(): string |
||
124 | { |
||
125 | return $this->message; |
||
126 | } |
||
127 | |||
128 | public function getOptions(): array |
||
129 | { |
||
130 | return [ |
||
131 | 'attributes' => $this->attributes, |
||
132 | 'min' => $this->min, |
||
133 | 'incorrectInputMessage' => [ |
||
134 | 'template' => $this->incorrectInputMessage, |
||
135 | 'parameters' => [], |
||
136 | ], |
||
137 | 'message' => [ |
||
138 | 'template' => $this->message, |
||
139 | 'parameters' => ['min' => $this->min], |
||
140 | ], |
||
141 | 'skipOnEmpty' => $this->getSkipOnEmptyOption(), |
||
142 | 'skipOnError' => $this->skipOnError, |
||
143 | ]; |
||
144 | } |
||
145 | |||
146 | public function getHandler(): string |
||
147 | { |
||
148 | return AtLeastHandler::class; |
||
149 | } |
||
150 | } |
||
151 |
Let?s assume that you have a directory layout like this:
and let?s assume the following content of
Bar.php
:If both files
OtherDir/Foo.php
andSomeDir/Foo.php
are loaded in the same runtime, you will see a PHP error such as the following:PHP Fatal error: Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php
However, as
OtherDir/Foo.php
does not necessarily have to be loaded and the error is only triggered if it is loaded beforeOtherDir/Bar.php
, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias: