Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
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) && array_key_exists($fieldName, $data) && $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 if ($field->isPrimary() && $field->isAutocomplete() && is_null($value))
928 {
929 unset($fieldsToDb[$fieldName]); // postgresql compatibility
930 continue;
931 }
932 $fieldsToDb[$fieldName] = $field->modifyValueBeforeSave($value, $fields);
933 }
934
935 // save data
936 $connection = $entity->getConnection();
937
938 $tableName = $entity->getDBTableName();
939 $identity = $entity->getAutoIncrement();
940
941 $dataReplacedColumn = static::replaceFieldName($fieldsToDb);
942 $id = $connection->add($tableName, $dataReplacedColumn, $identity);
943
944 // build standard primary
945 $primary = null;
946 $isGuessedPrimary = false;
947
948 if (!empty($id))
949 {
950 if($entity->getAutoIncrement() <> '')
951 {
952 $primary = array($entity->getAutoIncrement() => $id);
953 static::normalizePrimary($primary);
954 }
955 else
956 {
957 // for those who did not set 'autocomplete' flag but wants to get id from result
958 $primary = array('ID' => $id);
959 $isGuessedPrimary = true;
960 }
961 }
962 else
963 {
964 static::normalizePrimary($primary, $fields);
965 }
966
967 // fill result
968 $result->setPrimary($primary);
969 $result->setData($fields + $ufdata);
970 $result->setObject($object);
971
972 if (!$isGuessedPrimary)
973 {
974 foreach ($primary as $primaryName => $primaryValue)
975 {
976 $object->sysSetActual($primaryName, $primaryValue);
977 }
978 }
979
980 // save uf data
981 if (!empty($ufdata))
982 {
983 $ufUserId = false;
984
985 if ($object->authContext)
986 {
987 $ufUserId = $object->authContext->getUserId();
988 }
989
990 $USER_FIELD_MANAGER->update($entity->getUfId(), end($primary), $ufdata, $ufUserId);
991 }
992
993 static::cleanCache();
994
995 static::callOnAfterAddEvent($object, $fields + $ufdata, $id);
996 }
997 catch (\Exception $e)
998 {
999 // check result to avoid warning
1000 $result->isSuccess();
1001
1002 throw $e;
1003 }
1004
1005 return $result;
1006 }
1007
1016 public static function addMulti($rows, $ignoreEvents = false)
1017 {
1018 global $USER_FIELD_MANAGER;
1019
1020 $rows = array_values($rows);
1021 $forceSeparateQueries = false;
1022
1023 if (!$ignoreEvents && count($rows) > 1 && strlen(static::getEntity()->getAutoIncrement()))
1024 {
1025 $forceSeparateQueries = true;
1026
1027 // change to warning
1028 trigger_error(
1029 'Multi-insert doesn\'t work with events as far as we can not get last inserted IDs that we need for the events. '.
1030 'Insert query was forced to multiple separate queries.',
1031 E_USER_NOTICE
1032 );
1033 }
1034
1035 // prepare objects
1036 $objects = [];
1037
1038 foreach ($rows as $k => &$row)
1039 {
1040 $objects[$k] = static::convertArrayToObject($row, true);
1041 }
1042
1043 $entity = static::getEntity();
1044 $result = new AddResult();
1045
1046 try
1047 {
1048 // call onBeforeEvent
1049 if (!$ignoreEvents)
1050 {
1051 foreach ($objects as $k => $object)
1052 {
1053 static::callOnBeforeAddEvent($object, $rows[$k], $result);
1054 }
1055 }
1056
1057 // collect array data
1058 $allFields = [];
1059 $allUfData = [];
1060
1061 foreach ($objects as $k => $object)
1062 {
1063 // actualize old-style fields array from object
1064 $allFields[$k] = $object->collectValues(Values::CURRENT, FieldTypeMask::SCALAR);
1065
1066 // uf values
1067 $allUfData[$k] = $object->collectValues(Values::CURRENT, FieldTypeMask::USERTYPE);
1068 }
1069
1070 // check data and uf
1071 foreach ($objects as $k => $object)
1072 {
1073 $fields = $allFields[$k];
1074 $ufdata = $allUfData[$k];
1075
1076 // check data
1077 static::checkFields($result, null, $fields);
1078
1079 // check uf data
1080 if (!empty($ufdata))
1081 {
1082 static::checkUfFields($object, $ufdata, $result);
1083 }
1084
1085 // check if there is still some data
1086 if (empty($fields) && empty($ufdata))
1087 {
1088 $result->addError(new EntityError("There is no data to add."));
1089 }
1090 }
1091
1092 // return if any error in any row
1093 if (!$result->isSuccess(true))
1094 {
1095 return $result;
1096 }
1097
1098 //event on adding
1099 if (!$ignoreEvents)
1100 {
1101 foreach ($objects as $k => $object)
1102 {
1103 $fields = $allFields[$k];
1104 $ufdata = $allUfData[$k];
1105
1106 self::callOnAddEvent($object, $fields, $ufdata);
1107 }
1108 }
1109
1110 // prepare sql
1111 $allSqlData = [];
1112
1113 foreach ($allFields as $k => $fields)
1114 {
1115 // use save modifiers
1116 $fieldsToDb = $fields;
1117
1118 foreach ($fieldsToDb as $fieldName => $value)
1119 {
1120 $field = $entity->getField($fieldName);
1121 $fieldsToDb[$fieldName] = $field->modifyValueBeforeSave($value, $fields);
1122 }
1123
1124 $dataReplacedColumn = static::replaceFieldName($fieldsToDb);
1125
1126 $allSqlData[$k] = $dataReplacedColumn;
1127 }
1128
1129 // save data
1130 $connection = $entity->getConnection();
1131
1132 $tableName = $entity->getDBTableName();
1133 $identity = $entity->getAutoIncrement();
1134 $ids = [];
1135
1136 // multi insert on db level
1137 if ($forceSeparateQueries)
1138 {
1139 foreach ($allSqlData as $k => $sqlData)
1140 {
1141 // remember all ids
1142 $ids[$k] = $connection->add($tableName, $sqlData, $identity);
1143 }
1144 }
1145 else
1146 {
1147 $id = $connection->addMulti($tableName, $allSqlData, $identity);
1148 }
1149
1150 if (count($allSqlData) > 1)
1151 {
1152 // id doesn't make sense when multiple inserts
1153 $id = null;
1154 }
1155 else
1156 {
1157 $object = $objects[0];
1158 $fields = $allFields[0];
1159
1160 // build standard primary
1161 $primary = null;
1162
1163 if (!empty($id))
1164 {
1165 if($entity->getAutoIncrement() <> '')
1166 {
1167 $primary = array($entity->getAutoIncrement() => $id);
1168 static::normalizePrimary($primary);
1169 }
1170 else
1171 {
1172 // for those who did not set 'autocomplete' flag but want to get id from result
1173 $primary = array('ID' => $id);
1174 }
1175 }
1176 else
1177 {
1178 static::normalizePrimary($primary, $fields);
1179 }
1180
1181 // fill result
1182 $result->setPrimary($primary);
1183 $result->setData($fields);
1184 $result->setObject($object);
1185 }
1186
1187 // save uf data
1188 foreach ($allUfData as $k => $ufdata)
1189 {
1190 if (!empty($ufdata))
1191 {
1192 $ufUserId = false;
1193
1194 if ($objects[$k]->authContext)
1195 {
1196 $ufUserId = $objects[$k]->authContext->getUserId();
1197 }
1198
1199 $USER_FIELD_MANAGER->update($entity->getUfId(), end($primary), $ufdata, $ufUserId);
1200 }
1201 }
1202
1203 static::cleanCache();
1204
1205 // after event
1206 if (!$ignoreEvents)
1207 {
1208 foreach ($objects as $k => $object)
1209 {
1210 $fields = $allFields[$k] + $allUfData[$k];
1211 $id = $forceSeparateQueries ? $ids[$k] : null;
1212
1213 static::callOnAfterAddEvent($object, $fields, $id);
1214 }
1215 }
1216 }
1217 catch (\Exception $e)
1218 {
1219 // check result to avoid warning
1220 $result->isSuccess();
1221
1222 throw $e;
1223 }
1224
1225 return $result;
1226 }
1227
1246 public static function update($primary, array $data)
1247 {
1248 global $USER_FIELD_MANAGER;
1249
1250 // check primary
1251 static::normalizePrimary(
1252 $primary, isset($data["fields"]) && is_array($data["fields"]) ? $data["fields"] : $data
1253 );
1254 static::validatePrimary($primary);
1255
1256 // compatibility
1257 $fields = $data;
1258
1259 // prepare entity object for compatibility with new code
1260 $object = static::convertArrayToObject($fields, false, $primary);
1261
1262 $entity = static::getEntity();
1263 $result = new UpdateResult();
1264
1265 try
1266 {
1267 static::callOnBeforeUpdateEvent($object, $fields, $result);
1268
1269 // actualize old-style fields array from object
1270 $fields = $object->collectValues(Values::CURRENT, FieldTypeMask::SCALAR);
1271
1272 // uf values
1273 $ufdata = $object->collectValues(Values::CURRENT, FieldTypeMask::USERTYPE);
1274
1275 // check data
1276 static::checkFields($result, $primary, $fields);
1277
1278 // check uf data
1279 if (!empty($ufdata))
1280 {
1281 static::checkUfFields($object, $ufdata, $result);
1282 }
1283
1284 // check if there is still some data
1285 if (empty($fields) && empty($ufdata))
1286 {
1287 return $result;
1288 }
1289
1290 // return if any error
1291 if (!$result->isSuccess(true))
1292 {
1293 return $result;
1294 }
1295
1296 static::callOnUpdateEvent($object, $fields, $ufdata);
1297
1298 // use save modifiers
1299 $fieldsToDb = $fields;
1300
1301 foreach ($fieldsToDb as $fieldName => $value)
1302 {
1303 $field = $entity->getField($fieldName);
1304 $fieldsToDb[$fieldName] = $field->modifyValueBeforeSave($value, $fields);
1305 }
1306
1307 // save data
1308 if (!empty($fieldsToDb))
1309 {
1310 $connection = $entity->getConnection();
1311 $helper = $connection->getSqlHelper();
1312
1313 $tableName = $entity->getDBTableName();
1314
1315 $dataReplacedColumn = static::replaceFieldName($fieldsToDb);
1316 $update = $helper->prepareUpdate($tableName, $dataReplacedColumn);
1317
1318 $replacedPrimary = static::replaceFieldName($primary);
1319 $id = array();
1320 foreach ($replacedPrimary as $k => $v)
1321 {
1322 $id[] = $helper->prepareAssignment($tableName, $k, $v);
1323 }
1324 $where = implode(' AND ', $id);
1325
1326 $sql = "UPDATE ".$helper->quote($tableName)." SET ".$update[0]." WHERE ".$where;
1327 $connection->queryExecute($sql, $update[1]);
1328
1329 $result->setAffectedRowsCount($connection);
1330 }
1331
1332 $result->setData($fields + $ufdata);
1333 $result->setPrimary($primary);
1334 $result->setObject($object);
1335
1336 // save uf data
1337 if (!empty($ufdata))
1338 {
1339 $ufUserId = false;
1340
1341 if ($object->authContext)
1342 {
1343 $ufUserId = $object->authContext->getUserId();
1344 }
1345
1346 $USER_FIELD_MANAGER->update($entity->getUfId(), end($primary), $ufdata, $ufUserId);
1347 }
1348
1349 static::cleanCache();
1350
1351 // event after update
1352 static::callOnAfterUpdateEvent($object, $fields + $ufdata);
1353 }
1354 catch (\Exception $e)
1355 {
1356 // check result to avoid warning
1357 $result->isSuccess();
1358
1359 throw $e;
1360 }
1361
1362 return $result;
1363 }
1364
1374 public static function updateMulti($primaries, $data, $ignoreEvents = false)
1375 {
1376 $entity = static::getEntity();
1377 $primaries = array_values($primaries);
1378
1380 $objects = [];
1381
1382 foreach ($primaries as &$primary)
1383 {
1384 static::normalizePrimary($primary, $data);
1385 static::validatePrimary($primary);
1386
1388 $object = $entity->wakeUpObject($primary);
1389
1390 foreach ($data as $k => $v)
1391 {
1392 $object->set($k, $v);
1393 }
1394
1395 $objects[] = $object;
1396 }
1397
1398 $result = new UpdateResult;
1399
1400 try
1401 {
1402 // before event
1403 if (!$ignoreEvents)
1404 {
1405 foreach ($objects as $object)
1406 {
1407 static::callOnBeforeUpdateEvent($object, $data, $result);
1408 }
1409 }
1410
1411 // collect array data
1412 $allFields = [];
1413 $allUfData = [];
1414
1415 foreach ($objects as $k => $object)
1416 {
1417 // actualize old-style fields array from object
1418 $allFields[$k] = $object->collectValues(Values::CURRENT, FieldTypeMask::SCALAR);
1419
1420 // uf values
1421 $allUfData[$k] = $object->collectValues(Values::CURRENT, FieldTypeMask::USERTYPE);
1422 }
1423
1424 // check data and uf
1425 foreach ($objects as $k => $object)
1426 {
1427 $fields = $allFields[$k];
1428 $ufdata = $allUfData[$k];
1429
1430 // check data
1431 static::checkFields($result, $object->primary, $fields);
1432
1433 // check uf data
1434 if (!empty($ufdata))
1435 {
1436 static::checkUfFields($object, $ufdata, $result);
1437 }
1438
1439 // check if there is still some data
1440 if (empty($fields) && empty($ufdata))
1441 {
1442 $result->addError(new EntityError("There is no data to add."));
1443 }
1444 }
1445
1446 // return if any error in any row
1447 if (!$result->isSuccess(true))
1448 {
1449 return $result;
1450 }
1451
1452 //event on adding
1453 if (!$ignoreEvents)
1454 {
1455 foreach ($objects as $k => $object)
1456 {
1457 $fields = $allFields[$k];
1458 $ufdata = $allUfData[$k];
1459
1460 static::callOnUpdateEvent($object, $fields, $ufdata);
1461 }
1462 }
1463
1464 // prepare sql
1465 $allSqlData = [];
1466
1467 foreach ($allFields as $k => $fields)
1468 {
1469 // use save modifiers
1470 $fieldsToDb = $fields;
1471
1472 foreach ($fieldsToDb as $fieldName => $value)
1473 {
1474 $field = $entity->getField($fieldName);
1475 $fieldsToDb[$fieldName] = $field->modifyValueBeforeSave($value, $fields);
1476 }
1477
1478 $dataReplacedColumn = static::replaceFieldName($fieldsToDb);
1479
1480 $allSqlData[$k] = $dataReplacedColumn;
1481 }
1482
1483 // check if rows data are equal
1484 $areEqual = true;
1485
1486 $dataSample = $allSqlData[0];
1487 asort($dataSample);
1488
1489 if (!empty($allSqlData[0]))
1490 {
1491
1492 foreach ($allSqlData as $data)
1493 {
1494 asort($data);
1495
1496 if ($data !== $dataSample)
1497 {
1498 $areEqual = false;
1499 break;
1500 }
1501 }
1502
1503 // save data
1504 $connection = $entity->getConnection();
1505 $helper = $connection->getSqlHelper();
1506 $tableName = $entity->getDBTableName();
1507
1508 // save data
1509 if ($areEqual)
1510 {
1511 // one query
1512 $update = $helper->prepareUpdate($tableName, $dataSample);
1513 $where = [];
1514 $isSinglePrimary = (count($entity->getPrimaryArray()) == 1);
1515
1516 foreach ($allSqlData as $k => $data)
1517 {
1518 $replacedPrimary = static::replaceFieldName($objects[$k]->primary);
1519
1520 if ($isSinglePrimary)
1521 {
1522 // for single primary IN is better
1523 $primaryName = key($replacedPrimary);
1524 $primaryValue = current($replacedPrimary);
1525 $tableField = $entity->getConnection()->getTableField($tableName, $primaryName);
1526
1527 $where[] = $helper->convertToDb($primaryValue, $tableField);
1528 }
1529 else
1530 {
1531 $id = [];
1532
1533 foreach ($replacedPrimary as $primaryName => $primaryValue)
1534 {
1535 $id[] = $helper->prepareAssignment($tableName, $primaryName, $primaryValue);
1536 }
1537 $where[] = implode(' AND ', $id);
1538 }
1539 }
1540
1541 if ($isSinglePrimary)
1542 {
1543 $where = $helper->quote($entity->getPrimary()).' IN ('.join(', ', $where).')';
1544 }
1545 else
1546 {
1547 $where = '('.join(') OR (', $where).')';
1548 }
1549
1550 $sql = "UPDATE ".$helper->quote($tableName)." SET ".$update[0]." WHERE ".$where;
1551 $connection->queryExecute($sql, $update[1]);
1552
1553 $result->setAffectedRowsCount($connection);
1554 }
1555 else
1556 {
1557 // query for each row
1558 foreach ($allSqlData as $k => $dataReplacedColumn)
1559 {
1560 $update = $helper->prepareUpdate($tableName, $dataReplacedColumn);
1561
1562 $replacedPrimary = static::replaceFieldName($objects[$k]->primary);
1563
1564 $id = [];
1565
1566 foreach ($replacedPrimary as $primaryName => $primaryValue)
1567 {
1568 $id[] = $helper->prepareAssignment($tableName, $primaryName, $primaryValue);
1569 }
1570 $where = implode(' AND ', $id);
1571
1572 $sql = "UPDATE ".$helper->quote($tableName)." SET ".$update[0]." WHERE ".$where;
1573 $connection->queryExecute($sql, $update[1]);
1574
1575 $result->setAffectedRowsCount($connection);
1576 }
1577 }
1578 }
1579
1580 // doesn't make sense for multiple rows
1581 $result->setData($dataSample);
1582
1583 if (count($allSqlData) == 1)
1584 {
1585 $result->setPrimary($objects[0]->primary);
1586 $result->setObject($objects[0]);
1587 }
1588
1589 // save uf data
1590 foreach ($allUfData as $ufdata)
1591 {
1592 if (!empty($ufdata))
1593 {
1594 global $USER_FIELD_MANAGER;
1595 $USER_FIELD_MANAGER->update($entity->getUfId(), end($primary), $ufdata);
1596 }
1597 }
1598
1599 static::cleanCache();
1600
1601 // event after update
1602 if (!$ignoreEvents)
1603 {
1604 foreach ($objects as $k => $object)
1605 {
1606 $fields = $allFields[$k] + $allUfData[$k];
1607
1608 static::callOnAfterUpdateEvent($object, $fields);
1609 }
1610 }
1611 }
1612 catch (\Exception $e)
1613 {
1614 // check result to avoid warning
1615 $result->isSuccess();
1616
1617 throw $e;
1618 }
1619
1620 return $result;
1621 }
1622
1632 public static function delete($primary)
1633 {
1634 global $USER_FIELD_MANAGER;
1635
1636 // check primary
1637 static::normalizePrimary($primary);
1638 static::validatePrimary($primary);
1639
1640 $entity = static::getEntity();
1641 $result = new DeleteResult();
1642
1643 $entityClass = static::getEntity()->getDataClass();
1644 $primaryAsString = EntityObject::sysSerializePrimary($primary, static::getEntity());
1645
1646 $object = !empty(static::$currentDeletingObjects[$entityClass][$primaryAsString])
1647 ? static::$currentDeletingObjects[$entityClass][$primaryAsString]
1648 : static::wakeUpObject($primary);
1649
1650 try
1651 {
1652 //event before delete
1653 static::callOnBeforeDeleteEvent($object, $entity, $result);
1654
1655 // return if any error
1656 if (!$result->isSuccess(true))
1657 {
1658 return $result;
1659 }
1660
1661 //event on delete
1662 static::callOnDeleteEvent($object, $entity);
1663
1664 // delete
1665 $connection = $entity->getConnection();
1666 $helper = $connection->getSqlHelper();
1667
1668 $tableName = $entity->getDBTableName();
1669
1670 $replacedPrimary = static::replaceFieldName($primary);
1671 $id = array();
1672 foreach ($replacedPrimary as $k => $v)
1673 {
1674 $id[] = $helper->prepareAssignment($tableName, $k, $v);
1675 }
1676 $where = implode(' AND ', $id);
1677
1678 $sql = "DELETE FROM ".$helper->quote($tableName)." WHERE ".$where;
1679 $connection->queryExecute($sql);
1680
1681 // delete uf data
1682 if ($entity->getUfId())
1683 {
1684 $USER_FIELD_MANAGER->delete($entity->getUfId(), end($primary));
1685 }
1686
1687 static::cleanCache();
1688
1689 //event after delete
1690 static::callOnAfterDeleteEvent($object, $entity);
1691 }
1692 catch (\Exception $e)
1693 {
1694 // check result to avoid warning
1695 $result->isSuccess();
1696
1697 throw $e;
1698 }
1699 finally
1700 {
1701 // clean temporary objects
1702 if (!empty(static::$currentDeletingObjects[$entityClass][$primaryAsString]))
1703 {
1704 unset(static::$currentDeletingObjects[$entityClass][$primaryAsString]);
1705 }
1706 }
1707
1708 return $result;
1709 }
1710
1716 protected static function callOnBeforeAddEvent($object, $fields, $result)
1717 {
1718 //event before adding
1719 $event = new Event($object->entity, self::EVENT_ON_BEFORE_ADD, [
1720 'fields' => $fields,
1721 'object' => $object
1722 ]);
1723
1724 $event->send();
1725 $event->getErrors($result);
1726 $event->mergeObjectFields($object);
1727
1728 //event before adding (modern with namespace)
1729 $event = new Event($object->entity, self::EVENT_ON_BEFORE_ADD, [
1730 'fields' => $fields,
1731 'object' => $object
1732 ], true);
1733
1734 $event->send();
1735 $event->getErrors($result);
1736 $event->mergeObjectFields($object);
1737 }
1738
1744 protected static function callOnAddEvent($object, $fields, $ufdata)
1745 {
1746 $event = new Event($object->entity, self::EVENT_ON_ADD, [
1747 'fields' => $fields + $ufdata,
1748 'object' => clone $object
1749 ]);
1750 $event->send();
1751
1752 //event on adding (modern with namespace)
1753 $event = new Event($object->entity, self::EVENT_ON_ADD, [
1754 'fields' => $fields + $ufdata,
1755 'object' => clone $object
1756 ], true);
1757 $event->send();
1758 }
1759
1765 protected static function callOnAfterAddEvent($object, $fields, $id)
1766 {
1767 //event after adding
1768 $event = new Event($object->entity, self::EVENT_ON_AFTER_ADD, [
1769 'id' => $id,
1770 'fields' => $fields,
1771 'object' => clone $object
1772 ]);
1773 $event->send();
1774
1775 //event after adding (modern with namespace)
1776 $event = new Event($object->entity, self::EVENT_ON_AFTER_ADD, [
1777 'id' => $id,
1778 'primary' => $object->primary,
1779 'fields' => $fields,
1780 'object' => clone $object
1781 ], true);
1782 $event->send();
1783 }
1784
1790 protected static function callOnBeforeUpdateEvent($object, $fields, $result)
1791 {
1792 $event = new Event($object->entity, self::EVENT_ON_BEFORE_UPDATE, [
1793 'id' => $object->primary,
1794 'fields' => $fields,
1795 'object' => $object
1796 ]);
1797
1798 $event->send();
1799 $event->getErrors($result);
1800 $event->mergeObjectFields($object);
1801
1802 //event before update (modern with namespace)
1803 $event = new Event($object->entity, self::EVENT_ON_BEFORE_UPDATE, [
1804 'id' => $object->primary,
1805 'primary' => $object->primary,
1806 'fields' => $fields,
1807 'object' => $object
1808 ], true);
1809
1810 $event->send();
1811 $event->getErrors($result);
1812 $event->mergeObjectFields($object);
1813 }
1814
1820 protected static function callOnUpdateEvent($object, $fields, $ufdata)
1821 {
1822 $event = new Event($object->entity, self::EVENT_ON_UPDATE, [
1823 'id' => $object->primary,
1824 'fields' => $fields + $ufdata,
1825 'object' => clone $object
1826 ]);
1827 $event->send();
1828
1829 //event on update (modern with namespace)
1830 $event = new Event($object->entity, self::EVENT_ON_UPDATE, [
1831 'id' => $object->primary,
1832 'primary' => $object->primary,
1833 'fields' => $fields + $ufdata,
1834 'object' => clone $object
1835 ], true);
1836 $event->send();
1837 }
1838
1843 protected static function callOnAfterUpdateEvent($object, $fields)
1844 {
1845 $event = new Event($object->entity, self::EVENT_ON_AFTER_UPDATE, [
1846 'id' => $object->primary,
1847 'fields' => $fields,
1848 'object' => clone $object
1849 ]);
1850 $event->send();
1851
1852 //event after update (modern with namespace)
1853 $event = new Event($object->entity, self::EVENT_ON_AFTER_UPDATE, [
1854 'id' => $object->primary,
1855 'primary' => $object->primary,
1856 'fields' => $fields,
1857 'object' => clone $object
1858 ], true);
1859 $event->send();
1860 }
1861
1867 protected static function callOnBeforeDeleteEvent($object, $entity, $result)
1868 {
1869 $event = new Event($entity, self::EVENT_ON_BEFORE_DELETE, array("id" => $object->primary));
1870 $event->send();
1871 $event->getErrors($result);
1872
1873 //event before delete (modern with namespace)
1874 $event = new Event($entity, self::EVENT_ON_BEFORE_DELETE, array("id" => $object->primary, "primary" => $object->primary, "object" => clone $object), true);
1875 $event->send();
1876 $event->getErrors($result);
1877 }
1878
1883 protected static function callOnDeleteEvent($object, $entity)
1884 {
1885 $event = new Event($entity, self::EVENT_ON_DELETE, array("id" => $object->primary));
1886 $event->send();
1887
1888 //event on delete (modern with namespace)
1889 $event = new Event($entity, self::EVENT_ON_DELETE, array("id" => $object->primary, "primary" => $object->primary, "object" => clone $object), true);
1890 $event->send();
1891 }
1892
1897 protected static function callOnAfterDeleteEvent($object, $entity)
1898 {
1899 $event = new Event($entity, self::EVENT_ON_AFTER_DELETE, array("id" => $object->primary));
1900 $event->send();
1901
1902 //event after delete (modern with namespace)
1903 $event = new Event($entity, self::EVENT_ON_AFTER_DELETE, array("id" => $object->primary, "primary" => $object->primary, "object" => clone $object), true);
1904 $event->send();
1905 }
1906
1914 public static function enableCrypto($field, $table = null, $mode = true)
1915 {
1916 if($table === null)
1917 {
1918 $table = static::getTableName();
1919 }
1920 $options = array();
1921 $optionString = Main\Config\Option::get("main", "~crypto_".$table);
1922 if($optionString <> '')
1923 {
1924 $options = unserialize($optionString, ['allowed_classes' => false]);
1925 }
1926 $options[strtoupper($field)] = $mode;
1927 Main\Config\Option::set("main", "~crypto_".$table, serialize($options));
1928 }
1929
1938 public static function cryptoEnabled($field, $table = null)
1939 {
1940 if($table === null)
1941 {
1942 $table = static::getTableName();
1943 }
1944 $optionString = Main\Config\Option::get("main", "~crypto_".$table);
1945 if($optionString <> '')
1946 {
1947 $field = strtoupper($field);
1948 $options = unserialize($optionString, ['allowed_classes' => false]);
1949 if(isset($options[$field]) && $options[$field] === true)
1950 {
1951 return true;
1952 }
1953 }
1954 return false;
1955 }
1956
1960 public static function setCurrentDeletingObject($object): void
1961 {
1962 $entityClass = static::getEntity()->getDataClass();
1963 self::$currentDeletingObjects[$entityClass][$object->primaryAsString] = $object;
1964 }
1965
1966 public static function cleanCache(): void
1967 {
1968 $entity = static::getEntity();
1969 $entity->cleanCache();
1970 }
1971
1972 /*
1973 An inheritor class can define the event handlers for own events.
1974 Why? To prevent from rewriting the add/update/delete functions.
1975 These handlers are triggered in the Bitrix\Main\ORM\Event::send() function
1976 */
1977 public static function onBeforeAdd(Event $event){}
1978 public static function onAdd(Event $event){}
1979 public static function onAfterAdd(Event $event){}
1980 public static function onBeforeUpdate(Event $event){}
1981 public static function onUpdate(Event $event){}
1982 public static function onAfterUpdate(Event $event){}
1983 public static function onBeforeDelete(Event $event){}
1984 public static function onDelete(Event $event){}
1985 public static function onAfterDelete(Event $event){}
1986}
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 callOnBeforeDeleteEvent($object, $entity, $result)
static checkUfFields($object, $ufdata, $result)
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 callOnAfterDeleteEvent($object, $entity)
static update($primary, array $data)
addError(Error $error)
Definition result.php:50
static encode($data, $options=null)
Definition json.php:24