• Модуль: main
  • Путь к файлу: ~/bitrix/modules/main/lib/orm/query/query.php
  • Класс: Bitrix\Main\ORM\Query\Query
  • Вызов: Query::buildJoinMap
protected function buildJoinMap($chains = null)
{
	$connection = $this->entity->getConnection();
	$helper = $connection->getSqlHelper();

	$aliasLength = $helper->getAliasLength();

	if (empty($chains))
	{
		$chains = $this->global_chains;
	}

	foreach ($chains as $chain)
	{
		if ($chain->getLastElement()->getParameter('talias'))
		{
			// already been here
			continue;
		}

		// in NO_DOUBLING mode skip 1:N relations that presented in filter only
		if ($chain->forcesDataDoublingOff() || ($this->data_doubling_off && $chain->hasBackReference()))
		{
			$alias = $chain->getAlias();

			if (isset($this->filter_chains[$alias])
				&& !isset($this->select_chains[$alias]) && !isset($this->select_expr_chains[$alias])
				&& !isset($this->group_chains[$alias]) && !isset($this->order_chains[$alias])
			)
			{
				continue;
			}
		}

		$prev_alias = $this->getInitAlias(false);

		$map_key = '';

		/**
		 * elements after init entity
		 * @var $elements ChainElement[]
		 * */
		$elements = array_slice($chain->getAllElements(), 1);

		$currentDefinition = array();

		foreach ($elements as $element)
		{
			$table_alias = null;

			/**
			 * define main objects
			 * @var $ref_field Reference
			 * @var $dst_entity Entity
			 */
			if ($element->getValue() instanceof Reference)
			{
				// ref to another entity
				$ref_field = $element->getValue();
				$dst_entity = $ref_field->getRefEntity();
				$joinType = $ref_field->getJoinType();
			}
			elseif (is_array($element->getValue()))
			{
				// link from another entity to this
				list($dst_entity, $ref_field) = $element->getValue();
				$joinType = $ref_field->getJoinType();
			}
			elseif ($element->getValue() instanceof OneToMany)
			{
				// the same as back reference
				$dst_entity = $element->getValue()->getRefEntity();
				$ref_field = $element->getValue()->getRefField();
				$joinType = $element->getValue()->getJoinType() ?: $ref_field->getJoinType();
			}
			elseif ($element->getValue() instanceof ManyToMany)
			{
				$mtm = $element->getValue();

				// join mediator and remote entities in hidden mode
				// first, make new chain, remove everything after this mtm and remove mtm itself
				$tmpChain = clone $chain;
				$mtmDefinition = join('.', $currentDefinition);

				while ($tmpChain->getDefinition() != $mtmDefinition)
				{
					$tmpChain->removeLastElement();
				}

				// then add backReference to mediator - mediator entity and local reference
				$tmpChain->addElement(new ChainElement([
					$mtm->getMediatorEntity(), $mtm->getLocalReference()
				]));

				// then add reference from mediator to remote entity
				$tmpChain->addElement(new ChainElement($mtm->getRemoteReference()));

				// now join this chain
				$this->registerChain('global', $tmpChain);
				$this->buildJoinMap([$tmpChain]);

				// and finally remember table alias for mtm element
				$prev_alias = $tmpChain->getLastElement()->getParameter('talias');
				$element->setParameter('talias', $prev_alias);

				// skip any standard actions, continue with next element
				continue;
			}
			else
			{
				// scalar field
				// if it's a field of the init entity, use getInitAlias to use 'base' alias
				if ($prev_alias === $this->getInitAlias(false))
				{
					$element->setParameter('talias', $this->getInitAlias());
				}
				else
				{
					$element->setParameter('talias', $prev_alias.$this->table_alias_postfix);
				}

				continue;
			}

			// mapping
			if (empty($map_key))
			{
				$map_key = join('.', $currentDefinition);
			}

			$map_key .= '/' . $ref_field->getName() . '/' . $dst_entity->getName();

			$currentDefinition[] = $element->getDefinitionFragment();

			if (isset($this->join_registry[$map_key]))
			{
				// already connected
				$table_alias = $this->join_registry[$map_key];
			}
			else
			{
				// prepare reference
				$reference = $ref_field->getReference();

				if ($element->getValue() instanceof Reference)
				{
					// ref to another entity
					if (is_null($table_alias))
					{
						$table_alias = $prev_alias.'_'.strtolower($ref_field->getName());

						if (strlen($table_alias.$this->table_alias_postfix) > $aliasLength)
						{
							$old_table_alias = $table_alias;
							$table_alias = 'TALIAS_' . (count($this->replaced_taliases) + 1);
							$this->replaced_taliases[$table_alias] = $old_table_alias;
						}
					}

					$alias_this = $prev_alias;
					$alias_ref = $table_alias;

					$isBackReference = false;

					$definition_this = join('.', array_slice($currentDefinition, 0, -1));
					$definition_ref = join('.', $currentDefinition);
					$definition_join = $definition_ref;
				}
				elseif (is_array($element->getValue()) || $element->getValue() instanceof OneToMany)
				{
					if (is_null($table_alias))
					{
						$table_alias = StringHelper::camel2snake($dst_entity->getName()).'_'.strtolower($ref_field->getName());
						$table_alias = $prev_alias.'_'.$table_alias;

						if (strlen($table_alias.$this->table_alias_postfix) > $aliasLength)
						{
							$old_table_alias = $table_alias;
							$table_alias = 'TALIAS_' . (count($this->replaced_taliases) + 1);
							$this->replaced_taliases[$table_alias] = $old_table_alias;
						}
					}

					$alias_this = $table_alias;
					$alias_ref = $prev_alias;

					$isBackReference = true;

					$definition_this = join('.', $currentDefinition);
					$definition_ref = join('.', array_slice($currentDefinition, 0, -1));
					$definition_join = $definition_this;
				}
				else
				{
					throw new Main\SystemException(sprintf('Unknown reference element `%s`', $element->getValue()));
				}

				// replace this. and ref. to real definition
				if ($reference instanceof Filter)
				{
					$csw_reference = $this->prepareJoinFilterReference(
						$reference,
						$alias_this.$this->table_alias_postfix,
						$alias_ref.$this->table_alias_postfix,
						$definition_this,
						$definition_ref,
						$isBackReference
					);
				}
				else
				{
					$csw_reference = $this->prepareJoinReference(
						$reference,
						$alias_this.$this->table_alias_postfix,
						$alias_ref.$this->table_alias_postfix,
						$definition_this,
						$definition_ref,
						$isBackReference
					);
				}

				// double check after recursive call in prepareJoinReference
				if (!isset($this->join_registry[$map_key]))
				{
					$join = array(
						'type' => $joinType,
						'entity' => $dst_entity,
						'definition' => $definition_join,
						'table' => $dst_entity->getDBTableName(),
						'alias' => $table_alias.$this->table_alias_postfix,
						'reference' => $csw_reference,
						'map_key' => $map_key
					);

					$this->join_map[] = $join;
					$this->join_registry[$map_key] = $table_alias;
				}
			}

			// set alias for each element
			$element->setParameter('talias', $table_alias.$this->table_alias_postfix);

			$prev_alias = $table_alias;
		}
	}
}