• Модуль: main
  • Путь к файлу: ~/bitrix/modules/main/lib/orm/query/query.php
  • Класс: Bitrix\Main\ORM\Query\Query
  • Вызов: Query::prepareJoinReference
protected function prepareJoinReference($reference, $alias_this, $alias_ref, $baseDefinition, $refDefinition, $isBackReference)
{
	$new = array();

	foreach ($reference as $k => $v)
	{
		if ($k === 'LOGIC')
		{
			$new[$k] = $v;
			continue;
		}

		if (is_numeric($k))
		{
			// subfilter, recursive call
			$new[$k] = $this->prepareJoinReference($v, $alias_this, $alias_ref, $baseDefinition, $refDefinition, $isBackReference);
		}
		else
		{
			// key
			$sqlWhere = new \CSQLWhere();
			$csw_result = $sqlWhere->makeOperation($k);
			list($field, $operation) = array_values($csw_result);

			if (strpos($field, 'this.') === 0)
			{
				// parse the chain
				$definition = str_replace(\CSQLWhere::getOperationByCode($operation).'this.', '', $k);
				$absDefinition = $baseDefinition <> ''? $baseDefinition.'.'.$definition : $definition;

				$chain = $this->getRegisteredChain($absDefinition, true);

				if (!$isBackReference)
				{
					// make sure these fields will be joined before the main join
					$this->buildJoinMap(array($chain));
				}
				else
				{
					$chain->getLastElement()->setParameter('talias', $alias_this);
				}

				// recursively collect all "build_from" fields
				if ($chain->getLastElement()->getValue() instanceof ExpressionField)
				{
					$this->collectExprChains($chain);
					$buildFrom = $chain->getLastElement()->getValue()->getBuildFromChains();

					foreach ($buildFrom as $bf)
					{
						// set base chain
						$baseChain = clone $chain;

						// remove the last one - expression itself
						$baseChain->removeLastElement();

						// remove parent entity for this child
						$bf->removeFirstElement();

						// set new parents
						$bf->prepend($baseChain);
					}

					$this->buildJoinMap($buildFrom);
				}

				$k = \CSQLWhere::getOperationByCode($operation).$chain->getSqlDefinition();
			}
			elseif (strpos($field, 'ref.') === 0)
			{
				$definition = str_replace(\CSQLWhere::getOperationByCode($operation).'ref.', '', $k);

				if (strpos($definition, '.') !== false)
				{
					throw new Main\ArgumentException(sprintf(
						'Reference chain `%s` is not allowed here. First-level definitions only.', $field
					));
				}

				$absDefinition = $refDefinition <> ''? $refDefinition.'.'.$definition : $definition;
				$chain = $this->getRegisteredChain($absDefinition, true);

				if ($isBackReference)
				{
					// make sure these fields will be joined before the main join
					$this->buildJoinMap(array($chain));
				}
				else
				{
					$chain->getLastElement()->setParameter('talias', $alias_ref);
				}

				// recursively collect all "build_from" fields
				if ($chain->getLastElement()->getValue() instanceof ExpressionField)
				{
					$this->collectExprChains($chain);
					$this->buildJoinMap($chain->getLastElement()->getValue()->getBuildFromChains());
				}

				$k = \CSQLWhere::getOperationByCode($operation).$chain->getSqlDefinition();
			}
			else
			{
				throw new Main\SystemException(sprintf('Unknown reference key `%s`, it should start with "this." or "ref."', $k));
			}

			// value
			if (is_array($v))
			{
				// field = expression
				$v = new \CSQLWhereExpression($v[0], array_slice($v, 1));
			}
			elseif ($v instanceof Main\DB\SqlExpression)
			{
				// set entity connection
				$v->setConnection($this->entity->getConnection());
			}
			elseif (!is_object($v))
			{
				if (strpos($v, 'this.') === 0)
				{
					$definition = str_replace('this.', '', $v);
					$absDefinition = $baseDefinition <> ''? $baseDefinition.'.'.$definition : $definition;

					$chain = $this->getRegisteredChain($absDefinition, true);

					if (!$isBackReference)
					{
						// make sure these fields will be joined before the main join
						$this->buildJoinMap(array($chain));
					}
					else
					{
						$chain->getLastElement()->setParameter('talias', $alias_this);
					}

					// recursively collect all "build_from" fields
					if ($chain->getLastElement()->getValue() instanceof ExpressionField)
					{
						$this->collectExprChains($chain);
						$buildFrom = $chain->getLastElement()->getValue()->getBuildFromChains();

						foreach ($buildFrom as $bf)
						{
							// set base chain
							$baseChain = clone $chain;

							// remove the last one - expression itself
							$baseChain->removeLastElement();

							// remove parent entity for this child
							$bf->removeFirstElement();

							// set new parents
							$bf->prepend($baseChain);
						}

						$this->buildJoinMap($buildFrom);
					}

					$field_def = $chain->getSqlDefinition();
				}
				elseif (strpos($v, 'ref.') === 0)
				{
					$definition = str_replace('ref.', '', $v);

					if (strpos($definition, '.') !== false)
					{
						throw new Main\ArgumentException(sprintf(
							'Reference chain `%s` is not allowed here. First-level definitions only.', $v
						));
					}

					$absDefinition = $refDefinition <> ''? $refDefinition.'.'.$definition : $definition;
					$chain = $this->getRegisteredChain($absDefinition, true);

					if ($isBackReference)
					{
						// make sure these fields will be joined before the main join
						$this->buildJoinMap(array($chain));
					}
					else
					{
						$chain->getLastElement()->setParameter('talias', $alias_ref);
					}

					$this->buildJoinMap(array($chain));

					// recursively collect all "build_from" fields
					if ($chain->getLastElement()->getValue() instanceof ExpressionField)
					{
						// here could be one more check "First-level definitions only" for buildFrom elements
						$buildFromChains = $this->collectExprChains($chain);

						// set same talias to buildFrom elements
						foreach ($buildFromChains as $buildFromChain)
						{
							if (!$isBackReference && $buildFromChain->getSize() > $chain->getSize())
							{
								throw new Main\ArgumentException(sprintf(
									'Reference chain `%s` is not allowed here. First-level definitions only.',
									$buildFromChain->getDefinition()
								));
							}

							if ($buildFromChain->getSize() === $chain->getSize())
							{
								// same entity, same table
								$buildFromChain->getLastElement()->setParameter('talias', $alias_ref);
							}
						}

						$this->buildJoinMap($buildFromChains);
					}

					$field_def = $chain->getSqlDefinition();
				}
				else
				{
					throw new Main\SystemException(sprintf('Unknown reference value `%s`', $v));
				}

				$v = new \CSQLWhereExpression($field_def);
			}
			else
			{
				throw new Main\SystemException(sprintf('Unknown reference value `%s`, it should start with "this." or "ref."', $v));
			}

			$new[$k] = $v;
		}
	}

	return $new;
}