- Модуль: 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;
}
}
}