1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the CRUDlex package. |
5
|
|
|
* |
6
|
|
|
* (c) Philip Lehmann-Böhm <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace CRUDlex; |
13
|
|
|
|
14
|
|
|
use CRUDlex\CRUDEntityDefinition; |
15
|
|
|
use CRUDlex\CRUDData; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Represents a single set of data in field value pairs like the row in a |
19
|
|
|
* database. Depends of course on the {@see CRUDData} implementation being used. |
20
|
|
|
* With this objects, the data is passed arround and validated. |
21
|
|
|
*/ |
22
|
|
|
class CRUDEntity { |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* The {@see CRUDEntityDefinition} defining how this entity looks like. |
26
|
|
|
*/ |
27
|
|
|
protected $definition; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* Holds the key value data of the entity. |
31
|
|
|
*/ |
32
|
|
|
protected $entity = array(); |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Validates the given field for the required constraint. |
36
|
|
|
* |
37
|
|
|
* @param string $field the field to validate |
38
|
|
|
* @param array &$errors the error collecting array |
39
|
|
|
* @param boolean &$valid the validation flag |
40
|
|
|
*/ |
41
|
|
|
private function validateRequired($field, &$errors, &$valid) { |
42
|
|
|
if ($this->definition->isRequired($field) && !$this->definition->getFixedValue($field) && |
43
|
|
|
(!array_key_exists($field, $this->entity) |
44
|
|
|
|| $this->entity[$field] === null |
45
|
|
|
|| $this->entity[$field] === '')) { |
46
|
|
|
$errors[$field]['required'] = true; |
47
|
|
|
$valid = false; |
48
|
|
|
} |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Validates the given field for the unique constraint. |
53
|
|
|
* |
54
|
|
|
* @param string $field the field to validate |
55
|
|
|
* @param CRUDData $data the data instance to work with |
56
|
|
|
* @param array &$errors the error collecting array |
57
|
|
|
* @param boolean &$valid the validation flag |
58
|
|
|
*/ |
59
|
|
|
private function validateUnique($field, CRUDData $data, &$errors, &$valid) { |
60
|
|
|
if ($this->definition->isUnique($field) && array_key_exists($field, $this->entity) && $this->entity[$field]) { |
61
|
|
|
$params = array($field => $this->entity[$field]); |
62
|
|
|
$paramsOperators = array($field => '='); |
63
|
|
|
if ($this->entity['id'] !== null) { |
64
|
|
|
$params['id'] = $this->entity['id']; |
65
|
|
|
$paramsOperators['id'] = '!='; |
66
|
|
|
} |
67
|
|
|
$amount = intval($data->countBy($this->definition->getTable(), $params, $paramsOperators, true)); |
68
|
|
|
if ($amount > 0) { |
69
|
|
|
$errors[$field]['unique'] = true; |
70
|
|
|
$valid = false; |
71
|
|
|
} |
72
|
|
|
} |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Validates the given field for the set type. |
77
|
|
|
* |
78
|
|
|
* @param string $field the field to validate |
79
|
|
|
* @param array &$errors the error collecting array |
80
|
|
|
* @param boolean &$valid the validation flag |
81
|
|
|
*/ |
82
|
|
|
private function validateSet($field, &$errors, &$valid) { |
83
|
|
|
$type = $this->definition->getType($field); |
84
|
|
|
if ($type == 'set' && $this->entity[$field]) { |
85
|
|
|
$setItems = $this->definition->getSetItems($field); |
86
|
|
|
if (!in_array($this->entity[$field], $setItems)) { |
87
|
|
|
$errors[$field]['input'] = true; |
88
|
|
|
$valid = false; |
89
|
|
|
} |
90
|
|
|
} |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* Validates the given field for the int type. |
95
|
|
|
* |
96
|
|
|
* @param string $field the field to validate |
97
|
|
|
* @param array &$errors the error collecting array |
98
|
|
|
* @param boolean &$valid the validation flag |
99
|
|
|
*/ |
100
|
|
View Code Duplication |
private function validateInt($field, &$errors, &$valid) { |
|
|
|
|
101
|
|
|
$type = $this->definition->getType($field); |
102
|
|
|
if ($type == 'int' && $this->entity[$field] !== '' && $this->entity[$field] !== null && (string)(int)$this->entity[$field] != $this->entity[$field]) { |
103
|
|
|
$errors[$field]['input'] = true; |
104
|
|
|
$valid = false; |
105
|
|
|
} |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Validates the given field for the float type. |
110
|
|
|
* |
111
|
|
|
* @param string $field the field to validate |
112
|
|
|
* @param array &$errors the error collecting array |
113
|
|
|
* @param boolean &$valid the validation flag |
114
|
|
|
*/ |
115
|
|
View Code Duplication |
private function validateFloat($field, &$errors, &$valid) { |
|
|
|
|
116
|
|
|
$type = $this->definition->getType($field); |
117
|
|
|
if ($type == 'float' && $this->entity[$field] !== '' && $this->entity[$field] !== null && (string)(float)$this->entity[$field] != $this->entity[$field]) { |
118
|
|
|
$errors[$field]['input'] = true; |
119
|
|
|
$valid = false; |
120
|
|
|
} |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* Validates the given field for the date type. |
125
|
|
|
* |
126
|
|
|
* @param string $field the field to validate |
127
|
|
|
* @param array &$errors the error collecting array |
128
|
|
|
* @param boolean &$valid the validation flag |
129
|
|
|
*/ |
130
|
|
|
private function validateDate($field, &$errors, &$valid) { |
131
|
|
|
$type = $this->definition->getType($field); |
132
|
|
|
if ($type == 'date' && $this->entity[$field] && \DateTime::createFromFormat('Y-m-d', $this->entity[$field]) === false) { |
133
|
|
|
$errors[$field]['input'] = true; |
134
|
|
|
$valid = false; |
135
|
|
|
} |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* Validates the given field for the datetime type. |
140
|
|
|
* |
141
|
|
|
* @param string $field the field to validate |
142
|
|
|
* @param array &$errors the error collecting array |
143
|
|
|
* @param boolean &$valid the validation flag |
144
|
|
|
*/ |
145
|
|
|
private function validateDateTime($field, &$errors, &$valid) { |
146
|
|
|
$type = $this->definition->getType($field); |
147
|
|
|
if ($type == 'datetime' && $this->entity[$field] && |
148
|
|
|
\DateTime::createFromFormat('Y-m-d H:i', $this->entity[$field]) === false && |
149
|
|
|
\DateTime::createFromFormat('Y-m-d H:i:s', $this->entity[$field]) === false) { |
150
|
|
|
$errors[$field]['input'] = true; |
151
|
|
|
$valid = false; |
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Validates the given field for the reference type. |
157
|
|
|
* |
158
|
|
|
* @param string $field the field to validate |
159
|
|
|
* @param CRUDData $data the data instance to work with |
160
|
|
|
* @param array &$errors the error collecting array |
161
|
|
|
* @param boolean &$valid the validation flag |
162
|
|
|
*/ |
163
|
|
|
private function validateReference($field, CRUDData $data, &$errors, &$valid) { |
164
|
|
|
$type = $this->definition->getType($field); |
165
|
|
|
if ($type == 'reference' && $this->entity[$field] !== '' && $this->entity[$field] !== null) { |
166
|
|
|
$params = array('id' => $this->entity[$field]); |
167
|
|
|
$paramsOperators = array('id' => '='); |
168
|
|
|
$amount = $data->countBy($this->definition->getReferenceTable($field), $params, $paramsOperators, false); |
169
|
|
|
if ($amount == 0) { |
170
|
|
|
$errors[$field]['input'] = true; |
171
|
|
|
$valid = false; |
172
|
|
|
} |
173
|
|
|
} |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* Constructor. |
178
|
|
|
* |
179
|
|
|
* @param CRUDEntityDefinition $definition |
180
|
|
|
* the definition how this entity looks |
181
|
|
|
*/ |
182
|
|
|
public function __construct(CRUDEntityDefinition $definition) { |
183
|
|
|
$this->definition = $definition; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* Sets a field value pair of this entity. |
188
|
|
|
* |
189
|
|
|
* @param string $field |
190
|
|
|
* the field |
191
|
|
|
* @param mixed $value |
192
|
|
|
* the value |
193
|
|
|
*/ |
194
|
|
|
public function set($field, $value) { |
195
|
|
|
$this->entity[$field] = $value; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* Gets the value of a field. |
200
|
|
|
* |
201
|
|
|
* @param string $field |
202
|
|
|
* the field |
203
|
|
|
* |
204
|
|
|
* @return mixed |
205
|
|
|
* null on invalid field, an int if the definition says that the |
206
|
|
|
* type of the field is an int, a boolean if the field is a bool or |
207
|
|
|
* else the raw value |
208
|
|
|
*/ |
209
|
|
|
public function get($field) { |
210
|
|
|
|
211
|
|
|
if ($this->definition->getFixedValue($field) !== null) { |
212
|
|
|
return $this->definition->getFixedValue($field); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
if (!array_key_exists($field, $this->entity)) { |
216
|
|
|
return null; |
217
|
|
|
} |
218
|
|
|
$value = $this->entity[$field]; |
219
|
|
|
|
220
|
|
|
switch ($this->definition->getType($field)) { |
221
|
|
|
case 'int': |
222
|
|
|
$value = $value !== '' && $value !== null ? intval($value) : null; |
223
|
|
|
break; |
224
|
|
|
case 'float': |
225
|
|
|
$value = $value !== '' && $value !== null ? floatval($value) : null; |
226
|
|
|
break; |
227
|
|
|
case 'bool': |
228
|
|
|
$value = $value && $value !== '0'; |
229
|
|
|
break; |
230
|
|
|
} |
231
|
|
|
return $value; |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
/** |
235
|
|
|
* Gets the entity definition. |
236
|
|
|
* |
237
|
|
|
* @return CRUDEntityDefinition |
238
|
|
|
* the definition |
239
|
|
|
*/ |
240
|
|
|
public function getDefinition() { |
241
|
|
|
return $this->definition; |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
/** |
245
|
|
|
* Validates the entity against the definition. |
246
|
|
|
* |
247
|
|
|
* @param CRUDData $data |
248
|
|
|
* the data access instance used for counting things |
249
|
|
|
* |
250
|
|
|
* @return array |
251
|
|
|
* an array with the fields "valid" and "errors"; valid provides a quick |
252
|
|
|
* check whether the given entity passes the validation and errors is an |
253
|
|
|
* array with all fields as keys and arrays as values; this field arrays |
254
|
|
|
* contain three keys: required, unique and input; each of them represents |
255
|
|
|
* with a boolean whether the input is ok in that way; if "required" is |
256
|
|
|
* true, the field wasn't set, unique means the uniqueness of the field in |
257
|
|
|
* the datasource and input is used to indicate whether the form of the |
258
|
|
|
* value is correct (a valid int, date, depending on the type in the |
259
|
|
|
* definition) |
260
|
|
|
*/ |
261
|
|
|
public function validate(CRUDData $data) { |
262
|
|
|
|
263
|
|
|
$fields = $this->definition->getEditableFieldNames(); |
264
|
|
|
$errors = array(); |
265
|
|
|
$valid = true; |
266
|
|
|
foreach ($fields as $field) { |
267
|
|
|
$errors[$field] = array('required' => false, 'unique' => false, 'input' => false); |
268
|
|
|
|
269
|
|
|
$this->validateRequired($field, $errors, $valid); |
270
|
|
|
$this->validateUnique($field, $data, $errors, $valid); |
271
|
|
|
|
272
|
|
|
$this->validateSet($field, $errors, $valid); |
273
|
|
|
$this->validateInt($field, $errors, $valid); |
274
|
|
|
$this->validateFloat($field, $errors, $valid); |
275
|
|
|
$this->validateDate($field, $errors, $valid); |
276
|
|
|
$this->validateDateTime($field, $errors, $valid); |
277
|
|
|
$this->validateReference($field, $data, $errors, $valid); |
278
|
|
|
|
279
|
|
|
} |
280
|
|
|
return array('valid' => $valid, 'errors' => $errors); |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
} |
284
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.