Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 255 |
|
0.00% |
0 / 43 |
CRAP | |
0.00% |
0 / 1 |
Base | |
0.00% |
0 / 255 |
|
0.00% |
0 / 43 |
13340 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
12 | |||
entityFactory | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
factory | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
setRawData | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getRawData | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setStatus | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getStatus | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getReferenceMapping | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
setupPreFormatFields | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setupMapping | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
preSetup | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
postSetup | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
preSetupFields | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
postSetupFields | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setupFields | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
20 | |||
getReferenceFields | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
setupReferences | |
0.00% |
0 / 34 |
|
0.00% |
0 / 1 |
110 | |||
__set | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
30 | |||
addReference | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
getReference | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
__get | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
__isset | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
__unset | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
offsetExists | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
offsetGet | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
offsetSet | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
offsetUnset | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
count | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
jsonSerialize | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getFields | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
get | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
72 | |||
arrayAccessByDotPerpareKeys | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
30 | |||
save | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
saveEntitiy | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
42 | |||
postSave | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
saveReferences | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
20 | |||
delete | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
deleteEntity | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
0 | |||
deleteReferences | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
110 | |||
deleteWith | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
30 | |||
clearEntity | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
clearEntityReferences | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
42 | |||
getTableName | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | |
3 | namespace BO\Zmsdldb\Importer\MySQL\Entity; |
4 | |
5 | use BO\Zmsdldb\PDOAccess; |
6 | use BO\Zmsdldb\Importer\PDOTrait; |
7 | use BO\Zmsdldb\Importer\ItemNeedsUpdateTrait; |
8 | use BO\Zmsdldb\Importer\MySQL\Entity\Collection as EntityCollection |
9 | ; |
10 | |
11 | /** |
12 | * @SuppressWarnings(PHPMD.TooManyPublicMethods) |
13 | * @SuppressWarnings(PHPMD.TooManyMethods) |
14 | * @SuppressWarnings(PHPMD.NumberOfChildren) |
15 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) |
16 | * @SuppressWarnings(PHPMD.NPathComplexity) |
17 | * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) |
18 | */ |
19 | abstract class Base implements \Countable, \ArrayAccess, \JsonSerializable |
20 | { |
21 | use ItemNeedsUpdateTrait; |
22 | use PDOTrait; |
23 | |
24 | protected $fieldMapping = []; |
25 | |
26 | protected $fields = []; |
27 | |
28 | protected $referanceMapping = []; |
29 | |
30 | protected $preFormatFields = []; |
31 | |
32 | protected $references = []; |
33 | |
34 | protected $dataRaw = []; |
35 | |
36 | protected $setupFields = true; |
37 | protected $setupReferences = true; |
38 | |
39 | protected $status = 1; |
40 | |
41 | const STATUS_NEW = 1; |
42 | const STATUS_OLD = 0; |
43 | |
44 | public function __construct(PDOAccess $mySqlAccess, array $dataRaw = [], bool $setup = true) |
45 | { |
46 | try { |
47 | $this->pdoAccess = $mySqlAccess; |
48 | $this->dataRaw = $dataRaw; |
49 | |
50 | if (true === $setup) { |
51 | $this->setupMapping(); |
52 | |
53 | $this->preSetup(); |
54 | |
55 | $this->setupFields(); |
56 | |
57 | $this->setupReferences(); |
58 | |
59 | $this->postSetup(); |
60 | } |
61 | } catch (\Exception $e) { |
62 | throw $e; |
63 | } |
64 | } |
65 | |
66 | public static function entityFactory( |
67 | string $entityName, |
68 | PDOAccess $mySqlAccess, |
69 | array $dataRaw = [], |
70 | bool $setup = true |
71 | ) { |
72 | try { |
73 | $className = preg_replace_callback('/[_-]([a-z0-9]*)/i', function ($matches) { |
74 | return ucfirst($matches[1] ?? ''); |
75 | }, $entityName); |
76 | $className = '\\BO\\Zmsdldb\\Importer\\MySQL\\Entity' . $className; |
77 | |
78 | return new $className($mySqlAccess, $dataRaw, $setup); |
79 | } catch (\Exception $e) { |
80 | throw $e; |
81 | } |
82 | } |
83 | |
84 | public function factory(string $entityName, array $dataRaw = [], bool $setup = true) |
85 | { |
86 | try { |
87 | return static::entityFactory($entityName, $this->getPDOAccess(), $dataRaw, $setup); |
88 | } catch (\Exception $e) { |
89 | throw $e; |
90 | } |
91 | } |
92 | |
93 | public function setRawData(array $rawData = []) |
94 | { |
95 | $this->dataRaw = $rawData; |
96 | return $this; |
97 | } |
98 | |
99 | public function getRawData(): array |
100 | { |
101 | return $this->dataRaw; |
102 | } |
103 | |
104 | public function setStatus(int $status = Base::STATUS_NEW) |
105 | { |
106 | $this->status = $status; |
107 | } |
108 | |
109 | public function getStatus(): int |
110 | { |
111 | return $this->status; |
112 | } |
113 | |
114 | public function getReferenceMapping($setup = false): array |
115 | { |
116 | try { |
117 | if (true === $setup) { |
118 | $this->setupMapping(); |
119 | } |
120 | |
121 | return $this->referanceMapping; |
122 | } catch (\Exception $e) { |
123 | throw $e; |
124 | } |
125 | } |
126 | |
127 | protected function setupPreFormatFields() |
128 | { |
129 | } |
130 | |
131 | protected function setupMapping() |
132 | { |
133 | } |
134 | |
135 | public function preSetup() |
136 | { |
137 | } |
138 | |
139 | public function postSetup() |
140 | { |
141 | } |
142 | |
143 | public function preSetupFields() |
144 | { |
145 | } |
146 | |
147 | public function postSetupFields() |
148 | { |
149 | } |
150 | |
151 | final public function setupFields(): bool |
152 | { |
153 | try { |
154 | if (false === $this->setupFields) { |
155 | return true; |
156 | } |
157 | $this->preSetupFields(); |
158 | |
159 | $values = $this->get(array_keys(array_filter($this->fieldMapping))); |
160 | foreach ($values as $key => $value) { |
161 | $this->__set($key, $value); |
162 | } |
163 | $this->postSetupFields(); |
164 | $this->setupFields = false; |
165 | return true; |
166 | } catch (\Exception $e) { |
167 | throw $e; |
168 | } |
169 | } |
170 | |
171 | protected function getReferenceFields() |
172 | { |
173 | $referenceFields = array_flip(array_keys(array_filter($this->referanceMapping))); |
174 | |
175 | foreach (array_keys($referenceFields) as $name) { |
176 | $referenceFields[$name] = $this->get($name); |
177 | } |
178 | return $referenceFields; |
179 | } |
180 | |
181 | final public function setupReferences() |
182 | { |
183 | try { |
184 | if (false === $this->setupReferences) { |
185 | return true; |
186 | } |
187 | $values = $this->getReferenceFields(); |
188 | |
189 | foreach ($values as $name => $references) { |
190 | $referenceEntityClass = $this->referanceMapping[$name]['class']; |
191 | $addFields = []; |
192 | |
193 | foreach (($this->referanceMapping[$name]['neededFields'] ?? []) as $sourceKey => $destinationKey) { |
194 | $addFields[$destinationKey] = $this->get($sourceKey); |
195 | } |
196 | $isMultiple = $this->referanceMapping[$name]['multiple'] ?? true; |
197 | |
198 | if (false === $isMultiple) { |
199 | $references = [$references]; |
200 | } |
201 | |
202 | $position = 0; |
203 | foreach (($references ?? []) as $reference) { |
204 | foreach ($this->referanceMapping[$name]['addFields'] as $key => $value) { |
205 | if (is_callable($value)) { |
206 | $addFields[$key] = call_user_func_array($value, [$position, $name, $reference]); |
207 | } else { |
208 | $addFields[$key] = $value; |
209 | } |
210 | } |
211 | if (true === ($this->referanceMapping[$name]['selfAsArray'] ?? false)) { |
212 | $reference = [ |
213 | $name => $reference |
214 | ]; |
215 | } |
216 | $referencesInstance = new $referenceEntityClass( |
217 | $this->getPDOAccess(), |
218 | array_merge( |
219 | $reference, |
220 | $addFields |
221 | ) |
222 | ); |
223 | |
224 | $this->addReference($name, $referencesInstance); |
225 | $position++; |
226 | } |
227 | } |
228 | $this->setupReferences = false; |
229 | return true; |
230 | } catch (\Exception $e) { |
231 | throw $e; |
232 | } |
233 | } |
234 | |
235 | final public function __set($name, $value) |
236 | { |
237 | if (array_key_exists($name, $this->fieldMapping)) { |
238 | $name = $this->fieldMapping[$name]; |
239 | if (is_bool($value)) { |
240 | $value = (int)$value; |
241 | } elseif (stripos($name, '_json')) { |
242 | $value = json_encode($value); |
243 | } |
244 | $this->fields[$name] = $value; |
245 | } elseif (array_key_exists($name, $this->referanceMapping)) { |
246 | $this->addReference($name, $value); |
247 | } |
248 | } |
249 | |
250 | public function addReference(string $name, Base $reference) |
251 | { |
252 | if (array_key_exists($name, $this->referanceMapping)) { |
253 | if (!isset($this->references[$name])) { |
254 | $this->references[$name] = new EntityCollection(); |
255 | } |
256 | $this->references[$name][] = $reference; |
257 | } |
258 | } |
259 | |
260 | public function getReference(string $name) |
261 | { |
262 | if (array_key_exists($name, $this->references)) { |
263 | return $this->references[$name]; |
264 | } |
265 | throw new \InvalidArgumentException(__METHOD__ . " reference {$name} has not been set!"); |
266 | } |
267 | |
268 | final public function __get($name) |
269 | { |
270 | if (array_key_exists($name, $this->fields)) { |
271 | return $this->fields[$name]; |
272 | } |
273 | if (array_key_exists($name, $this->references)) { |
274 | return $this->references[$name]; |
275 | } |
276 | throw new \InvalidArgumentException(__METHOD__ . " {$name} has not been set!"); |
277 | } |
278 | |
279 | final public function __isset($name): bool |
280 | { |
281 | return array_key_exists($name, $this->fields) || array_key_exists($name, $this->references); |
282 | } |
283 | |
284 | final public function __unset($name) |
285 | { |
286 | if (array_key_exists($name, $this->fields)) { |
287 | unset($this->fields[$name]); |
288 | } |
289 | if (array_key_exists($name, $this->references)) { |
290 | unset($this->references[$name]); |
291 | } |
292 | } |
293 | |
294 | final public function offsetExists($offset): bool |
295 | { |
296 | return $this->__isset($offset); |
297 | } |
298 | |
299 | final public function offsetGet($offset) |
300 | { |
301 | return $this->__get($offset); |
302 | } |
303 | |
304 | final public function offsetSet($offset, $value): Base |
305 | { |
306 | $this->__set($offset, $value); |
307 | return $this; |
308 | } |
309 | |
310 | final public function offsetUnset($offset): Base |
311 | { |
312 | $this->__unset($offset); |
313 | return $this; |
314 | } |
315 | |
316 | final public function count(): int |
317 | { |
318 | return count($this->fields); |
319 | } |
320 | |
321 | public function jsonSerialize(): mixed |
322 | { |
323 | return $this->fields; |
324 | } |
325 | |
326 | public function getFields(): array |
327 | { |
328 | return $this->fields; |
329 | } |
330 | |
331 | public function get($key = null, $default = null) |
332 | { |
333 | if (null === $key) { |
334 | return $this->dataRaw; |
335 | } |
336 | $keys = $key; |
337 | if (!is_array($keys)) { |
338 | $keys = [$keys]; |
339 | } |
340 | $values = []; |
341 | |
342 | foreach ($keys as $key) { |
343 | if ('__RAW__' == $key) { |
344 | $values[$key] = $this->dataRaw; |
345 | continue; |
346 | } |
347 | $levels = static::arrayAccessByDotPerpareKeys($key); |
348 | |
349 | $value = $default; |
350 | |
351 | $pointer = &$this->dataRaw; |
352 | $levelsCount = count($levels); |
353 | for ($i = 0; $i < $levelsCount; ++$i) { |
354 | if (array_key_exists($levels[$i], $pointer)) { |
355 | $pointer = &$pointer[$levels[$i]]; |
356 | $value = $pointer; |
357 | |
358 | continue; |
359 | } |
360 | } |
361 | $values[$key] = ($value); |
362 | } |
363 | return 1 == count($keys) ? $values[$keys[0]] : $values; |
364 | } |
365 | |
366 | protected static function arrayAccessByDotPerpareKeys(string $key = null): array |
367 | { |
368 | if (null === $key) { |
369 | return []; |
370 | } |
371 | $keys = explode('.', $key); |
372 | if (false === $keys) { |
373 | throw new \Exception('Invalid key, key must be a string!'); |
374 | } |
375 | $keys = array_filter($keys, 'strlen'); |
376 | $keys = array_map(function ($key) { |
377 | return ((is_numeric($key) && !is_double(1 * $key)) ? (int)$key : $key); |
378 | }, $keys); |
379 | |
380 | return $keys; |
381 | } |
382 | |
383 | public function save() |
384 | { |
385 | try { |
386 | if (static::STATUS_NEW !== $this->getStatus()) { |
387 | return false; |
388 | } |
389 | $this->saveEntitiy(); |
390 | $this->saveReferences(); |
391 | return true; |
392 | } catch (\Exception $e) { |
393 | throw $e; |
394 | } |
395 | } |
396 | |
397 | final public function saveEntitiy(): bool |
398 | { |
399 | try { |
400 | if (static::STATUS_NEW !== $this->getStatus()) { |
401 | return false; |
402 | } |
403 | if (!empty($this->fields)) { |
404 | $sql = 'REPLACE INTO ' . static::getTableName() . ' '; |
405 | $sql .= '(`' . implode('`, `', array_keys($this->fields)) . '`) '; |
406 | |
407 | $questionMarks = array_fill(0, count($this->fields), '?'); |
408 | $sql .= 'VALUES (' . implode(', ', $questionMarks) . ') '; |
409 | |
410 | #print_r($sql . \PHP_EOL) ; |
411 | $stm = $this->getPDOAccess()->prepare($sql); |
412 | |
413 | $stm->execute(array_values($this->fields)); |
414 | |
415 | #$this->postSave($stm, $this); |
416 | |
417 | if ($stm && 0 < $stm->rowCount()) { |
418 | return true; |
419 | } |
420 | throw new \Exception('Could not save entity'); |
421 | } |
422 | throw new \Exception('Could not save entity, fields are empty'); |
423 | return false; |
424 | } catch (\Exception $e) { |
425 | throw $e; |
426 | } |
427 | return false; |
428 | } |
429 | |
430 | /** |
431 | * @SuppressWarnings(PHPMD.UnusedLocalVariable) |
432 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) |
433 | */ |
434 | public function postSave(\PDOStatement $stm, Base $entity) |
435 | { |
436 | } |
437 | |
438 | final public function saveReferences(): bool |
439 | { |
440 | try { |
441 | if (static::STATUS_NEW !== $this->getStatus()) { |
442 | return false; |
443 | } |
444 | if (!empty($this->references)) { |
445 | array_map(function ($referencesCollection) { |
446 | $referencesCollection->saveEntities(); |
447 | }, $this->references); |
448 | } |
449 | return true; |
450 | } catch (\Exception $e) { |
451 | throw $e; |
452 | } |
453 | } |
454 | |
455 | public function delete(): bool |
456 | { |
457 | try { |
458 | $this->deleteEntity(); |
459 | $this->deleteReferences(); |
460 | return true; |
461 | } catch (\Exception $e) { |
462 | throw $e; |
463 | } |
464 | } |
465 | |
466 | abstract public function deleteEntity(): bool; |
467 | |
468 | public function deleteReferences(): bool |
469 | { |
470 | #return true; |
471 | try { |
472 | foreach ($this->referanceMapping as $name => $mappingData) { |
473 | if (isset($mappingData['deleteFunction']) && is_callable($mappingData['deleteFunction'])) { |
474 | call_user_func_array($mappingData['deleteFunction'], [$this, $name, $mappingData]); |
475 | continue; |
476 | } |
477 | if (array_key_exists('delete', $mappingData) && false === $mappingData['delete']) { |
478 | continue; |
479 | } |
480 | if (!array_key_exists('deleteFields', $mappingData) || empty($mappingData['deleteFields'])) { |
481 | throw new \Exception(static::class . ' missing $deleteFields in reference mapping'); |
482 | } |
483 | $addFields = []; |
484 | $referenceEntityClass = $mappingData['class']; |
485 | foreach ($mappingData['deleteFields'] as $sourceKey => $val) { |
486 | $addFields[$sourceKey] = $val; |
487 | } |
488 | |
489 | $referencesInstance = new $referenceEntityClass( |
490 | $this->getPDOAccess(), |
491 | $addFields, |
492 | false |
493 | ); |
494 | $referencesInstance->setupFields(); |
495 | |
496 | #print_r(array_intersect_key($referencesInstance->getFields(), $addFields)); |
497 | #exit; |
498 | |
499 | $referencesInstance->deleteWith( |
500 | array_intersect_key($referencesInstance->getFields(), $addFields) |
501 | ); |
502 | } |
503 | return true; |
504 | } catch (\Exception $e) { |
505 | throw $e; |
506 | } |
507 | } |
508 | |
509 | final public function deleteWith(array $fields): bool |
510 | { |
511 | try { |
512 | $sql = "DELETE FROM " . static::getTableName(); |
513 | if (!empty($fields)) { |
514 | $where = array_map(function ($field) { |
515 | return $field . ' = ?'; |
516 | }, array_keys($fields)); |
517 | $sql .= " WHERE " . implode(' AND ', $where); |
518 | } |
519 | |
520 | $stm = $this->getPDOAccess()->prepare($sql); |
521 | $stm->execute(array_values($fields)); |
522 | if ($stm && 0 < $stm->rowCount()) { |
523 | return true; |
524 | } |
525 | return false; |
526 | } catch (\Exception $e) { |
527 | throw $e; |
528 | } |
529 | } |
530 | |
531 | public function clearEntity(array $addWhere = []): bool |
532 | { |
533 | try { |
534 | return $this->deleteWith($addWhere); |
535 | } catch (\Exception $e) { |
536 | throw $e; |
537 | } |
538 | } |
539 | |
540 | public function clearEntityReferences(): bool |
541 | { |
542 | try { |
543 | foreach ($this->getReferenceMapping(true) as $name => $mappingData) { |
544 | $referenceEntityClass = $mappingData['class']; |
545 | $clearFields = []; |
546 | $position = 0; |
547 | foreach (($mappingData['clearFields'] ?? []) as $key => $value) { |
548 | if (is_callable($value)) { |
549 | $clearFields[$key] = call_user_func_array($value, [$position++, $name, null]); |
550 | } else { |
551 | $clearFields[$key] = $value; |
552 | } |
553 | } |
554 | |
555 | $referencesInstance = new $referenceEntityClass( |
556 | $this->getPDOAccess(), |
557 | [], |
558 | false |
559 | ); |
560 | if (!empty($clearFields)) { |
561 | $referencesInstance->deleteWith($clearFields); |
562 | } else { |
563 | $referencesInstance->clearEntity(); |
564 | } |
565 | } |
566 | return true; |
567 | } catch (\Exception $e) { |
568 | throw $e; |
569 | } |
570 | } |
571 | |
572 | public static function getTableName(): string |
573 | { |
574 | if (defined('static::TABLENAME')) { |
575 | return strtolower(static::TABLENAME); |
576 | } |
577 | $classNameWithNs = explode("\\", static::class); |
578 | $className = end($classNameWithNs); |
579 | |
580 | return strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $className)); |
581 | } |
582 | } |