Complex classes like ValueValidator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ValueValidator, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
8 | class ValueValidator |
||
9 | { |
||
10 | |||
11 | /** |
||
12 | * The error messages generated after validation or set manually |
||
13 | * |
||
14 | * @var array |
||
15 | */ |
||
16 | protected $messages = []; |
||
17 | |||
18 | /** |
||
19 | * Will be used to construct the rules |
||
20 | * |
||
21 | * @var \Sirius\Validation\RuleFactory |
||
22 | */ |
||
23 | protected $ruleFactory; |
||
24 | |||
25 | /** |
||
26 | * The prototype that will be used to generate the error message |
||
27 | * |
||
28 | * @var \Sirius\Validation\ErrorMessage |
||
29 | */ |
||
30 | protected $errorMessagePrototype; |
||
31 | |||
32 | /** |
||
33 | * The rule collections for the validation |
||
34 | * |
||
35 | * @var \Sirius\Validation\RuleCollection |
||
36 | */ |
||
37 | protected $rules; |
||
38 | |||
39 | /** |
||
40 | * The label of the value to be validated |
||
41 | * |
||
42 | * @var string |
||
43 | */ |
||
44 | protected $label; |
||
45 | |||
46 | |||
47 | 24 | public function __construct( |
|
48 | RuleFactory $ruleFactory = null, |
||
49 | ErrorMessage $errorMessagePrototype = null, |
||
50 | $label = null |
||
51 | ) { |
||
52 | 24 | if (!$ruleFactory) { |
|
53 | 7 | $ruleFactory = new RuleFactory(); |
|
54 | } |
||
55 | 24 | $this->ruleFactory = $ruleFactory; |
|
56 | 24 | if (!$errorMessagePrototype) { |
|
57 | 7 | $errorMessagePrototype = new ErrorMessage(); |
|
58 | } |
||
59 | 24 | $this->errorMessagePrototype = $errorMessagePrototype; |
|
60 | 24 | if ($label) { |
|
61 | 2 | $this->label = $label; |
|
62 | } |
||
63 | 24 | $this->rules = new RuleCollection; |
|
64 | 24 | } |
|
65 | |||
66 | 1 | public function setLabel($label = null) |
|
67 | { |
||
68 | 1 | $this->label = $label; |
|
69 | |||
70 | 1 | return $this; |
|
71 | } |
||
72 | |||
73 | /** |
||
74 | * Add 1 or more validation rules |
||
75 | * |
||
76 | * @example |
||
77 | * // add multiple rules at once |
||
78 | * $validator->add(array( |
||
79 | * 'required', |
||
80 | * ['required', ['email', null, '{label} must be an email', 'Field B']], |
||
81 | * )); |
||
82 | * |
||
83 | * // add multiple rules using a string |
||
84 | * $validator->add('required | email'); |
||
85 | * |
||
86 | * // add validator with options |
||
87 | * $validator->add('minlength', ['min' => 2], '{label} should have at least {min} characters', 'Field label'); |
||
88 | * |
||
89 | * // add validator with string and parameters as JSON string |
||
90 | * $validator->add('minlength({"min": 2})({label} should have at least {min} characters)(Field label)'); |
||
91 | * |
||
92 | * // add validator with string and parameters as query string |
||
93 | * $validator->add('minlength(min=2)({label} should have at least {min} characters)(Field label)'); |
||
94 | * |
||
95 | * @param string|callback $name |
||
96 | * @param string|array $options |
||
97 | * @param string $messageTemplate |
||
98 | * @param string $label |
||
99 | * |
||
100 | * @return ValueValidator |
||
101 | */ |
||
102 | 24 | public function add($name, $options = null, $messageTemplate = null, $label = null) |
|
103 | { |
||
104 | 24 | if (is_array($name) && !is_callable($name)) { |
|
105 | return $this->addMultiple($name); |
||
106 | } |
||
107 | 24 | if (is_string($name)) { |
|
108 | // rule was supplied like 'required | email' |
||
109 | 24 | if (strpos($name, ' | ') !== false) { |
|
110 | 4 | return $this->addMultiple(explode(' | ', $name)); |
|
111 | } |
||
112 | // rule was supplied like this 'length(2,10)(error message template)(label)' |
||
113 | 24 | if (strpos($name, '(') !== false) { |
|
114 | 7 | list($name, $options, $messageTemplate, $label) = $this->parseRule($name); |
|
115 | } |
||
116 | } |
||
117 | |||
118 | // check for the default label |
||
119 | 24 | if (!$label && $this->label) { |
|
|
|||
120 | 2 | $label = $this->label; |
|
121 | } |
||
122 | |||
123 | 24 | $validator = $this->ruleFactory->createRule($name, $options, $messageTemplate, $label); |
|
124 | |||
125 | 22 | return $this->addRule($validator); |
|
126 | } |
||
127 | |||
128 | /** |
||
129 | * @param array $rules |
||
130 | * |
||
131 | * @return ValueValidator |
||
132 | */ |
||
133 | 4 | public function addMultiple($rules) |
|
143 | |||
144 | /** |
||
145 | * @param AbstractValidator $validationRule |
||
146 | * |
||
147 | * @return ValueValidator |
||
148 | */ |
||
149 | 22 | public function addRule(AbstractRule $validationRule) |
|
150 | { |
||
151 | 22 | $validationRule->setErrorMessagePrototype($this->errorMessagePrototype); |
|
152 | 22 | $this->rules->attach($validationRule); |
|
153 | |||
154 | 22 | return $this; |
|
155 | } |
||
156 | |||
157 | /** |
||
158 | * Remove validation rule |
||
159 | * |
||
160 | * @param mixed $name |
||
161 | * rule name or true if all rules should be deleted for that selector |
||
162 | * @param mixed $options |
||
163 | * rule options, necessary for rules that depend on params for their ID |
||
164 | * |
||
165 | * @throws \InvalidArgumentException |
||
166 | * @internal param string $selector data selector |
||
167 | * @return self |
||
168 | */ |
||
169 | 4 | public function remove($name = true, $options = null) |
|
170 | { |
||
171 | 4 | if ($name === true) { |
|
172 | 2 | $this->rules = new RuleCollection(); |
|
173 | |||
174 | 2 | return $this; |
|
175 | } |
||
176 | 2 | $validator = $this->ruleFactory->createRule($name, $options); |
|
177 | 2 | $this->rules->detach($validator); |
|
178 | |||
179 | 2 | return $this; |
|
180 | } |
||
181 | |||
182 | /** |
||
183 | * Converts a rule that was supplied as string into a set of options that define the rule |
||
184 | * |
||
185 | * @example 'minLength({"min":2})({label} must have at least {min} characters)(Street)' |
||
186 | * |
||
187 | * will be converted into |
||
188 | * |
||
189 | * [ |
||
190 | * 'minLength', // validator name |
||
191 | * ['min' => 2'], // validator options |
||
192 | * '{label} must have at least {min} characters', |
||
193 | * 'Street' // label |
||
194 | * ] |
||
195 | * |
||
196 | * @param string $ruleAsString |
||
197 | * |
||
198 | * @return array |
||
199 | */ |
||
200 | 7 | protected function parseRule($ruleAsString) |
|
231 | |||
232 | |||
233 | 22 | public function validate($value, string $valueIdentifier = null, DataWrapper\WrapperInterface $context = null) |
|
234 | { |
||
235 | 22 | $this->messages = []; |
|
236 | 22 | $isRequired = false; |
|
270 | |||
271 | 21 | private function validateRule($rule, $value, $valueIdentifier, $context) |
|
280 | |||
281 | 19 | public function getMessages() |
|
285 | |||
286 | 20 | public function addMessage($message) |
|
292 | |||
293 | 2 | public function getRules() |
|
297 | |||
298 | 20 | protected function isEmpty($value) |
|
302 | } |
||
303 |
In PHP, under loose comparison (like
==
, or!=
, orswitch
conditions), values of different types might be equal.For
string
values, the empty string''
is a special case, in particular the following results might be unexpected: