Bitrix-D7 22.6
 
Загрузка...
Поиск...
Не найдено
datamanager.php
1<?php
10
11use Bitrix\Main;
21use Bitrix\Main\ORM\Query\Result as QueryResult;
26
27Loc::loadMessages(__FILE__);
28
32abstract class DataManager
33{
34 const EVENT_ON_BEFORE_ADD = "OnBeforeAdd";
35 const EVENT_ON_ADD = "OnAdd";
36 const EVENT_ON_AFTER_ADD = "OnAfterAdd";
37 const EVENT_ON_BEFORE_UPDATE = "OnBeforeUpdate";
38 const EVENT_ON_UPDATE = "OnUpdate";
39 const EVENT_ON_AFTER_UPDATE = "OnAfterUpdate";
40 const EVENT_ON_BEFORE_DELETE = "OnBeforeDelete";
41 const EVENT_ON_DELETE = "OnDelete";
42 const EVENT_ON_AFTER_DELETE = "OnAfterDelete";
43
45 protected static $entity;
46
48 protected static $objectClass;
49
51 protected static $collectionClass;
52
54 protected static $currentDeletingObjects;
55
57 protected static $reservedWords = [
58 // keywords
59 'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue',
60 'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach',
61 'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends', 'final', 'finally', 'for', 'foreach', 'function',
62 'global', 'goto', 'if', 'implements', 'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset',
63 'list', 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', 'return',
64 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor', 'yield',
65 // classes
66 'self', 'parent',
67 // others
68 'int', 'float', 'bool', 'string', 'true', 'false', 'null', 'void', 'iterable', 'object', 'resource', 'mixed', 'numeric',
69 ];
70
78 public static function getEntity()
79 {
80 $class = static::getEntityClass()::normalizeEntityClass(get_called_class());
81
82 if (!isset(static::$entity[$class]))
83 {
84 static::$entity[$class] = static::getEntityClass()::getInstance($class);
85 }
86
87 return static::$entity[$class];
88 }
89
90 public static function unsetEntity($class)
91 {
92 $class = static::getEntityClass()::normalizeEntityClass($class);
93
94 if (isset(static::$entity[$class]))
95 {
96 unset(static::$entity[$class]);
97 }
98 }
99
105 public static function getTableName()
106 {
107 return null;
108 }
109
115 public static function getConnectionName()
116 {
117 return 'default';
118 }
119
123 public static function getTitle()
124 {
125 return null;
126 }
127
133 public static function getObjectClass()
134 {
135 if (!isset(static::$objectClass[get_called_class()]))
136 {
137 static::$objectClass[get_called_class()] = static::getObjectClassByDataClass(get_called_class());
138 }
139
140 return static::$objectClass[get_called_class()];
141 }
142
148 final public static function getObjectClassName()
149 {
150 $class = static::getObjectClass();
151 return substr($class, strrpos($class, '\\') + 1);
152 }
153
154 protected static function getObjectClassByDataClass($dataClass)
155 {
156 $objectClass = static::getEntityClass()::normalizeName($dataClass);
157
158 // make class name more unique
159 $namespace = substr($objectClass, 0, strrpos($objectClass, '\\') + 1);
160 $className = substr($objectClass, strrpos($objectClass, '\\') + 1);
161
162 $className = static::getEntityClass()::getDefaultObjectClassName($className);
163
164 return $namespace.$className;
165 }
166
172 public static function getCollectionClass()
173 {
174 if (!isset(static::$collectionClass[get_called_class()]))
175 {
176 static::$collectionClass[get_called_class()] = static::getCollectionClassByDataClass(get_called_class());
177 }
178
179 return static::$collectionClass[get_called_class()];
180 }
181
187 final public static function getCollectionClassName()
188 {
189 $class = static::getCollectionClass();
190 return substr($class, strrpos($class, '\\') + 1);
191 }
192
193 protected static function getCollectionClassByDataClass($dataClass)
194 {
195 $objectClass = static::getEntityClass()::normalizeName($dataClass);
196
197 // make class name more unique
198 $namespace = substr($objectClass, 0, strrpos($objectClass, '\\') + 1);
199 $className = substr($objectClass, strrpos($objectClass, '\\') + 1);
200
201 $className = static::getEntityClass()::getDefaultCollectionClassName($className);
202
203 return $namespace.$className;
204 }
205
209 public static function getObjectParentClass()
210 {
211 return EntityObject::class;
212 }
213
217 public static function getCollectionParentClass()
218 {
219 return Collection::class;
220 }
221
225 public static function getQueryClass()
226 {
227 return Query::class;
228 }
229
233 public static function getEntityClass()
234 {
235 return Entity::class;
236 }
237
245 final public static function createObject($setDefaultValues = true)
246 {
247 return static::getEntity()->createObject($setDefaultValues);
248 }
249
255 final public static function createCollection()
256 {
257 return static::getEntity()->createCollection();
258 }
259
269 final public static function wakeUpObject($row)
270 {
271 return static::getEntity()->wakeUpObject($row);
272 }
273
283 final public static function wakeUpCollection($rows)
284 {
285 return static::getEntity()->wakeUpCollection($rows);
286 }
287
292 public static function getMap()
293 {
294 return array();
295 }
296
297 public static function getUfId()
298 {
299 return null;
300 }
301
302 public static function isUts()
303 {
304 return false;
305 }
306
307 public static function isUtm()
308 {
309 return false;
310 }
311
317 public static function setDefaultScope($query)
318 {
319 return $query;
320 }
321
327 public static function postInitialize(Entity $entity)
328 {
329 return null;
330 }
331
343 public static function getByPrimary($primary, array $parameters = array())
344 {
345 static::normalizePrimary($primary);
346 static::validatePrimary($primary);
347
348 $primaryFilter = array();
349
350 foreach ($primary as $k => $v)
351 {
352 $primaryFilter['='.$k] = $v;
353 }
354
355 if (isset($parameters['filter']))
356 {
357 $parameters['filter'] = array($primaryFilter, $parameters['filter']);
358 }
359 else
360 {
361 $parameters['filter'] = $primaryFilter;
362 }
363
364 return static::getList($parameters);
365 }
366
377 public static function getById($id)
378 {
379 return static::getByPrimary($id);
380 }
381
392 public static function getRowById($id)
393 {
394 $result = static::getByPrimary($id);
395 $row = $result->fetch();
396
397 return (is_array($row)? $row : null);
398 }
399
410 public static function getRow(array $parameters)
411 {
412 $parameters['limit'] = 1;
413 $result = static::getList($parameters);
414 $row = $result->fetch();
415
416 return (is_array($row)? $row : null);
417 }
418
441 public static function getList(array $parameters = array())
442 {
443 $query = static::query();
444
445 if(!isset($parameters['select']))
446 {
447 $query->setSelect(array('*'));
448 }
449
450 foreach($parameters as $param => $value)
451 {
452 switch($param)
453 {
454 case 'select':
455 $query->setSelect($value);
456 break;
457 case 'filter':
458 $value instanceof Filter ? $query->where($value) : $query->setFilter($value);
459 break;
460 case 'group':
461 $query->setGroup($value);
462 break;
463 case 'order';
464 $query->setOrder($value);
465 break;
466 case 'limit':
467 $query->setLimit($value);
468 break;
469 case 'offset':
470 $query->setOffset($value);
471 break;
472 case 'count_total':
473 $query->countTotal($value);
474 break;
475 case 'runtime':
476 foreach ($value as $name => $fieldInfo)
477 {
478 $query->registerRuntimeField($name, $fieldInfo);
479 }
480 break;
481 case 'data_doubling':
482 if($value)
483 {
484 $query->enableDataDoubling();
485 }
486 else
487 {
488 $query->disableDataDoubling();
489 }
490 break;
491 case 'private_fields':
492 if($value)
493 {
494 $query->enablePrivateFields();
495 }
496 else
497 {
498 $query->disablePrivateFields();
499 }
500 break;
501 case 'cache':
502 $query->setCacheTtl($value["ttl"]);
503 if(isset($value["cache_joins"]))
504 {
505 $query->cacheJoins($value["cache_joins"]);
506 }
507 break;
508 default:
509 throw new Main\ArgumentException("Unknown parameter: ".$param, $param);
510 }
511 }
512
513 return $query->exec();
514 }
515
526 public static function getCount($filter = array(), array $cache = array())
527 {
528 $query = static::query();
529
530 // new filter
531 $query->addSelect(new ExpressionField('CNT', 'COUNT(1)'));
532
533 if ($filter instanceof Filter)
534 {
535 $query->where($filter);
536 }
537 else
538 {
539 $query->setFilter($filter);
540 }
541
542 if(isset($cache["ttl"]))
543 {
544 $query->setCacheTtl($cache["ttl"]);
545 }
546
547 $result = $query->exec()->fetch();
548
549 return (int)$result['CNT'];
550 }
551
559 public static function query()
560 {
561 $queryClass = static::getQueryClass();
562 return new $queryClass(static::getEntity());
563 }
564
572 protected static function replaceFieldName($data = array())
573 {
574 $newData = [];
575 $entity = static::getEntity();
576
577 foreach ($data as $fieldName => $value)
578 {
579 $newData[$entity->getField($fieldName)->getColumnName()] = $value;
580 }
581
582 return $newData;
583 }
584
592 protected static function normalizePrimary(&$primary, $data = array())
593 {
594 $entity = static::getEntity();
595 $entity_primary = $entity->getPrimaryArray();
596
597 if ($primary === null)
598 {
599 $primary = array();
600
601 // extract primary from data array
602 foreach ($entity_primary as $key)
603 {
605 $field = $entity->getField($key);
606 if ($field->isAutocomplete())
607 {
608 continue;
609 }
610
611 if (!isset($data[$key]))
612 {
613 throw new Main\ArgumentException(sprintf(
614 'Primary `%s` was not found when trying to query %s row.', $key, $entity->getName()
615 ));
616 }
617
618 $primary[$key] = $data[$key];
619 }
620 }
621 elseif (is_scalar($primary))
622 {
623 if (count($entity_primary) > 1)
624 {
625 throw new Main\ArgumentException(sprintf(
626 'Require multi primary {`%s`}, but one scalar value "%s" found when trying to query %s row.',
627 join('`, `', $entity_primary), $primary, $entity->getName()
628 ));
629 }
630
631 $primary = array($entity_primary[0] => $primary);
632 }
633 }
634
641 protected static function validatePrimary($primary)
642 {
643 $entity = static::getEntity();
644 if (is_array($primary))
645 {
646 if(empty($primary))
647 {
648 throw new Main\ArgumentException(sprintf(
649 'Empty primary found when trying to query %s row.', $entity->getName()
650 ));
651 }
652
653 $entity_primary = $entity->getPrimaryArray();
654
655 foreach (array_keys($primary) as $key)
656 {
657 if (!in_array($key, $entity_primary, true))
658 {
659 throw new Main\ArgumentException(sprintf(
660 'Unknown primary `%s` found when trying to query %s row.',
661 $key, $entity->getName()
662 ));
663 }
664 }
665 }
666 else
667 {
668 throw new Main\ArgumentException(sprintf(
669 'Unknown type of primary "%s" found when trying to query %s row.', gettype($primary), $entity->getName()
670 ));
671 }
672
673 // primary values validation
674 foreach ($primary as $key => $value)
675 {
676 if (!is_scalar($value) && !($value instanceof Main\Type\Date))
677 {
678 throw new Main\ArgumentException(sprintf(
679 'Unknown value type "%s" for primary "%s" found when trying to query %s row.',
680 gettype($value), $key, $entity->getName()
681 ));
682 }
683 }
684 }
685
696 public static function checkFields(Result $result, $primary, array $data)
697 {
698 $entity = static::getEntity();
699 //checks required fields
700 foreach ($entity->getFields() as $field)
701 {
702 if ($field instanceof ScalarField && $field->isRequired())
703 {
704 $fieldName = $field->getName();
705 if (
706 (empty($primary) && (!isset($data[$fieldName]) || $field->isValueEmpty($data[$fieldName])))
707 || (!empty($primary) && isset($data[$fieldName]) && $field->isValueEmpty($data[$fieldName]))
708 )
709 {
710 $result->addError(new FieldError(
711 $field,
712 Loc::getMessage("MAIN_ENTITY_FIELD_REQUIRED", array("#FIELD#"=>$field->getTitle())),
713 FieldError::EMPTY_REQUIRED
714 ));
715 }
716 }
717 }
718
719 // checks data - fieldname & type & strlen etc.
720 foreach ($data as $k => $v)
721 {
722 if ($entity->hasField($k))
723 {
724 $field = $entity->getField($k);
725
726 }
727 else
728 {
729 throw new Main\ArgumentException(sprintf(
730 'Field `%s` not found in entity when trying to query %s row.',
731 $k, $entity->getName()
732 ));
733 }
734
735 $field->validateValue($v, $primary, $data, $result);
736 }
737 }
738
748 protected static function convertArrayToObject(&$fields, $setDefaultValues = false, $primary = null)
749 {
750 // extended data format
751 $data = null;
752
753 if (isset($fields["fields"]) && is_array($fields["fields"]))
754 {
755 $data = $fields;
756 $fields = $data["fields"];
757 }
758
759 // convert to object
760 if (isset($fields['__object']))
761 {
762 $object = $fields['__object'];
763 unset($fields['__object']);
764 }
765 else
766 {
767 $entity = static::getEntity();
768
770 if ($primary === null)
771 {
772 $object = $entity->createObject($setDefaultValues);
773
774 foreach ($fields as $fieldName => $value)
775 {
776 // sometimes data array can be used for storing non-entity data
777 if ($entity->hasField($fieldName))
778 {
779 $object->sysSetValue($fieldName, $value);
780 }
781 }
782 }
783 else
784 {
785 $object = $entity->wakeUpObject($primary);
786
787 foreach ($fields as $fieldName => $value)
788 {
789 // sometimes data array can be used for storing non-entity data
790 if ($entity->hasField($fieldName))
791 {
792 if ($entity->getField($fieldName) instanceof ScalarField && $entity->getField($fieldName)->isPrimary())
793 {
794 // ignore old primary
795 if (array_key_exists($fieldName, $primary) && $primary[$fieldName] == $value)
796 {
797 unset($fields[$fieldName]);
798 continue;
799 }
800
801 // but prevent primary changing
802 trigger_error(sprintf(
803 'Primary of %s %s can not be changed. You can delete this row and add a new one',
804 static::getObjectClass(), Main\Web\Json::encode($object->primary)
805 ), E_USER_WARNING);
806
807 continue;
808 }
809
810 $object->sysSetValue($fieldName, $value);
811 }
812 }
813 }
814 }
815
816 // auth context
817 if (isset($data['auth_context']))
818 {
819 $object->authContext = $data['auth_context'];
820 }
821
822 return $object;
823 }
824
830 protected static function checkUfFields($object, $ufdata, $result)
831 {
832 global $USER_FIELD_MANAGER, $APPLICATION;
833
834 $userId = ($object->authContext && $object->authContext->getUserId())
835 ? $object->authContext->getUserId()
836 : false;
837
838 $ufPrimary = ($object->sysGetState() === Main\ORM\Objectify\State::RAW)
839 ? false
840 : end($object->primary);
841
842 if (!$USER_FIELD_MANAGER->CheckFields($object->entity->getUfId(), $ufPrimary, $ufdata, $userId))
843 {
844 if (is_object($APPLICATION) && $APPLICATION->getException())
845 {
846 $e = $APPLICATION->getException();
847 $result->addError(new EntityError($e->getString()));
848 $APPLICATION->resetException();
849 }
850 else
851 {
852 $result->addError(new EntityError("Unknown error while checking userfields"));
853 }
854 }
855 }
856
874 public static function add(array $data)
875 {
876 global $USER_FIELD_MANAGER;
877
878 // compatibility
879 $fields = $data;
880
881 // prepare entity object for compatibility with new code
882 $object = static::convertArrayToObject($fields, true);
883
884 $entity = static::getEntity();
885 $result = new AddResult();
886
887 try
888 {
889 static::callOnBeforeAddEvent($object, $fields, $result);
890
891 // actualize old-style fields array from object
892 $fields = $object->collectValues(Values::CURRENT, FieldTypeMask::SCALAR);
893
894 // uf values
895 $ufdata = $object->collectValues(Values::CURRENT, FieldTypeMask::USERTYPE);
896
897 // check data
898 static::checkFields($result, null, $fields);
899
900 // check uf data
901 if (!empty($ufdata))
902 {
903 static::checkUfFields($object, $ufdata, $result);
904 }
905
906 // check if there is still some data
907 if (empty($fields) && empty($ufdata))
908 {
909 $result->addError(new EntityError("There is no data to add."));
910 }
911
912 // return if any error
913 if (!$result->isSuccess(true))
914 {
915 return $result;
916 }
917
918 //event on adding
919 self::callOnAddEvent($object, $fields, $ufdata);
920
921 // use save modifiers
922 $fieldsToDb = $fields;
923
924 foreach ($fieldsToDb as $fieldName => $value)
925 {
926 $field = $entity->getField($fieldName);
927 $fieldsToDb[$fieldName] = $field->modifyValueBeforeSave($value, $fields);
928 }
929
930 // save data
931 $connection = $entity->getConnection();
932
933 $tableName = $entity->getDBTableName();
934 $identity = $entity->getAutoIncrement();
935
936 $dataReplacedColumn = static::replaceFieldName($fieldsToDb);
937 $id = $connection->add($tableName, $dataReplacedColumn, $identity);
938
939 // build standard primary
940 $primary = null;
941 $isGuessedPrimary = false;
942
943 if (!empty($id))
944 {
945 if($entity->getAutoIncrement() <> '')
946 {
947 $primary = array($entity->getAutoIncrement() => $id);
948 static::normalizePrimary($primary);
949 }
950 else
951 {
952 // for those who did not set 'autocomplete' flag but wants to get id from result
953 $primary = array('ID' => $id);
954 $isGuessedPrimary = true;
955 }
956 }
957 else
958 {
959 static::normalizePrimary($primary, $fields);
960 }
961
962 // fill result
963 $result->setPrimary($primary);
964 $result->setData($fields + $ufdata);
965 $result->setObject($object);
966
967 if (!$isGuessedPrimary)
968 {
969 foreach ($primary as $primaryName => $primaryValue)
970 {
971 $object->sysSetActual($primaryName, $primaryValue);
972 }
973 }
974
975 // save uf data
976 if (!empty($ufdata))
977 {
978 $ufUserId = false;
979
980 if ($object->authContext)
981 {
982 $ufUserId = $object->authContext->getUserId();
983 }
984
985 $USER_FIELD_MANAGER->update($entity->getUfId(), end($primary), $ufdata, $ufUserId);
986 }
987
988 static::cleanCache();
989
990 static::callOnAfterAddEvent($object, $fields + $ufdata, $id);
991 }
992 catch (\Exception $e)
993 {
994 // check result to avoid warning
995 $result->isSuccess();
996
997 throw $e;
998 }
999
1000 return $result;
1001 }
1002
1011 public static function addMulti($rows, $ignoreEvents = false)
1012 {
1013 global $USER_FIELD_MANAGER;
1014
1015 $rows = array_values($rows);
1016 $forceSeparateQueries = false;
1017
1018 if (!$ignoreEvents && count($rows) > 1 && strlen(static::getEntity()->getAutoIncrement()))
1019 {
1020 $forceSeparateQueries = true;
1021
1022 // change to warning
1023 trigger_error(
1024 'Multi-insert doesn\'t work with events as far as we can not get last inserted IDs that we need for the events. '.
1025 'Insert query was forced to multiple separate queries.',
1026 E_USER_NOTICE
1027 );
1028 }
1029
1030 // prepare objects
1031 $objects = [];
1032
1033 foreach ($rows as $k => &$row)
1034 {
1035 $objects[$k] = static::convertArrayToObject($row, true);
1036 }
1037
1038 $entity = static::getEntity();
1039 $result = new AddResult();
1040
1041 try
1042 {
1043 // call onBeforeEvent
1044 if (!$ignoreEvents)
1045 {
1046 foreach ($objects as $k => $object)
1047 {
1048 static::callOnBeforeAddEvent($object, $rows[$k], $result);
1049 }
1050 }
1051
1052 // collect array data
1053 $allFields = [];
1054 $allUfData = [];
1055
1056 foreach ($objects as $k => $object)
1057 {
1058 // actualize old-style fields array from object
1059 $allFields[$k] = $object->collectValues(Values::CURRENT, FieldTypeMask::SCALAR);
1060
1061 // uf values
1062 $allUfData[$k] = $object->collectValues(Values::CURRENT, FieldTypeMask::USERTYPE);
1063 }
1064
1065 // check data and uf
1066 foreach ($objects as $k => $object)
1067 {
1068 $fields = $allFields[$k];
1069 $ufdata = $allUfData[$k];
1070
1071 // check data
1072 static::checkFields($result, null, $fields);
1073
1074 // check uf data
1075 if (!empty($ufdata))
1076 {
1077 static::checkUfFields($object, $ufdata, $result);
1078 }
1079
1080 // check if there is still some data
1081 if (empty($fields) && empty($ufdata))
1082 {
1083 $result->addError(new EntityError("There is no data to add."));
1084 }
1085 }
1086
1087 // return if any error in any row
1088 if (!$result->isSuccess(true))
1089 {
1090 return $result;
1091 }
1092
1093 //event on adding
1094 if (!$ignoreEvents)
1095 {
1096 foreach ($objects as $k => $object)
1097 {
1098 $fields = $allFields[$k];
1099 $ufdata = $allUfData[$k];
1100
1101 self::callOnAddEvent($object, $fields, $ufdata);
1102 }
1103 }
1104
1105 // prepare sql
1106 $allSqlData = [];
1107
1108 foreach ($allFields as $k => $fields)
1109 {
1110 // use save modifiers
1111 $fieldsToDb = $fields;
1112
1113 foreach ($fieldsToDb as $fieldName => $value)
1114 {
1115 $field = $entity->getField($fieldName);
1116 $fieldsToDb[$fieldName] = $field->modifyValueBeforeSave($value, $fields);
1117 }
1118
1119 $dataReplacedColumn = static::replaceFieldName($fieldsToDb);
1120
1121 $allSqlData[$k] = $dataReplacedColumn;
1122 }
1123
1124 // save data
1125 $connection = $entity->getConnection();
1126
1127 $tableName = $entity->getDBTableName();
1128 $identity = $entity->getAutoIncrement();
1129 $ids = [];
1130
1131 // multi insert on db level
1132 if ($forceSeparateQueries)
1133 {
1134 foreach ($allSqlData as $k => $sqlData)
1135 {
1136 // remember all ids
1137 $ids[$k] = $connection->add($tableName, $sqlData, $identity);
1138 }
1139 }
1140 else
1141 {
1142 $id = $connection->addMulti($tableName, $allSqlData, $identity);
1143 }
1144
1145 if (count($allSqlData) > 1)
1146 {
1147 // id doesn't make sense when multiple inserts
1148 $id = null;
1149 }
1150 else
1151 {
1152 $object = $objects[0];
1153 $fields = $allFields[0];
1154
1155 // build standard primary
1156 $primary = null;
1157
1158 if (!empty($id))
1159 {
1160 if($entity->getAutoIncrement() <> '')
1161 {
1162 $primary = array($entity->getAutoIncrement() => $id);
1163 static::normalizePrimary($primary);
1164 }
1165 else
1166 {
1167 // for those who did not set 'autocomplete' flag but want to get id from result
1168 $primary = array('ID' => $id);
1169 }
1170 }
1171 else
1172 {
1173 static::normalizePrimary($primary, $fields);
1174 }
1175
1176 // fill result
1177 $result->setPrimary($primary);
1178 $result->setData($fields);
1179 $result->setObject($object);
1180 }
1181
1182 // save uf data
1183 foreach ($allUfData as $k => $ufdata)
1184 {
1185 if (!empty($ufdata))
1186 {
1187 $ufUserId = false;
1188
1189 if ($objects[$k]->authContext)
1190 {
1191 $ufUserId = $objects[$k]->authContext->getUserId();
1192 }
1193
1194 $USER_FIELD_MANAGER->update($entity->getUfId(), end($primary), $ufdata, $ufUserId);
1195 }
1196 }
1197
1198 static::cleanCache();
1199
1200 // after event
1201 if (!$ignoreEvents)
1202 {
1203 foreach ($objects as $k => $object)
1204 {
1205 $fields = $allFields[$k] + $allUfData[$k];
1206 $id = $forceSeparateQueries ? $ids[$k] : null;
1207
1208 static::callOnAfterAddEvent($object, $fields, $id);
1209 }
1210 }
1211 }
1212 catch (\Exception $e)
1213 {
1214 // check result to avoid warning
1215 $result->isSuccess();
1216
1217 throw $e;
1218 }
1219
1220 return $result;
1221 }
1222
1241 public static function update($primary, array $data)
1242 {
1243 global $USER_FIELD_MANAGER;
1244
1245 // check primary
1246 static::normalizePrimary(
1247 $primary, isset($data["fields"]) && is_array($data["fields"]) ? $data["fields"] : $data
1248 );
1249 static::validatePrimary($primary);
1250
1251 // compatibility
1252 $fields = $data;
1253
1254 // prepare entity object for compatibility with new code
1255 $object = static::convertArrayToObject($fields, false, $primary);
1256
1257 $entity = static::getEntity();
1258 $result = new UpdateResult();
1259
1260 try
1261 {
1262 static::callOnBeforeUpdateEvent($object, $fields, $result);
1263
1264 // actualize old-style fields array from object
1265 $fields = $object->collectValues(Values::CURRENT, FieldTypeMask::SCALAR);
1266
1267 // uf values
1268 $ufdata = $object->collectValues(Values::CURRENT, FieldTypeMask::USERTYPE);
1269
1270 // check data
1271 static::checkFields($result, $primary, $fields);
1272
1273 // check uf data
1274 if (!empty($ufdata))
1275 {
1276 static::checkUfFields($object, $ufdata, $result);
1277 }
1278
1279 // check if there is still some data
1280 if (empty($fields) && empty($ufdata))
1281 {
1282 return $result;
1283 }
1284
1285 // return if any error
1286 if (!$result->isSuccess(true))
1287 {
1288 return $result;
1289 }
1290
1291 static::callOnUpdateEvent($object, $fields, $ufdata);
1292
1293 // use save modifiers
1294 $fieldsToDb = $fields;
1295
1296 foreach ($fieldsToDb as $fieldName => $value)
1297 {
1298 $field = $entity->getField($fieldName);
1299 $fieldsToDb[$fieldName] = $field->modifyValueBeforeSave($value, $fields);
1300 }
1301
1302 // save data
1303 if (!empty($fieldsToDb))
1304 {
1305 $connection = $entity->getConnection();
1306 $helper = $connection->getSqlHelper();
1307
1308 $tableName = $entity->getDBTableName();
1309
1310 $dataReplacedColumn = static::replaceFieldName($fieldsToDb);
1311 $update = $helper->prepareUpdate($tableName, $dataReplacedColumn);
1312
1313 $replacedPrimary = static::replaceFieldName($primary);
1314 $id = array();
1315 foreach ($replacedPrimary as $k => $v)
1316 {
1317 $id[] = $helper->prepareAssignment($tableName, $k, $v);
1318 }
1319 $where = implode(' AND ', $id);
1320
1321 $sql = "UPDATE ".$helper->quote($tableName)." SET ".$update[0]." WHERE ".$where;
1322 $connection->queryExecute($sql, $update[1]);
1323
1324 $result->setAffectedRowsCount($connection);
1325 }
1326
1327 $result->setData($fields + $ufdata);
1328 $result->setPrimary($primary);
1329 $result->setObject($object);
1330
1331 // save uf data
1332 if (!empty($ufdata))
1333 {
1334 $ufUserId = false;
1335
1336 if ($object->authContext)
1337 {
1338 $ufUserId = $object->authContext->getUserId();
1339 }
1340
1341 $USER_FIELD_MANAGER->update($entity->getUfId(), end($primary), $ufdata, $ufUserId);
1342 }
1343
1344 static::cleanCache();
1345
1346 // event after update
1347 static::callOnAfterUpdateEvent($object, $fields + $ufdata);
1348 }
1349 catch (\Exception $e)
1350 {
1351 // check result to avoid warning
1352 $result->isSuccess();
1353
1354 throw $e;
1355 }
1356
1357 return $result;
1358 }
1359
1369 public static function updateMulti($primaries, $data, $ignoreEvents = false)
1370 {
1371 $entity = static::getEntity();
1372 $primaries = array_values($primaries);
1373
1375 $objects = [];
1376
1377 foreach ($primaries as &$primary)
1378 {
1379 static::normalizePrimary($primary, $data);
1380 static::validatePrimary($primary);
1381
1383 $object = $entity->wakeUpObject($primary);
1384
1385 foreach ($data as $k => $v)
1386 {
1387 $object->set($k, $v);
1388 }
1389
1390 $objects[] = $object;
1391 }
1392
1393 $result = new UpdateResult;
1394
1395 try
1396 {
1397 // before event
1398 if (!$ignoreEvents)
1399 {
1400 foreach ($objects as $object)
1401 {
1402 static::callOnBeforeUpdateEvent($object, $data, $result);
1403 }
1404 }
1405
1406 // collect array data
1407 $allFields = [];
1408 $allUfData = [];
1409
1410 foreach ($objects as $k => $object)
1411 {
1412 // actualize old-style fields array from object
1413 $allFields[$k] = $object->collectValues(Values::CURRENT, FieldTypeMask::SCALAR);
1414
1415 // uf values
1416 $allUfData[$k] = $object->collectValues(Values::CURRENT, FieldTypeMask::USERTYPE);
1417 }
1418
1419 // check data and uf
1420 foreach ($objects as $k => $object)
1421 {
1422 $fields = $allFields[$k];
1423 $ufdata = $allUfData[$k];
1424
1425 // check data
1426 static::checkFields($result, $object->primary, $fields);
1427
1428 // check uf data
1429 if (!empty($ufdata))
1430 {
1431 static::checkUfFields($object, $ufdata, $result);
1432 }
1433
1434 // check if there is still some data
1435 if (empty($fields) && empty($ufdata))
1436 {
1437 $result->addError(new EntityError("There is no data to add."));
1438 }
1439 }
1440
1441 // return if any error in any row
1442 if (!$result->isSuccess(true))
1443 {
1444 return $result;
1445 }
1446
1447 //event on adding
1448 if (!$ignoreEvents)
1449 {
1450 foreach ($objects as $k => $object)
1451 {
1452 $fields = $allFields[$k];
1453 $ufdata = $allUfData[$k];
1454
1455 static::callOnUpdateEvent($object, $fields, $ufdata);
1456 }
1457 }
1458
1459 // prepare sql
1460 $allSqlData = [];
1461
1462 foreach ($allFields as $k => $fields)
1463 {
1464 // use save modifiers
1465 $fieldsToDb = $fields;
1466
1467 foreach ($fieldsToDb as $fieldName => $value)
1468 {
1469 $field = $entity->getField($fieldName);
1470 $fieldsToDb[$fieldName] = $field->modifyValueBeforeSave($value, $fields);
1471 }
1472
1473 $dataReplacedColumn = static::replaceFieldName($fieldsToDb);
1474
1475 $allSqlData[$k] = $dataReplacedColumn;
1476 }
1477
1478 // check if rows data are equal
1479 $areEqual = true;
1480
1481 $dataSample = $allSqlData[0];
1482 asort($dataSample);
1483
1484 if (!empty($allSqlData[0]))
1485 {
1486
1487 foreach ($allSqlData as $data)
1488 {
1489 asort($data);
1490
1491 if ($data !== $dataSample)
1492 {
1493 $areEqual = false;
1494 break;
1495 }
1496 }
1497
1498 // save data
1499 $connection = $entity->getConnection();
1500 $helper = $connection->getSqlHelper();
1501 $tableName = $entity->getDBTableName();
1502
1503 // save data
1504 if ($areEqual)
1505 {
1506 // one query
1507 $update = $helper->prepareUpdate($tableName, $dataSample);
1508 $where = [];
1509 $isSinglePrimary = (count($entity->getPrimaryArray()) == 1);
1510
1511 foreach ($allSqlData as $k => $data)
1512 {
1513 $replacedPrimary = static::replaceFieldName($objects[$k]->primary);
1514
1515 if ($isSinglePrimary)
1516 {
1517 // for single primary IN is better
1518 $primaryName = key($replacedPrimary);
1519 $primaryValue = current($replacedPrimary);
1520 $tableField = $entity->getConnection()->getTableField($tableName, $primaryName);
1521
1522 $where[] = $helper->convertToDb($primaryValue, $tableField);
1523 }
1524 else
1525 {
1526 $id = [];
1527
1528 foreach ($replacedPrimary as $primaryName => $primaryValue)
1529 {
1530 $id[] = $helper->prepareAssignment($tableName, $primaryName, $primaryValue);
1531 }
1532 $where[] = implode(' AND ', $id);
1533 }
1534 }
1535
1536 if ($isSinglePrimary)
1537 {
1538 $where = $helper->quote($entity->getPrimary()).' IN ('.join(', ', $where).')';
1539 }
1540 else
1541 {
1542 $where = '('.join(') OR (', $where).')';
1543 }
1544
1545 $sql = "UPDATE ".$helper->quote($tableName)." SET ".$update[0]." WHERE ".$where;
1546 $connection->queryExecute($sql, $update[1]);
1547
1548 $result->setAffectedRowsCount($connection);
1549 }
1550 else
1551 {
1552 // query for each row
1553 foreach ($allSqlData as $k => $dataReplacedColumn)
1554 {
1555 $update = $helper->prepareUpdate($tableName, $dataReplacedColumn);
1556
1557 $replacedPrimary = static::replaceFieldName($objects[$k]->primary);
1558
1559 $id = [];
1560
1561 foreach ($replacedPrimary as $primaryName => $primaryValue)
1562 {
1563 $id[] = $helper->prepareAssignment($tableName, $primaryName, $primaryValue);
1564 }
1565 $where = implode(' AND ', $id);
1566
1567 $sql = "UPDATE ".$helper->quote($tableName)." SET ".$update[0]." WHERE ".$where;
1568 $connection->queryExecute($sql, $update[1]);
1569
1570 $result->setAffectedRowsCount($connection);
1571 }
1572 }
1573 }
1574
1575 // doesn't make sense for multiple rows
1576 $result->setData($dataSample);
1577
1578 if (count($allSqlData) == 1)
1579 {
1580 $result->setPrimary($objects[0]->primary);
1581 $result->setObject($objects[0]);
1582 }
1583
1584 // save uf data
1585 foreach ($allUfData as $ufdata)
1586 {
1587 if (!empty($ufdata))
1588 {
1589 global $USER_FIELD_MANAGER;
1590 $USER_FIELD_MANAGER->update($entity->getUfId(), end($primary), $ufdata);
1591 }
1592 }
1593
1594 static::cleanCache();
1595
1596 // event after update
1597 if (!$ignoreEvents)
1598 {
1599 foreach ($objects as $k => $object)
1600 {
1601 $fields = $allFields[$k] + $allUfData[$k];
1602
1603 static::callOnAfterUpdateEvent($object, $fields);
1604 }
1605 }
1606 }
1607 catch (\Exception $e)
1608 {
1609 // check result to avoid warning
1610 $result->isSuccess();
1611
1612 throw $e;
1613 }
1614
1615 return $result;
1616 }
1617
1627 public static function delete($primary)
1628 {
1629 global $USER_FIELD_MANAGER;
1630
1631 // check primary
1632 static::normalizePrimary($primary);
1633 static::validatePrimary($primary);
1634
1635 $entity = static::getEntity();
1636 $result = new DeleteResult();
1637
1638 $entityClass = static::getEntity()->getDataClass();
1639 $primaryAsString = EntityObject::sysSerializePrimary($primary, static::getEntity());
1640
1641 $object = !empty(static::$currentDeletingObjects[$entityClass][$primaryAsString])
1642 ? static::$currentDeletingObjects[$entityClass][$primaryAsString]
1643 : static::wakeUpObject($primary);
1644
1645 try
1646 {
1647 //event before delete
1648 static::callOnBeforeDeleteEvent($object, $entity, $result);
1649
1650 // return if any error
1651 if (!$result->isSuccess(true))
1652 {
1653 return $result;
1654 }
1655
1656 //event on delete
1657 static::callOnDeleteEvent($object, $entity);
1658
1659 // delete
1660 $connection = $entity->getConnection();
1661 $helper = $connection->getSqlHelper();
1662
1663 $tableName = $entity->getDBTableName();
1664
1665 $replacedPrimary = static::replaceFieldName($primary);
1666 $id = array();
1667 foreach ($replacedPrimary as $k => $v)
1668 {
1669 $id[] = $helper->prepareAssignment($tableName, $k, $v);
1670 }
1671 $where = implode(' AND ', $id);
1672
1673 $sql = "DELETE FROM ".$helper->quote($tableName)." WHERE ".$where;
1674 $connection->queryExecute($sql);
1675
1676 // delete uf data
1677 if ($entity->getUfId())
1678 {
1679 $USER_FIELD_MANAGER->delete($entity->getUfId(), end($primary));
1680 }
1681
1682 static::cleanCache();
1683
1684 //event after delete
1685 static::callOnAfterDeleteEvent($object, $entity);
1686 }
1687 catch (\Exception $e)
1688 {
1689 // check result to avoid warning
1690 $result->isSuccess();
1691
1692 throw $e;
1693 }
1694 finally
1695 {
1696 // clean temporary objects
1697 if (!empty(static::$currentDeletingObjects[$entityClass][$primaryAsString]))
1698 {
1699 unset(static::$currentDeletingObjects[$entityClass][$primaryAsString]);
1700 }
1701 }
1702
1703 return $result;
1704 }
1705
1711 protected static function callOnBeforeAddEvent($object, $fields, $result)
1712 {
1713 //event before adding
1714 $event = new Event($object->entity, self::EVENT_ON_BEFORE_ADD, [
1715 'fields' => $fields,
1716 'object' => $object
1717 ]);
1718
1719 $event->send();
1720 $event->getErrors($result);
1721 $event->mergeObjectFields($object);
1722
1723 //event before adding (modern with namespace)
1724 $event = new Event($object->entity, self::EVENT_ON_BEFORE_ADD, [
1725 'fields' => $fields,
1726 'object' => $object
1727 ], true);
1728
1729 $event->send();
1730 $event->getErrors($result);
1731 $event->mergeObjectFields($object);
1732 }
1733
1739 protected static function callOnAddEvent($object, $fields, $ufdata)
1740 {
1741 $event = new Event($object->entity, self::EVENT_ON_ADD, [
1742 'fields' => $fields + $ufdata,
1743 'object' => clone $object
1744 ]);
1745 $event->send();
1746
1747 //event on adding (modern with namespace)
1748 $event = new Event($object->entity, self::EVENT_ON_ADD, [
1749 'fields' => $fields + $ufdata,
1750 'object' => clone $object
1751 ], true);
1752 $event->send();
1753 }
1754
1760 protected static function callOnAfterAddEvent($object, $fields, $id)
1761 {
1762 //event after adding
1763 $event = new Event($object->entity, self::EVENT_ON_AFTER_ADD, [
1764 'id' => $id,
1765 'fields' => $fields,
1766 'object' => clone $object
1767 ]);
1768 $event->send();
1769
1770 //event after adding (modern with namespace)
1771 $event = new Event($object->entity, self::EVENT_ON_AFTER_ADD, [
1772 'id' => $id,
1773 'primary' => $object->primary,
1774 'fields' => $fields,
1775 'object' => clone $object
1776 ], true);
1777 $event->send();
1778 }
1779
1785 protected static function callOnBeforeUpdateEvent($object, $fields, $result)
1786 {
1787 $event = new Event($object->entity, self::EVENT_ON_BEFORE_UPDATE, [
1788 'id' => $object->primary,
1789 'fields' => $fields,
1790 'object' => $object
1791 ]);
1792
1793 $event->send();
1794 $event->getErrors($result);
1795 $event->mergeObjectFields($object);
1796
1797 //event before update (modern with namespace)
1798 $event = new Event($object->entity, self::EVENT_ON_BEFORE_UPDATE, [
1799 'id' => $object->primary,
1800 'primary' => $object->primary,
1801 'fields' => $fields,
1802 'object' => $object
1803 ], true);
1804
1805 $event->send();
1806 $event->getErrors($result);
1807 $event->mergeObjectFields($object);
1808 }
1809
1815 protected static function callOnUpdateEvent($object, $fields, $ufdata)
1816 {
1817 $event = new Event($object->entity, self::EVENT_ON_UPDATE, [
1818 'id' => $object->primary,
1819 'fields' => $fields + $ufdata,
1820 'object' => clone $object
1821 ]);
1822 $event->send();
1823
1824 //event on update (modern with namespace)
1825 $event = new Event($object->entity, self::EVENT_ON_UPDATE, [
1826 'id' => $object->primary,
1827 'primary' => $object->primary,
1828 'fields' => $fields + $ufdata,
1829 'object' => clone $object
1830 ], true);
1831 $event->send();
1832 }
1833
1838 protected static function callOnAfterUpdateEvent($object, $fields)
1839 {
1840 $event = new Event($object->entity, self::EVENT_ON_AFTER_UPDATE, [
1841 'id' => $object->primary,
1842 'fields' => $fields,
1843 'object' => clone $object
1844 ]);
1845 $event->send();
1846
1847 //event after update (modern with namespace)
1848 $event = new Event($object->entity, self::EVENT_ON_AFTER_UPDATE, [
1849 'id' => $object->primary,
1850 'primary' => $object->primary,
1851 'fields' => $fields,
1852 'object' => clone $object
1853 ], true);
1854 $event->send();
1855 }
1856
1862 protected static function callOnBeforeDeleteEvent($object, $entity, $result)
1863 {
1864 $event = new Event($entity, self::EVENT_ON_BEFORE_DELETE, array("id" => $object->primary));
1865 $event->send();
1866 $event->getErrors($result);
1867
1868 //event before delete (modern with namespace)
1869 $event = new Event($entity, self::EVENT_ON_BEFORE_DELETE, array("id" => $object->primary, "primary" => $object->primary, "object" => clone $object), true);
1870 $event->send();
1871 $event->getErrors($result);
1872 }
1873
1878 protected static function callOnDeleteEvent($object, $entity)
1879 {
1880 $event = new Event($entity, self::EVENT_ON_DELETE, array("id" => $object->primary));
1881 $event->send();
1882
1883 //event on delete (modern with namespace)
1884 $event = new Event($entity, self::EVENT_ON_DELETE, array("id" => $object->primary, "primary" => $object->primary, "object" => clone $object), true);
1885 $event->send();
1886 }
1887
1892 protected static function callOnAfterDeleteEvent($object, $entity)
1893 {
1894 $event = new Event($entity, self::EVENT_ON_AFTER_DELETE, array("id" => $object->primary));
1895 $event->send();
1896
1897 //event after delete (modern with namespace)
1898 $event = new Event($entity, self::EVENT_ON_AFTER_DELETE, array("id" => $object->primary, "primary" => $object->primary, "object" => clone $object), true);
1899 $event->send();
1900 }
1901
1909 public static function enableCrypto($field, $table = null, $mode = true)
1910 {
1911 if($table === null)
1912 {
1913 $table = static::getTableName();
1914 }
1915 $options = array();
1916 $optionString = Main\Config\Option::get("main", "~crypto_".$table);
1917 if($optionString <> '')
1918 {
1919 $options = unserialize($optionString, ['allowed_classes' => false]);
1920 }
1921 $options[strtoupper($field)] = $mode;
1922 Main\Config\Option::set("main", "~crypto_".$table, serialize($options));
1923 }
1924
1933 public static function cryptoEnabled($field, $table = null)
1934 {
1935 if($table === null)
1936 {
1937 $table = static::getTableName();
1938 }
1939 $optionString = Main\Config\Option::get("main", "~crypto_".$table);
1940 if($optionString <> '')
1941 {
1942 $field = strtoupper($field);
1943 $options = unserialize($optionString, ['allowed_classes' => false]);
1944 if(isset($options[$field]) && $options[$field] === true)
1945 {
1946 return true;
1947 }
1948 }
1949 return false;
1950 }
1951
1955 public static function setCurrentDeletingObject($object): void
1956 {
1957 $entityClass = static::getEntity()->getDataClass();
1958 self::$currentDeletingObjects[$entityClass][$object->primaryAsString] = $object;
1959 }
1960
1961 public static function cleanCache(): void
1962 {
1963 $entity = static::getEntity();
1964 $entity->cleanCache();
1965 }
1966
1967 /*
1968 An inheritor class can define the event handlers for own events.
1969 Why? To prevent from rewriting the add/update/delete functions.
1970 These handlers are triggered in the Bitrix\Main\ORM\Event::send() function
1971 */
1972 public static function onBeforeAdd(Event $event){}
1973 public static function onAdd(Event $event){}
1974 public static function onAfterAdd(Event $event){}
1975 public static function onBeforeUpdate(Event $event){}
1976 public static function onUpdate(Event $event){}
1977 public static function onAfterUpdate(Event $event){}
1978 public static function onBeforeDelete(Event $event){}
1979 public static function onDelete(Event $event){}
1980 public static function onAfterDelete(Event $event){}
1981}
static loadMessages($file)
Definition: loc.php:64
static getMessage($code, $replace=null, $language=null)
Definition: loc.php:29
static callOnBeforeUpdateEvent($object, $fields, $result)
static getObjectClassByDataClass($dataClass)
static getCollectionClassByDataClass($dataClass)
static callOnAfterUpdateEvent($object, $fields)
static callOnAddEvent($object, $fields, $ufdata)
static onAfterUpdate(Event $event)
static onDelete(Event $event)
static callOnBeforeDeleteEvent($object, $entity, $result)
static checkUfFields($object, $ufdata, $result)
static setCurrentDeletingObject($object)
static getRow(array $parameters)
static getList(array $parameters=array())
static onAfterAdd(Event $event)
static onBeforeAdd(Event $event)
static callOnDeleteEvent($object, $entity)
static addMulti($rows, $ignoreEvents=false)
static callOnBeforeAddEvent($object, $fields, $result)
static createObject($setDefaultValues=true)
static getCount($filter=array(), array $cache=array())
static cryptoEnabled($field, $table=null)
static replaceFieldName($data=array())
static checkFields(Result $result, $primary, array $data)
static enableCrypto($field, $table=null, $mode=true)
static onAfterDelete(Event $event)
static callOnUpdateEvent($object, $fields, $ufdata)
static postInitialize(Entity $entity)
static callOnAfterAddEvent($object, $fields, $id)
static onBeforeDelete(Event $event)
static onBeforeUpdate(Event $event)
static getByPrimary($primary, array $parameters=array())
static onUpdate(Event $event)
static callOnAfterDeleteEvent($object, $entity)
static update($primary, array $data)
static encode($data, $options=null)
Definition: json.php:24