Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
model.php
1<?php
3
5use Bitrix\Main\Entity\Query\Filter\ConditionTree;
12
17abstract class Model implements IErrorable
18{
20 protected $id;
21 protected $createdAt;
22 protected $updatedAt;
23 protected $errors;
24 private $deletedEntities = array();
25 private $currentDbState = array();
26 //private $lazyAttributes;
27
31 public function __construct()
32 {
33 $this->createdAt = new DateTime();
34 $this->updatedAt = new DateTime();
35 }
36
42 public static function getTableClassName()
43 {
45 }
46
47
53 public static function getMapAttributes()
54 {
55 return array(
56 'ID' => 'id',
57 'CREATED_DATE' => 'createdAt',
58 'UPDATED_DATE' => 'updatedAt'
59 );
60 }
61
92 public static function getMapReferenceAttributes()
93 {
94 return array();
95 }
96
97
101 public function save()
102 {
103
104 $referenceMapAttributes = static::getMapReferenceAttributes();
105
106 foreach ($referenceMapAttributes as $referenceAttributeName => $assoc)
107 {
108 if (!empty($this->{$referenceAttributeName}))
109 {
110 switch ($assoc['type'])
111 {
112 case Common::MANY_TO_ONE:
113 $this->saveManyToOneReference($this->{$referenceAttributeName}, $assoc);
114 break;
115 case Common::ONE_TO_ONE:
116 //TODO:
117 break;
118 }
119 }
120 }
121
122 $ormFields = $this->getConvertedMapAttributesToOrmFields();
123 if (!$ormFields['ID'])
124 {
125 $addResult = $this->add($ormFields);
126 $ownerId = $addResult->getId();
127 $this->setId($ownerId);
128 }
129 else
130 {
131 $ownerId = $ormFields['ID'];
132 $changedAttributes = $this->getChangedOrmAttributes($ormFields);
133 if ($changedAttributes)
134 {
135 $this->update(array('ID' => $ownerId), $changedAttributes);
136 }
137 if (!empty($this->deletedEntities))
138 {
139 $this->deleteReference($this->deletedEntities);
140 }
141 }
142
143
144
145
146 foreach ($referenceMapAttributes as $referenceAttributeName => $assoc)
147 {
148 if (!empty($this->{$referenceAttributeName}))
149 {
150 switch ($assoc['type'])
151 {
152 case Common::ONE_TO_MANY:
153 $this->saveOneToManyReferences($this->{$referenceAttributeName}, $assoc, $ownerId);
154 break;
155 case Common::MANY_TO_MANY:
156 $this->saveManyToManyReferences($this->{$referenceAttributeName}, $assoc, $ownerId);
157 break;
158 }
159 }
160 }
161
162 return $ownerId;
163 }
164
170 private function saveOneToManyReferences($references, $assoc, $ownerEntityId)
171 {
172 foreach ($references as $key => $reference)
173 {
174 if ($reference instanceof $assoc['targetEntity'])
175 {
176 $mapReferenceAttributes = $reference::getMapReferenceAttributes();
177 $reference->{$mapReferenceAttributes[$assoc['mappedBy']]['join']['field'][0]} = $ownerEntityId;
178 $reference->save();
179 }
180
181 }
182 }
183
189 private function saveManyToManyReferences($references, $assoc, $ownerEntityId)
190 {
191 foreach ($references as $key => $reference)
192 {
193 if ($reference instanceof $assoc['targetEntity'])
194 {
195 $isReferenceNew = !(boolean)$reference->getId();
196 $referenceId = $reference->save();
197 if ($isReferenceNew)
198 {
199 $column = array_values($assoc['join']['column']);
200 $column = $column[0];
201 $inverseColumn = array_values($assoc['join']['inverseColumn']);
202 $inverseColumn = $inverseColumn[0];
203 $connectData = array(
204 $column[1] => $ownerEntityId,
205 $inverseColumn[1] => $referenceId,
206 );
208 $ormTableClassName = $assoc['join']['tableClassName'];
209 $ormTableClassName::add($connectData);
210 }
211 }
212 }
213 }
214
219 private function saveManyToOneReference($reference, $assoc)
220 {
221 if ($reference instanceof $assoc['targetEntity'])
222 {
223 $reference = clone $reference;
224 $reference->{$assoc['inversedBy']} = null;
225 $reference->save();
226 $this->{$assoc['join']['field'][0]} = $reference->getId();
227 }
228 }
229
230
231
235 private function getConvertedMapAttributesToOrmFields()
236 {
237 $result = array();
238 $fieldsMap = static::getMapAttributes();
239 foreach ($fieldsMap as $ormFieldName => $objectProperty)
240 {
241 $result[$ormFieldName] = $this->{$objectProperty};
242 }
243 return $result;
244 }
245
246
251 private function add(array $data)
252 {
253 $tableClassName = static::getTableClassName();
254 $resultData = $tableClassName::add($data);
255 $this->currentDbState = $resultData->getData();
256 return $resultData;
257 }
258
263 private function getChangedOrmAttributes($newEntityAttributes)
264 {
265
270 $oldEntityAttributes = $this->getCurrentDbState();
271 unset($oldEntityAttributes['CREATED_DATE']);
272 unset($oldEntityAttributes['UPDATED_DATE']);
273 unset($newEntityAttributes['CREATED_DATE']);
274 unset($newEntityAttributes['UPDATED_DATE']);
275 $result = array();
276 foreach ($oldEntityAttributes as $key => $value)
277 {
278 if ($newEntityAttributes[$key] != $value)
279 {
280 $result[$key] = $newEntityAttributes[$key];
281 }
282 }
283
284 return $result;
285 }
286
292 private function update($primary, array $data)
293 {
294 $tableClassName = static::getTableClassName();
295 $data['UPDATED_DATE'] = new DateTime();
296 unset($data['ID']);
297 $resultData = $tableClassName::update($primary, $data);
298
299 foreach ($resultData->getData() as $key => $value)
300 {
301 $this->currentDbState[$key] = $value;
302 }
303 return $resultData;
304 }
305
306
310 public static function getClassName()
311 {
312 return get_called_class();
313 }
314
315
322 public static function load($filter, array $with = array(), $order = array())
323 {
324 $models = static::getModelList(array(
325 'select' => array('*'),
326 'filter' => $filter,
327 'with' => $with,
328 'order' => $order
329 ));
330 return array_shift($models);
331 }
332
338 protected static function getModelList(array $parameters)
339 {
340 $modelList = array();
341 $query = static::getList($parameters);
342 while ($row = $query->fetch())
343 {
344 if (!$modelList[$row['ID']])
345 {
346 $model = static::buildFromArray($row);
347 }
348 else
349 {
350 $model = static::buildFromArray($row, $modelList[$row['ID']]);
351 }
352 if ($model->id)
353 {
354 $modelList[$model->id] = $model;
355 }
356 }
357
358 return $modelList;
359 }
360
366 protected static function getList(array $parameters)
367 {
369 $tableClass = static::getTableClassName();
370 return $tableClass::getList(static::prepareGetListParameters($parameters));
371 }
372
380 protected static function buildFromArray(array $attributes, $parentEntity = null)
381 {
383 $model = new static;
384 return $model->setAttributes($attributes, $parentEntity);
385 }
386
394 private function setAttributes(array $attributes, $parentEntity)
395 {
396 foreach ($attributes as $key => $value)
397 {
398 if ($value === null)
399 {
400 unset($attributes[$key]);
401 }
402 }
403 if (!$parentEntity)
404 {
405 $mapAttributes = static::getMapAttributes();
406 foreach ($attributes as $key => $value)
407 {
408 if (!empty($mapAttributes[$key]))
409 {
410 $this->{$mapAttributes[$key]} = $value;
411 $this->currentDbState[$key] = $value;
412 unset($attributes[$key]);
413 }
414 }
415 $parentEntity = $this;
416 }
417
418 if (empty($attributes))
419 {
420 return $parentEntity;
421 }
422
423 $subEntitiesMapAttributes = static::getMapReferenceAttributes();
424 $subEntitiesKeys = array_keys($subEntitiesMapAttributes);
425
426
427 $subEntityAttributes = array();
428 $loadedSubEntitiesKeys = array();
429 foreach ($attributes as $key => $value)
430 {
431 $delimiter = self::ATTRIBUTE_SLICE_DELIMITER;
432 $selectedAttributeParts = explode($delimiter, $key);
433 if (count($selectedAttributeParts) == 2 && in_array($selectedAttributeParts[0], $subEntitiesKeys))
434 {
435 $loadedSubEntitiesKeys[$selectedAttributeParts[0]] = $selectedAttributeParts[0];
436 $subEntityAttributes[$selectedAttributeParts[0]][$selectedAttributeParts[1]] = $value;
437 }
438 elseif (count($selectedAttributeParts) >= 2)
439 {
440 $nestedEntityParentKey = array_shift($selectedAttributeParts);
441 $nestedElementKey = implode(self::ATTRIBUTE_SLICE_DELIMITER, $selectedAttributeParts);
442 $subEntityAttributes[$nestedEntityParentKey][$nestedElementKey] = $value;
443 }
444 }
445
446 foreach ($subEntityAttributes as $key => $validAttributes)
447 {
448 if (!empty($subEntitiesMapAttributes[$key]))
449 {
451 $targetEntityClass = $subEntitiesMapAttributes[$key]['targetEntity'];
452 if ($subEntitiesMapAttributes[$key]['type'] != Common::MANY_TO_ONE)
453 {
454 if (!isset($parentEntity->{$key}[$validAttributes['ID']]))
455 {
456 $subEntity = $targetEntityClass::buildFromArray($validAttributes);
457
458 $nestedEntityReferenceMap = $subEntity::getMapReferenceAttributes();
459
460
464 if ($subEntitiesMapAttributes[$key]['type'] == Common::ONE_TO_MANY)
465 {
466 if (!empty($nestedEntityReferenceMap[$subEntitiesMapAttributes[$key]['mappedBy']]))
467 {
468 $subEntity->{$subEntitiesMapAttributes[$key]['mappedBy']} = $parentEntity;
469 }
470 }
471 $parentEntity->{$key}[$subEntity->id] = $subEntity;
472 }
473 else
474 {
475 $targetEntityClass::buildFromArray($validAttributes, $parentEntity->{$key}[$validAttributes['ID']]);
476 }
477 }
478 else
479 {
480 $subEntity = $targetEntityClass::buildFromArray($validAttributes);
481 $parentEntity->{$key} = $subEntity;
482 }
483
484 }
485 }
486
487
488 return $parentEntity;
489 }
490
496 protected static function prepareGetListParameters(array $parameters)
497 {
498 if (!empty($parameters['with']))
499 {
500 if (!is_array($parameters['with']))
501 {
502 throw new ArgumentException('"with" must be array');
503 }
504 if (!isset($parameters['select']))
505 {
506 $parameters['select'] = array('*');
507 }
508 elseif (!in_array('*', $parameters['select']) && !in_array('ID', $parameters['select']))
509 {
510 $parameters['select'][] = 'ID';
511 }
512 $parameters['select'] = array_merge($parameters['select'], static::buildOrmSelectForReference($parameters['with']));
513 }
514
515 unset($parameters['with']);
516 return $parameters;
517 }
518
524 protected static function buildOrmSelectForReference(array $with)
525 {
526 $select = array();
527 $referenceAttributes = static::getMapReferenceAttributes();
528 foreach ($with as $referenceKey)
529 {
530 $testNesting = explode('.', $referenceKey);
531 $nestedReferenceAttributes = $referenceAttributes;
532 $prefix = '';
533 $fromKeyNamePrefix = '';
534 foreach ($testNesting as $reference)
535 {
536 if (!empty($nestedReferenceAttributes[$reference]))
537 {
538 $prefix = $prefix . $reference . self::ATTRIBUTE_SLICE_DELIMITER;
539 switch ($nestedReferenceAttributes[$reference]['type'])
540 {
541 case Common::ONE_TO_MANY:
543 $targetEntity = $nestedReferenceAttributes[$reference]['targetEntity'];
544 $targetOrmTable = $targetEntity::getTableClassName();
545 $fromKeyNamePrefix = !empty($fromKeyNamePrefix) ? $fromKeyNamePrefix . '.' : '';
546 $select[$prefix] = $fromKeyNamePrefix.$targetOrmTable::getClassName().':'.mb_strtoupper($nestedReferenceAttributes[$reference]['mappedBy']);
547 $fromKeyNamePrefix .= $targetOrmTable::getClassName().':'.mb_strtoupper($nestedReferenceAttributes[$reference]['mappedBy']);
548 $nestedReferenceAttributes = $targetEntity::getMapReferenceAttributes();
549 break;
550 case Common::MANY_TO_MANY:
551 $fromKeyName = array_keys($nestedReferenceAttributes[$reference]['join']['column']);
552 $fromKeyName = $fromKeyName[0];
553 $fromKeyNamePrefix = !empty($fromKeyNamePrefix) ? $fromKeyNamePrefix . '.' : '';
554 $toKeyName = array_keys($nestedReferenceAttributes[$reference]['join']['inverseColumn']);
555 $toKeyName = $toKeyName[0];
556 $select[$prefix] = $fromKeyNamePrefix . $nestedReferenceAttributes[$reference]['join']['tableClassName']
557 . ':'
558 . $fromKeyName
559 . '.'
560 . $toKeyName;
561 $targetEntity = $nestedReferenceAttributes[$reference]['targetEntity'];
562 $nestedReferenceAttributes = $targetEntity::getMapReferenceAttributes();
563 break;
564 case Common::MANY_TO_ONE:
565 $fromKeyNamePrefix = !empty($fromKeyNamePrefix) ? $fromKeyNamePrefix . '.' : '';
566 $select[$prefix] = $fromKeyNamePrefix.mb_strtoupper($reference);
567 $fromKeyNamePrefix .= mb_strtoupper($reference);
568 break;
569
570 }
571 }
572 else
573 {
574 throw new ArgumentException("Reference with name:" . $reference . ' not define in reference map');
575 }
576 }
577 }
578 return $select;
579 }
580
584 public function delete()
585 {
586 $ownerId = $this->getId();
587 $referenceAttributesMap = static::getMapReferenceAttributes();
588
589 foreach ($referenceAttributesMap as $referenceKey => $referenceAttributes)
590 {
591 if (!empty($referenceAttributes['options']['deleteSkip']))
592 {
593 continue;
594 }
595
596 if (!$this->{$referenceKey})
597 {
598 $this->loadAttribute($referenceKey);
599 }
600
601
602 if ($this->{$referenceKey})
603 {
604 switch ($referenceAttributes['type'])
605 {
606 case Common::ONE_TO_MANY:
607 $this->deleteOneToManyReferences($this->{$referenceKey});
608 break;
609 case Common::MANY_TO_MANY:
610 $this->deleteManyToManyReferences($this->{$referenceKey}, $referenceAttributes, $ownerId);
611 break;
612 case Common::MANY_TO_ONE:
613 $this->deleteManyToOneReference($this->{$referenceKey}, $referenceAttributes, $ownerId);
614 break;
615 case Common::ONE_TO_ONE:
616 //TODO
617 break;
618 }
619 }
620
621 }
622 $entityTableClass = static::getTableClassName();
623 $deleteEntity = $entityTableClass::delete($ownerId);
624 if ($deleteEntity->isSuccess())
625 {
626 return true;
627 }
628 else
629 {
630 $this->errors[] = $deleteEntity->getErrors();
631 return null;
632 }
633 }
634
638 private function deleteOneToManyReferences($referenceEntities)
639 {
640 foreach ($referenceEntities as $referenceEntity)
641 {
642 $referenceEntity->delete();
643 }
644 }
645
651 private function deleteManyToManyReferences($referenceEntities, $assoc, $ownerId)
652 {
653 $connectColumn = array_shift($assoc['join']['column']);
654 $connectInverseColumn = array_shift($assoc['join']['inverseColumn']);
655 foreach ($referenceEntities as $referenceEntity)
656 {
657 $connectPrimaryKey = array();
659 $connectTableClass = $assoc['join']['tableClassName'];
660 $connectPrimaryKey[$connectColumn[1]] = $ownerId;
661 $connectPrimaryKey[$connectInverseColumn[1]] = $referenceEntity->getId();;
662 $connectTableClass::delete($connectPrimaryKey);
663 $referenceEntity->delete();
664 }
665 }
666
673 private function deleteManyToOneReference(&$referenceEntity, $assoc, $ownerId)
674 {
675 if ($referenceEntity instanceof $assoc['targetEntity'])
676 {
677 unset($referenceEntity->{$assoc['inversedBy']}[$ownerId]);
678 }
679 }
680
684 public function getId()
685 {
686 return $this->id;
687 }
688
693 public function setId($id)
694 {
695 $this->id = $id;
696 }
697
701 public function getCreatedAt()
702 {
703 return $this->createdAt;
704 }
705
710 public function setCreatedAt(DateTime $createdAt)
711 {
712 $this->createdAt = $createdAt;
713 }
714
718 public function getUpdatedAt()
719 {
720 return $this->updatedAt;
721 }
722
727 public function setUpdatedAt(DateTime $updatedAt)
728 {
729 $this->updatedAt = $updatedAt;
730 }
731
736 public static function loadById($id)
737 {
738 $entity = static::load(array('ID' => $id));
739 return $entity;
740 }
741
746 public function loadAttribute($attributeName)
747 {
748 if (property_exists($this, $attributeName) && $this->{$attributeName} == null)
749 {
750 $entity = static::load(array('ID' => $this->getId()), array($attributeName));
751 $this->{$attributeName} = $entity->{$attributeName};
752 }
753 }
754
755
763 public function __call($name , array $arguments)
764 {
765 $isDeleteReferenceCall = preg_match_all('/^delete(\w+)/', $name, $deleteCallNameParts);
766 if ($isDeleteReferenceCall)
767 {
768 $referenceName = $deleteCallNameParts[1][0];
769 $referenceName = mb_strtolower($referenceName);
770 $referenceMapAttributes = $this::getMapReferenceAttributes();
771 if (!empty($referenceMapAttributes[$referenceName]))
772 {
774 $entities = !empty($arguments[0]) ? $arguments[0] : array();
775 if (!is_array($entities))
776 {
777 $entities = array(
778 $entities
779 );
780 }
781
782 foreach ($entities as $entity)
783 {
784 $this->deletedEntities[$referenceName][$entity->getId()] = $entity;
785 unset($this->{$referenceName}[$entity->getId()]);
786 }
787 }
788 }
789
790 $isAddReferenceCall = preg_match_all('/^add(\w+)/', $name, $addCallNameParts);
791 if ($isAddReferenceCall)
792 {
793 $referenceName = $addCallNameParts[1][0];
794 $referenceName = mb_strtolower($referenceName);
795 $referenceMapAttributes = $this::getMapReferenceAttributes();
796 if (!empty($referenceMapAttributes[$referenceName]))
797 {
799 $entities = !empty($arguments[0]) ? $arguments[0] : array();
800 if (!is_array($entities))
801 {
802 $entities = array(
803 $entities
804 );
805 }
806
807 foreach ($entities as $entity)
808 {
809 $this->{$referenceName}[] = $entity;
810 }
811 }
812 }
813
814 }
815
819 private function deleteReference($deletedReferenceEntities)
820 {
821 foreach ($deletedReferenceEntities as $referenceName => $referenceEntities)
822 {
823 $map = $this::getMapReferenceAttributes();
824 $map = $map[$referenceName];
825 switch ($map['type'])
826 {
827 case Common::ONE_TO_MANY:
828 foreach ($referenceEntities as $referenceEntity)
829 {
830 $referenceEntity->delete();
831 }
832 break;
833 case Common::MANY_TO_MANY:
834 $connectColumn = array_shift($map['join']['column']);
835 $connectInverseColumn = array_shift($map['join']['inverseColumn']);
836
837 foreach ($referenceEntities as $referenceEntity)
838 {
839 $connectPrimaryKey = array();
841 $connectTableClass = $map['join']['tableClassName'];
842 $connectPrimaryKey[$connectColumn[1]] = $this->getId();
843 $connectPrimaryKey[$connectInverseColumn[1]] = $referenceEntity->getId();
844 $connectTableClass::delete($connectPrimaryKey);
845 }
846 break;
847 case Common::ONE_TO_ONE:
848 //TODO
849 break;
850 case Common::MANY_TO_ONE:
851 //TODO
852 break;
853 }
854 }
855
856
857 }
858
862 public function getErrors()
863 {
864 return $this->errors;
865 }
866
870 public function getCurrentDbState()
871 {
872 return $this->currentDbState;
873 }
874
875
876 public static function factoryWithHorizontalCells($cellCount = 1)
877 {
878 $row = new DashboardRow();
879 $row->setGId(Util::generateUserUniqueId());
880 $map = [
881 'type' => 'cell-container',
882 'orientation' => 'horizontal',
883 'elements' => []
884 ];
885 for ($i = 0; $i < $cellCount; $i++)
886 {
887 $cellId = 'cell_' . randString(4);
888 $map['elements'][] = [
889 'type' => 'cell',
890 'id' => $cellId
891 ];
892 }
893 $row->setLayoutMap($map);
894
895 return $row;
896 }
897
898}
static prepareGetListParameters(array $parameters)
Definition model.php:496
static getModelList(array $parameters)
Definition model.php:338
static factoryWithHorizontalCells($cellCount=1)
Definition model.php:876
static load($filter, array $with=array(), $order=array())
Definition model.php:322