• Модуль: main
  • Путь к файлу: ~/bitrix/modules/main/lib/entity/querychain.php
  • Класс: BitrixMainEntityQueryChain
  • Вызов: QueryChain::getChainByDefinition
static function getChainByDefinition(Base $init_entity, $definition)
{
	if (!is_string($definition))
	{
		throw new MainArgumentException('String expected, but `'.gettype($definition).'` is given.');
	}

	$chain = new QueryChain;
	$chain->addElement(new QueryChainElement($init_entity));

	$def_elements = explode('.', $definition);
	$def_elements_size = count($def_elements);

	$prev_entity  = $init_entity;

	$i = 0;

	foreach ($def_elements as &$def_element)
	{
		$is_last_elem  = (++$i == $def_elements_size);

		$not_found = false;

		// all elements should be a Reference field or Entity
		// normal (scalar) field can only be the last element

		if ($prev_entity->hasField($def_element))
		{
			// field has been found at current entity
			$field = $prev_entity->getField($def_element);

			if ($field instanceof ReferenceField)
			{
				$prev_entity = $field->getRefEntity();
			}
			elseif ($field instanceof ExpressionField)
			{
				// expr can be in the middle too
			}
			elseif (!$is_last_elem)
			{
				throw new SystemException(sprintf(
					'Normal fields can be only the last in chain, `%s` %s is not the last.',
					$field->getName(), get_class($field)
				));
			}

			if ($is_last_elem && $field instanceof ExpressionField)
			{
				// we should have own copy of build_from_chains to set join aliases there
				$field = clone $field;
			}

			$chain->addElement(new QueryChainElement($field));
		}
		elseif ($prev_entity->hasUField($def_element) && false)
		{
			/** @deprecated */
			// extend chain with utm/uts entity
			$ufield = $prev_entity->getUField($def_element);

			if ($ufield->isMultiple())
			{
				// add utm entity  user.utm:source_object (1:N)
				$utm_entity = Base::getInstance($prev_entity->getNamespace().'Utm'.$prev_entity->getName());

				$chain->addElement(new QueryChainElement(
					array($utm_entity, $utm_entity->getField('SOURCE_OBJECT')),
					array('ufield' => $ufield)
				));

				if ($ufield->getTypeId() == 'iblock_section'
					&& substr($ufield->getName(), -3) == '_BY'
					&& $prev_entity->hasUField(substr($ufield->getName(), 0, -3))
				)
				{
					// connect next entity
					$utm_fname = $ufield->getName();
					$prev_entity = Base::getInstance('BitrixIblockSection');
				}
				else
				{
					$utm_fname = $ufield->getValueFieldName();
				}

				$chain->addElement(new QueryChainElement(
					$utm_entity->getField($utm_fname),
					array('ufield' => $ufield)
				));
			}
			else
			{
				// uts table - single value
				// add uts entity user.uts (1:1)
				$uts_entity = Base::getInstance($prev_entity->getNamespace().'Uts'.$prev_entity->getName());

				$chain->addElement(new QueryChainElement(
					$prev_entity->getField('UTS_OBJECT')
				));

				// add `value` field
				$chain->addElement(new QueryChainElement(
					$uts_entity->getField($def_element)
				));
			}
		}
		elseif (Base::isExists($def_element)
			&& Base::getInstance($def_element)->getReferencesCountTo($prev_entity->getName()) == 1
		)
		{
			// def_element is another entity with only 1 reference to current entity
			// need to identify Reference field
			$ref_entity = Base::getInstance($def_element);
			$field = end($ref_entity->getReferencesTo($prev_entity->getName()));

			$prev_entity = $ref_entity;

			$chain->addElement(new QueryChainElement(
				array($ref_entity, $field)
			));
		}
		elseif ( ($pos_wh = strpos($def_element, ':')) > 0 )
		{
			$ref_entity_name = substr($def_element, 0, $pos_wh);

			if (strpos($ref_entity_name, '\') === false)
			{
				// if reference has no namespace, then it'is in the namespace of previous entity
				$ref_entity_name = $prev_entity->getNamespace().$ref_entity_name;
			}

			if (
				Base::isExists($ref_entity_name)
				&& Base::getInstance($ref_entity_name)->hasField($ref_field_name = substr($def_element, $pos_wh+1))
				&& Base::getInstance($ref_entity_name)->getField($ref_field_name) instanceof ReferenceField
			)
			{
				/** @var ReferenceField $reference */
				$reference = Base::getInstance($ref_entity_name)->getField($ref_field_name);

				if (
					$reference->getRefEntity()->getFullName() == $prev_entity->getFullName() ||
					is_subclass_of(
						$prev_entity->getDataClass(),
						$reference->getRefEntity()->getDataClass()
					)
				)
				{
					// chain element is another entity with >1 references to current entity
					// def like NewsArticle:AUTHOR, NewsArticle:LAST_COMMENTER
					// NewsArticle - entity, AUTHOR and LAST_COMMENTER - Reference fields
					$chain->addElement(new QueryChainElement(array(
						Base::getInstance($ref_entity_name),
						Base::getInstance($ref_entity_name)->getField($ref_field_name)
					)));

					$prev_entity = Base::getInstance($ref_entity_name);
				}
				else
				{
					$not_found = true;
				}
			}
			else
			{
				$not_found = true;
			}

		}
		elseif ($def_element == '*' && $is_last_elem)
		{
			continue;
		}
		else
		{
			// unknown chain
			$not_found = true;
		}

		if ($not_found)
		{
			throw new SystemException(sprintf(
				'Unknown field definition `%s` (%s) for %s Entity.',
				$def_element, $definition, $prev_entity->getName()
			), 100);
		}
	}

	return $chain;
}