77 public static function get($entityName)
79 return static::getInstance($entityName);
89 public static function has($entityName)
91 $entityClass = static::normalizeEntityClass($entityName);
92 return class_exists($entityClass);
106 $entityName = static::normalizeEntityClass($entityName);
108 return self::getInstanceDirect($entityName);
118 protected static function getInstanceDirect($className)
120 if (empty(self::$instances[$className]))
123 $entityClass = $className::getEntityClass();
126 if (empty(self::$instances[$className]))
129 $entity->initialize($className);
133 $className::postInitialize(
$entity);
135 self::$instances[$className] =
$entity;
139 return self::$instances[$className];
154 if ($fieldInfo instanceof
Field)
159 if (!empty($fieldName) && !is_numeric($fieldName))
161 $field->setName($fieldName);
164 elseif (is_array($fieldInfo))
166 if (!empty($fieldInfo[
'reference']))
168 if (is_string($fieldInfo[
'data_type']) && !str_contains($fieldInfo[
'data_type'],
'\\'))
171 $fieldInfo[
'data_type'] = $this->
getNamespace().$fieldInfo[
'data_type'];
175 $field =
new Reference($fieldName, $fieldInfo[
'data_type'], $fieldInfo[
'reference'], $fieldInfo);
177 elseif (!empty($fieldInfo[
'expression']))
179 $expression = array_shift($fieldInfo[
'expression']);
180 $buildFrom = $fieldInfo[
'expression'];
182 $field =
new ExpressionField($fieldName, $expression, $buildFrom, $fieldInfo);
184 elseif (!empty($fieldInfo[
'USER_TYPE_ID']))
186 $field =
new UField($fieldInfo);
190 $fieldClass = StringHelper::snake2camel($fieldInfo[
'data_type']) .
'Field';
191 $fieldClass =
'\\Bitrix\\Main\\Entity\\'.$fieldClass;
193 if (strlen($fieldInfo[
'data_type']) && class_exists($fieldClass))
195 $field =
new $fieldClass($fieldName, $fieldInfo);
197 elseif (strlen($fieldInfo[
'data_type']) && class_exists($fieldInfo[
'data_type']))
199 $fieldClass = $fieldInfo[
'data_type'];
200 $field =
new $fieldClass($fieldName, $fieldInfo);
205 'Unknown data type "%s" found for `%s` field in %s Entity.',
206 $fieldInfo[
'data_type'], $fieldName, $this->
getName()
214 is_object($fieldInfo) ? get_class($fieldInfo) : gettype($fieldInfo)
218 $field->setEntity($this);
219 $field->postInitialize();
224 public function initialize($className)
227 $this->className = $className;
230 $this->connectionName = $className::getConnectionName();
231 $this->dbTableName = $className::getTableName();
232 $this->fieldsMap = $className::getMap();
233 $this->uf_id = $className::getUfId();
234 $this->isUts = $className::isUts();
235 $this->isUtm = $className::isUtm();
251 $this->className = static::normalizeEntityClass(
$className);
253 $classPath = explode(
'\\', ltrim($this->className,
'\\'));
254 $this->name = substr(end($classPath), 0, -5);
264 $classPath = explode(
'\\', ltrim($this->className,
'\\'));
265 $this->name = substr(end($classPath), 0, -5);
268 if (is_null($this->dbTableName))
270 $_classPath = array_slice($classPath, 0, -1);
272 $this->dbTableName =
'b_';
274 foreach ($_classPath as
$i => $_pathElem)
276 if (
$i == 0 && $_pathElem ==
'Bitrix')
282 if (
$i == 1 && $_pathElem ==
'Main')
288 $this->dbTableName .= strtolower($_pathElem).
'_';
292 if ($this->name !== end($_classPath))
294 $this->dbTableName .= StringHelper::camel2snake($this->name);
298 $this->dbTableName = substr($this->dbTableName, 0, -1);
302 $this->primary =
array();
303 $this->references =
array();
306 foreach ($this->fieldsMap as $fieldName => &$fieldInfo)
308 $this->
addField($fieldInfo, $fieldName);
311 if (!empty($this->fieldsMap) && empty($this->primary))
317 if (empty($this->uf_id))
321 if($userTypeManager instanceof \CUserTypeManager)
323 $entityList = $userTypeManager->getEntityList();
324 $ufId = is_array($entityList) ? array_search($this->className, $entityList) :
false;
327 $this->uf_id = $ufId;
332 if (!empty($this->uf_id))
335 Main\UserFieldTable::attachFields($this, $this->uf_id);
338 static::$ufIdIndex[$this->uf_id] = $this->className;
349 $dataManagerClass = $this->className;
350 return static::normalizeName($dataManagerClass::getObjectClass());
360 $dataManagerClass = $this->className;
361 return $dataManagerClass::getObjectClassName();
374 $className = static::DEFAULT_OBJECT_PREFIX.$className;
385 return static::normalizeName($dataClass::getCollectionClass());
394 return $dataClass::getCollectionClassName();
399 $className = static::DEFAULT_OBJECT_PREFIX.$entityName.
'_Collection';
412 return new $objectClass($setDefaultValues);
421 return new $collectionClass($this);
436 return $objectClass::wakeUp($row);
451 return $collectionClass::wakeUp(
$rows);
463 if (isset($this->fields[StringHelper::strtoupper($field->
getName())]) && !$this->isClone)
465 trigger_error(sprintf(
466 'Entity `%s` already has Field with name `%s`.', $this->
getFullName(), $field->
getName()
475 $this->references[$field->getRefEntityName()][] = $field;
478 $this->fields[StringHelper::strtoupper($field->
getName())] = $field;
480 if ($field instanceof
ScalarField && $field->isPrimary())
482 $this->primary[] = $field->
getName();
484 if($field->isAutocomplete())
486 $this->autoIncrement = $field->
getName();
491 if ($field instanceof UField && $field->getTypeId() ==
'iblock_section')
493 $refFieldName = $field->
getName().
'_BY';
495 if ($field->isMultiple())
497 $localFieldName = $field->getValueFieldName();
501 $localFieldName = $field->
getName();
504 $newFieldInfo =
array(
505 'data_type' =>
'Bitrix\Iblock\Section',
506 'reference' =>
array($localFieldName,
'ID'),
509 $newRefField =
new Reference($refFieldName, $newFieldInfo[
'data_type'], $newFieldInfo[
'reference'][0], $newFieldInfo[
'reference'][1]);
510 $newRefField->setEntity($this);
512 $this->fields[StringHelper::strtoupper($refFieldName)] = $newRefField;
526 public function addField($fieldInfo, $fieldName =
null)
530 return $this->
appendField($field) ? $field :
false;
535 if (array_key_exists(
$key = strtolower($refEntityName), $this->references))
546 if (array_key_exists(
$key = strtolower($refEntityName), $this->references))
548 return $this->references[
$key];
570 return $this->fields[StringHelper::strtoupper(
$name)];
580 return isset($this->fields[StringHelper::strtoupper(
$name)]);
588 $scalarFields =
array();
594 $scalarFields[$field->getName()] = $field;
598 return $scalarFields;
612 if ($this->hasUField(
$name))
614 return $this->u_fields[
$name];
618 '%s Entity has no `%s` userfield.', $this->
getName(),
$name
630 public function hasUField(
$name)
632 if (is_null($this->u_fields))
634 $this->u_fields =
array();
636 if($this->uf_id <>
'')
643 $this->u_fields[
$info[
'FIELD_NAME']] =
new UField(
$info);
644 $this->u_fields[
$info[
'FIELD_NAME']]->setEntity($this);
647 if(
$info[
'USER_TYPE_ID'] ==
'iblock_section')
649 $info[
'FIELD_NAME'] .=
'_BY';
650 $this->u_fields[
$info[
'FIELD_NAME']] =
new UField(
$info);
651 $this->u_fields[
$info[
'FIELD_NAME']]->setEntity($this);
657 return isset($this->u_fields[
$name]);
667 return substr($this->className, 0, -5);
672 return substr($this->className, 0, strrpos($this->className,
'\\') + 1);
677 if($this->module ===
null)
682 $parts = explode(
"\\", $this->className);
683 if($parts[1] ==
"Bitrix")
684 $this->module = strtolower($parts[2]);
685 elseif(!empty($parts[1]) && isset($parts[2]))
686 $this->module = strtolower($parts[1].
".".$parts[2]);
690 return $this->module;
698 return $this->className;
704 public function getConnection()
713 return $this->dbTableName;
718 return count($this->primary) == 1 ? $this->primary[0] : $this->primary;
723 return $this->primary;
731 return $this->autoIncrement;
756 $dataClass = $this->className;
757 return $dataClass::setDefaultScope(
$query);
762 return class_exists(static::normalizeEntityClass(
$name));
772 if (strtolower(substr($entityName, -5)) !==
'table')
774 $entityName .=
'Table';
777 if (!str_starts_with($entityName,
'\\'))
779 $entityName =
'\\'.$entityName;
787 $class = static::normalizeEntityClass($class);
788 $lastPos = strrpos($class,
'\\');
797 $namespace = substr($class, 1, $lastPos - 1);
799 $name = substr($class, $lastPos + 1, -5);
801 return compact(
'namespace',
'name');
806 if ($this->code ===
null)
811 $class_path = explode(
'\\', strtoupper(ltrim($this->className,
'\\')));
814 $class_path = array_slice($class_path, 0, -1);
817 if (
count($class_path) && $class_path[0] ===
'BITRIX')
819 $class_path = array_slice($class_path, 1);
823 if (!empty($class_path))
825 $this->code = join(
'_', $class_path).
'_';
829 $this->code .= strtoupper(StringHelper::camel2snake($this->
getName()));
837 return $this->
getCode().
'_ENTITY';
843 $title = $dataClass::getTitle();
862 return StringHelper::camel2snake(
$str);
874 return StringHelper::snake2camel(
$str);
879 if (!str_starts_with($entityName,
'\\'))
881 $entityName =
'\\'.$entityName;
884 if (strtolower(substr($entityName, -5)) ===
'table')
886 $entityName = substr($entityName, 0, -5);
894 $this->isClone =
true;
897 foreach ($this->fields as $field)
899 $field->resetEntity();
900 $field->setEntity($this);
912 public static function getInstanceByQuery(
Query $query, &$entity_name =
null)
914 if ($entity_name ===
null)
916 $entity_name =
'Tmp'.randString().
'x';
918 elseif (!preg_match(
'/^[a-z0-9_]+$/i', $entity_name))
921 'Invalid entity name `%s`.', $entity_name
925 $query_string =
'('.$query->getQuery().
')';
926 $query_chains =
$query->getChains();
928 $replaced_aliases = array_flip(
$query->getReplacedAliases());
931 $fieldsMap =
array();
933 foreach (
$query->getSelect() as
$k => $v)
939 $fieldsMap[
$k] =
array(
'data_type' => $v[
'data_type']);
943 if ($v instanceof ExpressionField)
945 $fieldDefinition = $v->getName();
948 $dataType = Field::getOldDataTypeByField($query_chains[$fieldDefinition]->getLastElement()->getValue());
949 $fieldsMap[$fieldDefinition] =
array(
'data_type' => $dataType);
953 $fieldDefinition = is_numeric(
$k) ? $v :
$k;
956 $field = $query_chains[$fieldDefinition]->getLastElement()->getValue();
958 if ($field instanceof ExpressionField)
960 $dataType = Field::getOldDataTypeByField($query_chains[$fieldDefinition]->getLastElement()->
getValue());
967 $fieldsMap[$fieldDefinition]->setName($fieldDefinition);
968 $fieldsMap[$fieldDefinition]->setColumnName($fieldDefinition);
974 if (isset($replaced_aliases[
$k]))
989 $eval =
'class '.$entity_name.
'Table extends '.DataManager::class.
' {'.PHP_EOL;
990 $eval .=
'public static function getMap() {'.PHP_EOL;
991 $eval .=
'return '.var_export([
'TMP_ID' => [
'data_type' =>
'integer',
'primary' =>
true,
'auto_generated' =>
true]],
true).
';'.PHP_EOL;
993 $eval .=
'public static function getTableName() {'.PHP_EOL;
994 $eval .=
'return '.var_export($query_string,
true).
';'.PHP_EOL;
1000 $entity = self::getInstance($entity_name);
1020 public static function compileEntity($entityName,
$fields =
null, $parameters =
array())
1025 if (strtolower(substr($entityName, -5)) !==
'table')
1027 $entityName .=
'Table';
1031 if (!preg_match(
'/^[a-z0-9_]+$/i', $entityName))
1033 throw new Main\ArgumentException(sprintf(
1034 'Invalid entity className `%s`.', $entityName
1039 $fullEntityName = $entityName;
1042 if (!empty($parameters[
'namespace']) && $parameters[
'namespace'] !==
'\\')
1044 $namespace = $parameters[
'namespace'];
1046 if (!preg_match(
'/^[a-z0-9_\\\\]+$/i', $namespace))
1048 throw new Main\ArgumentException(sprintf(
1049 'Invalid namespace name `%s`', $namespace
1053 $classCode = $classCode.
"namespace {$namespace} ".
"{";
1054 $classCodeEnd =
'}'.$classCodeEnd;
1056 $fullEntityName =
'\\'.$namespace.
'\\'.$fullEntityName;
1059 $parentClass = !empty($parameters[
'parent']) ? $parameters[
'parent'] : DataManager::class;
1062 $classCode = $classCode.
"class {$entityName} extends \\".$parentClass.
" {";
1063 $classCodeEnd =
'}'.$classCodeEnd;
1065 if (!empty($parameters[
'table_name']))
1067 $classCode .=
'public static function getTableName(){return '.var_export($parameters[
'table_name'],
true).
';}';
1070 if (!empty($parameters[
'uf_id']))
1072 $classCode .=
'public static function getUfId(){return '.var_export($parameters[
'uf_id'],
true).
';}';
1075 if (!empty($parameters[
'default_scope']))
1077 $classCode .=
'public static function setDefaultScope($query){'.$parameters[
'default_scope'].
'}';
1080 if (isset($parameters[
'parent_map']) && !$parameters[
'parent_map'])
1082 $classCode .=
'public static function getMap(){return [];}';
1085 if(isset($parameters[
'object_parent']) && is_a($parameters[
'object_parent'], EntityObject::class,
true))
1087 $classCode .=
'public static function getObjectParentClass(){return '.var_export($parameters[
'object_parent'],
true).
';}';
1091 eval($classCode.$classCodeEnd);
1093 $entity = $fullEntityName::getEntity();
1098 foreach (
$fields as $fieldName => $field)
1100 $entity->addField($field, $fieldName);
1111 public function compileDbTableStructureDump()
1123 if ($field->isAutocomplete())
1125 $autocomplete[] = $field->getName();
1128 if ($field->isUnique())
1130 $unique[] = $field->getName();
1141 foreach ($unique as $fieldName)
1144 Main\DB\Connection::INDEX_UNIQUE);
1150 return $connection->getDisabledQueryExecutingDump();
1160 $dataClass = static::normalizeEntityClass($dataClass);
1161 $classParts = static::getEntityClassParts($dataClass);
1163 if (class_exists($dataClass::getObjectClass(),
false)
1164 && is_subclass_of($dataClass::getObjectClass(), EntityObject::class))
1167 return $dataClass::getObjectClass();
1170 $baseObjectClass =
'\\'.$dataClass::getObjectParentClass();
1171 $objectClassName = static::getDefaultObjectClassName($classParts[
'name']);
1174 if($classParts[
'namespace'] <>
'')
1176 $eval .=
"namespace {$classParts['namespace']} {";
1178 $eval .=
"class {$objectClassName} extends {$baseObjectClass} {";
1179 $eval .=
"static public \$dataClass = '{$dataClass}';";
1181 if($classParts[
'namespace'] <>
'')
1188 return $dataClass::getObjectClass();
1198 $dataClass = static::normalizeEntityClass($dataClass);
1199 $classParts = static::getEntityClassParts($dataClass);
1201 if (class_exists($dataClass::getCollectionClass(),
false)
1202 && is_subclass_of($dataClass::getCollectionClass(), Collection::class))
1205 return $dataClass::getCollectionClass();
1208 $baseCollectionClass =
'\\'.$dataClass::getCollectionParentClass();
1209 $collectionClassName = static::getDefaultCollectionClassName($classParts[
'name']);
1212 if($classParts[
'namespace'] <>
'')
1214 $eval .=
"namespace {$classParts['namespace']} {";
1216 $eval .=
"class {$collectionClassName} extends {$baseCollectionClass} {";
1217 $eval .=
"static public \$dataClass = '{$dataClass}';";
1219 if($classParts[
'namespace'] <>
'')
1226 return $dataClass::getCollectionClass();
1237 foreach ($this->compileDbTableStructureDump() as $sqlQuery)
1239 $this->getConnection()->query($sqlQuery);
1252 $entityName =
$entity->getDataClass();
1256 $entityName = static::normalizeEntityClass(
$entity);
1259 if (isset(self::$instances[$entityName]))
1261 unset(self::$instances[$entityName]);
1262 DataManager::unsetEntity($entityName);
1273 if (!empty($userfield[
'ENTITY_ID']))
1275 $ufEntityId = $userfield[
'ENTITY_ID'];
1279 $usertype = new \CUserTypeEntity();
1280 $userfield = $usertype->GetList([], [
"ID" => $id])->Fetch();
1284 $ufEntityId = $userfield[
'ENTITY_ID'];
1288 if (empty($ufEntityId))
1294 if (!empty(static::$ufIdIndex[$ufEntityId]))
1296 if (!empty(static::$instances[static::$ufIdIndex[$ufEntityId]]))
1299 static::destroy(static::$instances[static::$ufIdIndex[$ufEntityId]]);
1323 if ($cache->read($ttl, $cacheId.
".total", $cacheDir))
1325 $count = $cache->get($cacheId.
".total");
1333 if($cache->read($ttl, $cacheId, $cacheDir))
1361 $cache->set($cacheId,
$rows);
1366 $cache->set($cacheId.
".total",
$count);
1367 $arrayResult->setCount(
$count);
1369 return $arrayResult;
1384 if (!$this->className::isCacheable())
1392 if(isset($cacheFlags[
$table.
"_min_ttl"]))
1394 $ttl = (int)max($ttl, $cacheFlags[$table.
"_min_ttl"]);
1396 if(isset($cacheFlags[
$table.
"_max_ttl"]))
1398 $ttl = (int)min($ttl, $cacheFlags[$table.
"_max_ttl"]);
1405 return "orm_".$this->getDBTableName();
static getMessage($code, $replace=null, $language=null)
static normalizeEntityClass($entityName)
readFromCache($ttl, $cacheId, $countTotal=false)
static onUserTypeChange($userfield, $id=null)
static compileObjectClass($dataClass)
static normalizeName($entityName)
appendField(Field $field)
writeToCache(Main\DB\Result $result, $cacheId, $countTotal=false)
static getDefaultObjectClassName($entityName)
initializeField($fieldName, $fieldInfo)
static getEntityClassParts($class)
static getDefaultCollectionClassName($entityName)
createObject($setDefaultValues=true)
static getInstance($entityName)
static compileCollectionClass($dataClass)
enableFullTextIndex($field, $mode=true)
addField($fieldInfo, $fieldName=null)
getReferencesTo($refEntityName)
getReferencesCountTo($refEntityName)
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)