76 public static function get($entityName)
78 return static::getInstance($entityName);
88 public static function has($entityName)
90 $entityClass = static::normalizeEntityClass($entityName);
91 return class_exists($entityClass);
105 $entityName = static::normalizeEntityClass($entityName);
107 return self::getInstanceDirect($entityName);
117 protected static function getInstanceDirect($className)
119 if (empty(self::$instances[$className]))
122 $entityClass = $className::getEntityClass();
125 if (empty(self::$instances[$className]))
127 $entity =
new $entityClass;
128 $entity->initialize($className);
129 $entity->postInitialize();
132 $className::postInitialize($entity);
134 self::$instances[$className] = $entity;
138 return self::$instances[$className];
153 if ($fieldInfo instanceof
Field)
158 if (!empty($fieldName) && !is_numeric($fieldName))
160 $field->setName($fieldName);
163 elseif (is_array($fieldInfo))
165 if (!empty($fieldInfo[
'reference']))
167 if (is_string($fieldInfo[
'data_type']) && strpos($fieldInfo[
'data_type'],
'\\') ===
false)
170 $fieldInfo[
'data_type'] = $this->
getNamespace().$fieldInfo[
'data_type'];
174 $field =
new Reference($fieldName, $fieldInfo[
'data_type'], $fieldInfo[
'reference'], $fieldInfo);
176 elseif (!empty($fieldInfo[
'expression']))
178 $expression = array_shift($fieldInfo[
'expression']);
179 $buildFrom = $fieldInfo[
'expression'];
181 $field =
new ExpressionField($fieldName, $expression, $buildFrom, $fieldInfo);
183 elseif (!empty($fieldInfo[
'USER_TYPE_ID']))
185 $field =
new UField($fieldInfo);
189 $fieldClass = StringHelper::snake2camel($fieldInfo[
'data_type']) .
'Field';
190 $fieldClass =
'\\Bitrix\\Main\\Entity\\'.$fieldClass;
192 if (strlen($fieldInfo[
'data_type']) && class_exists($fieldClass))
194 $field =
new $fieldClass($fieldName, $fieldInfo);
196 elseif (strlen($fieldInfo[
'data_type']) && class_exists($fieldInfo[
'data_type']))
198 $fieldClass = $fieldInfo[
'data_type'];
199 $field =
new $fieldClass($fieldName, $fieldInfo);
204 'Unknown data type "%s" found for `%s` field in %s Entity.',
205 $fieldInfo[
'data_type'], $fieldName, $this->
getName()
213 is_object($fieldInfo) ? get_class($fieldInfo) : gettype($fieldInfo)
217 $field->setEntity($this);
218 $field->postInitialize();
223 public function initialize($className)
226 $this->className = $className;
229 $this->connectionName = $className::getConnectionName();
230 $this->dbTableName = $className::getTableName();
231 $this->fieldsMap = $className::getMap();
232 $this->uf_id = $className::getUfId();
233 $this->isUts = $className::isUts();
234 $this->isUtm = $className::isUtm();
252 $this->className = static::normalizeEntityClass($className);
254 $classPath = explode(
'\\', ltrim($this->className,
'\\'));
255 $this->name = substr(end($classPath), 0, -5);
265 $classPath = explode(
'\\', ltrim($this->className,
'\\'));
266 $this->name = substr(end($classPath), 0, -5);
269 if (is_null($this->dbTableName))
271 $_classPath = array_slice($classPath, 0, -1);
273 $this->dbTableName =
'b_';
275 foreach ($_classPath as $i => $_pathElem)
277 if ($i == 0 && $_pathElem ==
'Bitrix')
283 if ($i == 1 && $_pathElem ==
'Main')
289 $this->dbTableName .= strtolower($_pathElem).
'_';
293 if ($this->name !== end($_classPath))
295 $this->dbTableName .= StringHelper::camel2snake($this->name);
299 $this->dbTableName = substr($this->dbTableName, 0, -1);
303 $this->primary = array();
304 $this->references = array();
307 foreach ($this->fieldsMap as $fieldName => &$fieldInfo)
309 $this->
addField($fieldInfo, $fieldName);
312 if (!empty($this->fieldsMap) && empty($this->primary))
318 if (empty($this->uf_id))
321 $userTypeManager = Main\Application::getUserTypeManager();
322 if($userTypeManager instanceof \CUserTypeManager)
324 $entityList = $userTypeManager->getEntityList();
325 $ufId = is_array($entityList) ? array_search($this->className, $entityList) :
false;
328 $this->uf_id = $ufId;
333 if (!empty($this->uf_id))
336 Main\UserFieldTable::attachFields($this, $this->uf_id);
339 static::$ufIdIndex[$this->uf_id] = $this->className;
350 $dataManagerClass = $this->className;
351 return static::normalizeName($dataManagerClass::getObjectClass());
361 $dataManagerClass = $this->className;
362 return $dataManagerClass::getObjectClassName();
367 $className = $entityName;
369 if ($className ==
'')
372 $className =
'NNM_Object';
375 $className = static::DEFAULT_OBJECT_PREFIX.$className;
386 return static::normalizeName($dataClass::getCollectionClass());
395 return $dataClass::getCollectionClassName();
400 $className = static::DEFAULT_OBJECT_PREFIX.$entityName.
'_Collection';
413 return new $objectClass($setDefaultValues);
422 return new $collectionClass($this);
437 return $objectClass::wakeUp($row);
452 return $collectionClass::wakeUp($rows);
464 if (isset($this->fields[StringHelper::strtoupper($field->
getName())]) && !$this->isClone)
466 trigger_error(sprintf(
467 'Entity `%s` already has Field with name `%s`.', $this->
getFullName(), $field->
getName()
476 $this->references[$field->getRefEntityName()][] = $field;
479 $this->fields[StringHelper::strtoupper($field->
getName())] = $field;
481 if ($field instanceof
ScalarField && $field->isPrimary())
483 $this->primary[] = $field->
getName();
485 if($field->isAutocomplete())
487 $this->autoIncrement = $field->
getName();
492 if ($field instanceof UField && $field->getTypeId() ==
'iblock_section')
494 $refFieldName = $field->
getName().
'_BY';
496 if ($field->isMultiple())
498 $localFieldName = $field->getValueFieldName();
502 $localFieldName = $field->
getName();
505 $newFieldInfo = array(
506 'data_type' =>
'Bitrix\Iblock\Section',
507 'reference' => array($localFieldName,
'ID')
510 $newRefField =
new Reference($refFieldName, $newFieldInfo[
'data_type'], $newFieldInfo[
'reference'][0], $newFieldInfo[
'reference'][1]);
511 $newRefField->setEntity($this);
513 $this->fields[StringHelper::strtoupper($refFieldName)] = $newRefField;
527 public function addField($fieldInfo, $fieldName =
null)
531 return $this->
appendField($field) ? $field :
false;
536 if (array_key_exists($key = strtolower($refEntityName), $this->references))
538 return count($this->references[$key]);
547 if (array_key_exists($key = strtolower($refEntityName), $this->references))
549 return $this->references[$key];
558 return $this->fields;
571 return $this->fields[StringHelper::strtoupper(
$name)];
581 return isset($this->fields[StringHelper::strtoupper(
$name)]);
589 $scalarFields = array();
595 $scalarFields[$field->getName()] = $field;
599 return $scalarFields;
613 if ($this->hasUField(
$name))
615 return $this->u_fields[
$name];
619 '%s Entity has no `%s` userfield.', $this->
getName(),
$name
631 public function hasUField($name)
633 if (is_null($this->u_fields))
635 $this->u_fields = array();
637 if($this->uf_id <>
'')
640 global $USER_FIELD_MANAGER;
642 foreach($USER_FIELD_MANAGER->getUserFields($this->uf_id) as $info)
644 $this->u_fields[$info[
'FIELD_NAME']] =
new UField($info);
645 $this->u_fields[$info[
'FIELD_NAME']]->setEntity($this);
648 if($info[
'USER_TYPE_ID'] ==
'iblock_section')
650 $info[
'FIELD_NAME'] .=
'_BY';
651 $this->u_fields[$info[
'FIELD_NAME']] =
new UField($info);
652 $this->u_fields[$info[
'FIELD_NAME']]->setEntity($this);
658 return isset($this->u_fields[$name]);
668 return substr($this->className, 0, -5);
673 return substr($this->className, 0, strrpos($this->className,
'\\') + 1);
678 if($this->module ===
null)
683 $parts = explode(
"\\", $this->className);
684 if($parts[1] ==
"Bitrix")
685 $this->module = strtolower($parts[2]);
686 elseif(!empty($parts[1]) && isset($parts[2]))
687 $this->module = strtolower($parts[1].
".".$parts[2]);
691 return $this->module;
699 return $this->className;
706 public function getConnection()
709 $conn = Main\Application::getInstance()->getConnectionPool()->getConnection($this->connectionName);
715 return $this->dbTableName;
720 return count($this->primary) == 1 ? $this->primary[0] : $this->primary;
725 return $this->primary;
730 return $this->autoIncrement;
755 $dataClass = $this->className;
756 return $dataClass::setDefaultScope($query);
761 return class_exists(static::normalizeEntityClass(
$name));
771 if (strtolower(substr($entityName, -5)) !==
'table')
773 $entityName .=
'Table';
776 if (substr($entityName, 0, 1) !==
'\\')
778 $entityName =
'\\'.$entityName;
786 $class = static::normalizeEntityClass($class);
787 $lastPos = strrpos($class,
'\\');
796 $namespace = substr($class, 1, $lastPos - 1);
798 $name = substr($class, $lastPos + 1, -5);
800 return compact(
'namespace',
'name');
805 if ($this->code ===
null)
810 $class_path = explode(
'\\', strtoupper(ltrim($this->className,
'\\')));
813 $class_path = array_slice($class_path, 0, -1);
816 if (count($class_path) && $class_path[0] ===
'BITRIX')
818 $class_path = array_slice($class_path, 1);
822 if (!empty($class_path))
824 $this->code = join(
'_', $class_path).
'_';
828 $this->code .= strtoupper(StringHelper::camel2snake($this->
getName()));
836 return $this->
getCode().
'_ENTITY';
842 $title = $dataClass::getTitle();
846 $title = Main\Localization\Loc::getMessage($this->
getLangCode());
861 return StringHelper::camel2snake($str);
873 return StringHelper::snake2camel($str);
878 if (substr($entityName, 0, 1) !==
'\\')
880 $entityName =
'\\'.$entityName;
883 if (strtolower(substr($entityName, -5)) ===
'table')
885 $entityName = substr($entityName, 0, -5);
893 $this->isClone =
true;
896 foreach ($this->fields as $field)
898 $field->resetEntity();
899 $field->setEntity($this);
911 public static function getInstanceByQuery(
Query $query, &$entity_name =
null)
913 if ($entity_name ===
null)
915 $entity_name =
'Tmp'.randString().
'x';
917 elseif (!preg_match(
'/^[a-z0-9_]+$/i', $entity_name))
920 'Invalid entity name `%s`.', $entity_name
924 $query_string =
'('.$query->getQuery().
')';
930 $fieldsMap = array();
932 foreach ($query->
getSelect() as $k => $v)
938 $fieldsMap[$k] = array(
'data_type' => $v[
'data_type']);
942 if ($v instanceof ExpressionField)
944 $fieldDefinition = $v->getName();
947 $dataType = Field::getOldDataTypeByField($query_chains[$fieldDefinition]->getLastElement()->getValue());
948 $fieldsMap[$fieldDefinition] = array(
'data_type' => $dataType);
952 $fieldDefinition = is_numeric($k) ? $v : $k;
955 $field = $query_chains[$fieldDefinition]->getLastElement()->getValue();
957 if ($field instanceof ExpressionField)
959 $dataType = Field::getOldDataTypeByField($query_chains[$fieldDefinition]->getLastElement()->getValue());
960 $fieldsMap[$fieldDefinition] = array(
'data_type' => $dataType);
966 $fieldsMap[$fieldDefinition]->setName($fieldDefinition);
967 $fieldsMap[$fieldDefinition]->setColumnName($fieldDefinition);
973 if (isset($replaced_aliases[$k]))
975 if (is_array($fieldsMap[$k]))
977 $fieldsMap[$k][
'column_name'] = $replaced_aliases[$k];
979 elseif ($fieldsMap[$k] instanceof ScalarField)
982 $fieldsMap[$k]->setColumnName($replaced_aliases[$k]);
988 $eval =
'class '.$entity_name.
'Table extends '.DataManager::class.
' {'.PHP_EOL;
989 $eval .=
'public static function getMap() {'.PHP_EOL;
990 $eval .=
'return '.var_export([
'TMP_ID' => [
'data_type' =>
'integer',
'primary' =>
true,
'auto_generated' =>
true]],
true).
';'.PHP_EOL;
992 $eval .=
'public static function getTableName() {'.PHP_EOL;
993 $eval .=
'return '.var_export($query_string,
true).
';'.PHP_EOL;
999 $entity = self::getInstance($entity_name);
1001 foreach ($fieldsMap as $k => $v)
1003 $entity->addField($v, $k);
1019 public static function compileEntity($entityName, $fields =
null, $parameters = array())
1024 if (strtolower(substr($entityName, -5)) !==
'table')
1026 $entityName .=
'Table';
1030 if (!preg_match(
'/^[a-z0-9_]+$/i', $entityName))
1032 throw new Main\ArgumentException(sprintf(
1033 'Invalid entity className `%s`.', $entityName
1038 $fullEntityName = $entityName;
1041 if (!empty($parameters[
'namespace']) && $parameters[
'namespace'] !==
'\\')
1043 $namespace = $parameters[
'namespace'];
1045 if (!preg_match(
'/^[a-z0-9_\\\\]+$/i', $namespace))
1047 throw new Main\ArgumentException(sprintf(
1048 'Invalid namespace name `%s`', $namespace
1052 $classCode = $classCode.
"namespace {$namespace} ".
"{";
1053 $classCodeEnd =
'}'.$classCodeEnd;
1055 $fullEntityName =
'\\'.$namespace.
'\\'.$fullEntityName;
1058 $parentClass = !empty($parameters[
'parent']) ? $parameters[
'parent'] : DataManager::class;
1061 $classCode = $classCode.
"class {$entityName} extends \\".$parentClass.
" {";
1062 $classCodeEnd =
'}'.$classCodeEnd;
1064 if (!empty($parameters[
'table_name']))
1066 $classCode .=
'public static function getTableName(){return '.var_export($parameters[
'table_name'],
true).
';}';
1069 if (!empty($parameters[
'uf_id']))
1071 $classCode .=
'public static function getUfId(){return '.var_export($parameters[
'uf_id'],
true).
';}';
1074 if (!empty($parameters[
'default_scope']))
1076 $classCode .=
'public static function setDefaultScope($query){'.$parameters[
'default_scope'].
'}';
1079 if (isset($parameters[
'parent_map']) && $parameters[
'parent_map'] ==
false)
1081 $classCode .=
'public static function getMap(){return [];}';
1084 if(isset($parameters[
'object_parent']) && is_a($parameters[
'object_parent'], EntityObject::class,
true))
1086 $classCode .=
'public static function getObjectParentClass(){return '.var_export($parameters[
'object_parent'],
true).
';}';
1090 eval($classCode.$classCodeEnd);
1092 $entity = $fullEntityName::getEntity();
1095 if (!empty($fields))
1097 foreach ($fields as $fieldName => $field)
1099 $entity->addField($field, $fieldName);
1110 public function compileDbTableStructureDump()
1115 $connection = $this->getConnection();
1120 foreach ($fields as $field)
1122 if ($field->isAutocomplete())
1124 $autocomplete[] = $field->getName();
1127 if ($field->isUnique())
1129 $unique[] = $field->getName();
1134 $connection->disableQueryExecuting();
1140 foreach ($unique as $fieldName)
1142 $connection->createIndex($this->
getDBTableName(), $fieldName, [$fieldName],
null,
1143 Main\DB\MysqlCommonConnection::INDEX_UNIQUE);
1147 $connection->enableQueryExecuting();
1149 return $connection->getDisabledQueryExecutingDump();
1159 $dataClass = static::normalizeEntityClass($dataClass);
1160 $classParts = static::getEntityClassParts($dataClass);
1162 if (class_exists($dataClass::getObjectClass(),
false)
1163 && is_subclass_of($dataClass::getObjectClass(), EntityObject::class))
1166 return $dataClass::getObjectClass();
1169 $baseObjectClass =
'\\'.$dataClass::getObjectParentClass();
1170 $objectClassName = static::getDefaultObjectClassName($classParts[
'name']);
1173 if($classParts[
'namespace'] <>
'')
1175 $eval .=
"namespace {$classParts['namespace']} {";
1177 $eval .=
"class {$objectClassName} extends {$baseObjectClass} {";
1178 $eval .=
"static public \$dataClass = '{$dataClass}';";
1180 if($classParts[
'namespace'] <>
'')
1187 return $dataClass::getObjectClass();
1197 $dataClass = static::normalizeEntityClass($dataClass);
1198 $classParts = static::getEntityClassParts($dataClass);
1200 if (class_exists($dataClass::getCollectionClass(),
false)
1201 && is_subclass_of($dataClass::getCollectionClass(), Collection::class))
1204 return $dataClass::getCollectionClass();
1207 $baseCollectionClass =
'\\'.$dataClass::getCollectionParentClass();
1208 $collectionClassName = static::getDefaultCollectionClassName($classParts[
'name']);
1211 if($classParts[
'namespace'] <>
'')
1213 $eval .=
"namespace {$classParts['namespace']} {";
1215 $eval .=
"class {$collectionClassName} extends {$baseCollectionClass} {";
1216 $eval .=
"static public \$dataClass = '{$dataClass}';";
1218 if($classParts[
'namespace'] <>
'')
1225 return $dataClass::getCollectionClass();
1236 foreach ($this->compileDbTableStructureDump() as $sqlQuery)
1238 $this->getConnection()->query($sqlQuery);
1249 if ($entity instanceof
Entity)
1251 $entityName = $entity->getDataClass();
1255 $entityName = static::normalizeEntityClass($entity);
1258 if (isset(self::$instances[$entityName]))
1260 unset(self::$instances[$entityName]);
1261 DataManager::unsetEntity($entityName);
1272 if (!empty($userfield[
'ENTITY_ID']))
1274 $ufEntityId = $userfield[
'ENTITY_ID'];
1276 elseif (!empty($id))
1278 $usertype = new \CUserTypeEntity();
1279 $userfield = $usertype->GetList([], [
"ID" => $id])->Fetch();
1283 $ufEntityId = $userfield[
'ENTITY_ID'];
1287 if (empty($ufEntityId))
1293 if (!empty(static::$ufIdIndex[$ufEntityId]))
1295 if (!empty(static::$instances[static::$ufIdIndex[$ufEntityId]]))
1298 static::destroy(static::$instances[static::$ufIdIndex[$ufEntityId]]);
1317 $cache = Main\Application::getInstance()->getManagedCache();
1323 if ($cache->read($ttl, $cacheId.
".total", $cacheDir))
1325 $count = $cache->get($cacheId.
".total");
1333 if($cache->read($ttl, $cacheId, $cacheDir))
1338 $result->setCount($count);
1357 $rows = $result->fetchAll();
1360 $cache = Main\Application::getInstance()->getManagedCache();
1361 $cache->set($cacheId, $rows);
1365 $count = $result->getCount();
1366 $cache->set($cacheId.
".total", $count);
1367 $arrayResult->setCount($count);
1369 return $arrayResult;
1385 $cacheFlags = Main\Config\Configuration::getValue(
"cache_flags");
1386 if(isset($cacheFlags[$table.
"_min_ttl"]))
1388 $ttl = (int)max($ttl, $cacheFlags[$table.
"_min_ttl"]);
1390 if(isset($cacheFlags[$table.
"_max_ttl"]))
1392 $ttl = (int)min($ttl, $cacheFlags[$table.
"_max_ttl"]);
1399 return "orm_".$this->getDBTableName();
1410 $cache = Main\Application::getInstance()->getManagedCache();
readFromCache($ttl, $cacheId, $countTotal=false)
static onUserTypeChange($userfield, $id=null)
writeToCache(Main\DB\Result $result, $cacheId, $countTotal=false)
addField($fieldInfo, $fieldName=null)