File manager - Edit - /home/c14075/dragmet-ural.ru/www/bitrix/modules/main/lib/orm/query/filter/conditiontree.php
Back
<?php /** * Bitrix Framework * @package bitrix * @subpackage main * @copyright 2001-2016 Bitrix */ namespace Bitrix\Main\ORM\Query\Filter; use Bitrix\Main\ORM\Fields\BooleanField; use Bitrix\Main\ORM\Fields\ExpressionField; use Bitrix\Main\ORM\Query\Filter\Expressions\ColumnExpression; use Bitrix\Main\ORM\Query\Query; use Bitrix\Main\ORM\Query\Chain; use Bitrix\Main\ORM\Fields\IReadable; use Bitrix\Main\DB\SqlExpression; use Bitrix\Main\ArgumentException; use Bitrix\Main\SystemException; use Bitrix\Main\Type\RandomSequence; /** * Handles filtering conditions for Query and join conditions for Entity References. * @package bitrix * @subpackage main */ class ConditionTree { /** @var Chain[] */ protected $chains; /** @var Condition[]|ConditionTree[] */ protected $conditions = array(); /** * @var string and|or * @see ConditionTree::logic() */ protected $logic; /** @var RandomSequence For temporary identifiers (field names) */ protected static RandomSequence $randomSequence; const LOGIC_OR = 'or'; const LOGIC_AND = 'and'; /** * Whether to set NOT before all the conditions. * @var bool */ protected $isNegative = false; /** * ConditionTree constructor. */ public function __construct() { $this->logic = static::LOGIC_AND; } /** * All conditions will be imploded by this logic: static::LOGIC_AND or static::LOGIC_OR * * @param string $logic and|or * * @return $this|string * @throws ArgumentException */ public function logic($logic = null) { if ($logic === null) { return $this->logic; } if (!in_array(strtolower($logic), [static::LOGIC_AND, static::LOGIC_OR], true)) { throw new ArgumentException("Unknown logic"); } $this->logic = strtolower($logic); return $this; } /** * Sets NOT before all the conditions. * * @param bool $negative * * @return $this */ public function negative($negative = true) { $this->isNegative = (bool) $negative; return $this; } /** * General condition. In regular case used with 3 parameters: * where(columnName, operator, value), e.g. ('ID', '=', 1); ('SALARY', '>', '500') * * List of available operators can be found in Operator class. * @see Operator::$operators * * Can be used in short format: * where(columnName, value), with operator '=' by default * Can be used in ultra short format: * where(columnName), for boolean fields only * * Can be used for subfilter set: * where(ConditionTree subfilter) * * Instead of columnName, you can use runtime field: * where(new ExpressionField('TMP', 'CONCAT(%s, %s)', ["NAME", "LAST_NAME"]), 'Anton Ivanov') * or with expr helper * where(Query::expr()->concat("NAME", "LAST_NAME"), 'Anton Ivanov') * * @param mixed ...$filter * * @return $this * @throws ArgumentException */ public function where(...$filter) { // subfilter if (count($filter) == 1 && $filter[0] instanceof ConditionTree) { $this->conditions[] = $filter[0]; return $this; } // ready condition if (count($filter) == 1 && $filter[0] instanceof Condition) { $this->conditions[] = $filter[0]; return $this; } // array of conditions if (count($filter) == 1 && is_array($filter[0])) { foreach ($filter[0] as $condition) { // call `where` for each condition call_user_func_array(array($this, 'where'), $condition); } return $this; } // regular conditions if (count($filter) == 3) { // everything is clear list($column, $operator, $value) = $filter; } elseif (count($filter) == 2) { // equal by default list($column, $value) = $filter; $operator = '='; } elseif (count($filter) == 1) { // suppose it is boolean field with true value $column = $filter[0]; $operator = '='; $value = true; } else { throw new ArgumentException('Wrong arguments'); } // validate operator $operators = Operator::get(); if (!isset($operators[$operator])) { throw new ArgumentException("Unknown operator `{$operator}`"); } // add condition $this->conditions[] = new Condition($column, $operator, $value); return $this; } /** * Sets NOT before any conditions or subfilter. * @see ConditionTree::where() * * @param mixed ...$filter * * @return $this */ public function whereNot(...$filter) { $subFilter = new static(); call_user_func_array(array($subFilter, 'where'), $filter); $this->conditions[] = $subFilter->negative(); return $this; } /** * The same logic as where(), but value will be taken as another column name. * @see ConditionTree::where() * * @param mixed ...$filter * * @return $this * @throws ArgumentException */ public function whereColumn(...$filter) { if (count($filter) == 3) { list($column, $operator, $value) = $filter; } elseif (count($filter) == 2) { list($column, $value) = $filter; $operator = '='; } else { throw new ArgumentException('Wrong arguments'); } // convert value to column format $value = new Expressions\ColumnExpression($value); // put through general method $this->where($column, $operator, $value); return $this; } /** * Compares column with NULL. * * @param string $column * * @return $this */ public function whereNull($column) { $this->conditions[] = new Condition($column, '=', null); return $this; } /** * Compares column with NOT NULL. * * @param string $column * * @return $this */ public function whereNotNull($column) { $this->conditions[] = new Condition($column, '<>', null); return $this; } /** * IN() condition. * * @param string $column * @param array|Query|SqlExpression $values * * @return $this */ public function whereIn($column, $values) { if (!empty($values)) { $this->conditions[] = new Condition($column, 'in', $values); } return $this; } /** * Negative IN() condition. * @see ConditionTree::whereIn() * * @param string $column * @param array|Query|SqlExpression $values * * @return $this */ public function whereNotIn($column, $values) { $subFilter = new static(); $this->conditions[] = $subFilter->whereIn($column, $values)->negative(); return $this; } /** * BETWEEN condition. * * @param $column * @param $valueMin * @param $valueMax * * @return $this */ public function whereBetween($column, $valueMin, $valueMax) { $this->conditions[] = new Condition($column, 'between', array($valueMin, $valueMax)); return $this; } /** * Negative BETWEEN condition. * @see ConditionTree::whereBetween() * * @param $column * @param $valueMin * @param $valueMax * * @return $this */ public function whereNotBetween($column, $valueMin, $valueMax) { $subFilter = new static(); $this->conditions[] = $subFilter->whereBetween($column, $valueMin, $valueMax)->negative(); return $this; } /** * LIKE condition, without default % placement. * * @param $column * @param $value * * @return $this */ public function whereLike($column, $value) { $this->conditions[] = new Condition($column, 'like', $value); return $this; } /** * Negative LIKE condition, without default % placement. * @see ConditionTree::whereLike() * * @param $column * @param $value * * @return $this */ public function whereNotLike($column, $value) { $subFilter = new static(); $this->conditions[] = $subFilter->whereLike($column, $value)->negative(); return $this; } /** * Exists() condition. Can be used with Query object or plain sql wrapped with SqlExpression. * * @param Query|SqlExpression $query * * @return $this */ public function whereExists($query) { $this->conditions[] = new Condition(null, 'exists', $query); return $this; } /** * Negative Exists() condition. Can be used with Query object or plain sql wrapped with SqlExpression. * @see ConditionTree::whereExists() * * @param Query|SqlExpression $query * * @return $this * @throws ArgumentException */ public function whereNotExists($query) { if ($query instanceof Query || $query instanceof SqlExpression) { $subFilter = new static(); $this->conditions[] = $subFilter->whereExists($query)->negative(); return $this; } throw new ArgumentException('Unknown type of query '.gettype($query)); } /** * Fulltext search condition. * @see Helper::matchAgainstWildcard() for preparing $value for AGAINST. * * @param $column * @param $value * * @return $this */ public function whereMatch($column, $value) { $this->conditions[] = new Condition($column, 'match', $value); return $this; } /** * Negative fulltext search condition. * @see Helper::matchAgainstWildcard() for preparing $value for AGAINST. * * @param $column * @param $value * * @return $this */ public function whereNotMatch($column, $value) { $subFilter = new static(); $this->conditions[] = $subFilter->whereMatch($column, $value)->negative(); return $this; } /** * Any SQL Expression condition * @see ExpressionField * * @param string $expr * @param string[] $arguments * * @return $this * @throws ArgumentException * @throws SystemException */ public function whereExpr($expr, $arguments) { // get random field name $randomSequence = static::getRandomSequence(); $tmpName = 'TMP_'.$randomSequence->randString(10); // set boolean expression $tmpField = (new ExpressionField($tmpName, $expr, $arguments)) ->configureValueType(BooleanField::class); // add condition $this->where($tmpField, 'expr', true); return $this; } /** * Returns SQL for all conditions and subfilters. * * @param Chain[] $chains * * @return string * @throws ArgumentException * @throws SystemException */ public function getSql($chains) { // save chains $this->chains = $chains; $finalSql = array(); // build sql foreach ($this->conditions as $condition) { if ($condition instanceof ConditionTree) { // subfilter $subFilter = $condition; $sql = $subFilter->getSql($chains); if (count($subFilter->getConditions()) > 1) { $sql = "({$sql})"; } } else { // regular condition $columnSqlDefinition = null; $columnField = null; // define column field if ($condition->getColumn() !== null) { $chain = $chains[$condition->getDefinition()]; $columnSqlDefinition = $chain->getSqlDefinition(); /** @var IReadable $columnField */ $columnField = $chain->getLastElement()->getValue(); } // final value's sql if (in_array($condition->getOperator(), array('in', 'between'), true) && is_array($condition->getValue())) { // value is in array of atomic values $finalValue = $this->convertValues($condition->getValue(), $columnField); } else { $finalValue = $this->convertValue($condition->getValue(), $columnField); } // operation method $operators = Operator::get(); $operator = $operators[$condition->getOperator()]; // final sql $sql = call_user_func( array('Bitrix\Main\ORM\Query\Filter\Operator', $operator), $columnSqlDefinition, $finalValue ); } if ($sql != '') { $finalSql[] = $sql; } } $sql = null; if (!empty($finalSql)) { // concat with $this->logic $sql = join(" ".strtoupper($this->logic)." ", $finalSql); // and put NOT if negative if ($this->isNegative) { $sql = count($finalSql) > 1 ? "NOT ({$sql})" : "NOT {$sql}"; } } return $sql; } /** * Returns all conditions and subfilters. * * @return ConditionTree[]|Condition[] */ public function getConditions() { return $this->conditions; } /** * Adds prepared condition. * * @param Condition|ConditionTree $condition * * @return $this * @throws ArgumentException */ public function addCondition($condition) { if ($condition instanceof Condition || $condition instanceof ConditionTree) { $this->conditions[] = $condition; return $this; } throw new ArgumentException('Unknown type of condition '.gettype($condition)); } /** * Checks if filter is not empty. * * @return bool */ public function hasConditions() { return !empty($this->conditions); } /** * Replaces condition with a new one. * * @param $currentCondition * @param $newCondition * * @return bool */ public function replaceCondition($currentCondition, $newCondition) { foreach ($this->conditions as $k => $condition) { if ($condition === $currentCondition) { $this->conditions[$k] = $newCondition; return true; } } return false; } /** * Removes one condition * * @param $condition * * @return bool */ public function removeCondition($condition) { foreach ($this->conditions as $k => $_condition) { if ($condition == $_condition) { unset($this->conditions[$k]); return true; } } return false; } /** * Removes all conditions */ public function removeAllConditions() { $this->conditions = []; } /** * Converts any value to raw SQL, except of NULL, which is supposed to be handled in Operator. * * @param mixed $value * @param IReadable $field * * @return mixed|null|string * @throws ArgumentException * @throws SystemException */ protected function convertValue($value, IReadable $field = null) { // any sql expression if ($value instanceof SqlExpression) { return $value->compile(); } // subquery if ($value instanceof Query) { return $value->getQuery(); } // subfilter if ($value instanceof ConditionTree) { return $value->getSql($this->chains); } // nulls if ($value === null) { return new Expressions\NullExpression; } if ($value instanceof Expressions\ColumnExpression) { /** @var Chain $valueChain */ $valueChain = $this->chains[$value->getDefinition()]; return $valueChain->getSqlDefinition(); } return $field->convertValueToDb($value); // give them current sql helper } /** * Converts array of values to raw SQL. * @see ConditionTree::convertValue() * * @param array $values * @param \Bitrix\Main\ORM\Fields\IReadable|null $field * * @return array * @throws ArgumentException * @throws SystemException */ protected function convertValues($values, IReadable $field = null) { foreach ($values as $k => $value) { $values[$k] = $this->convertValue($value, $field); } return $values; } public function __clone() { $newConditions = array(); foreach ($this->conditions as $condition) { $newConditions[] = clone $condition; } $this->conditions = $newConditions; } /** * @return RandomSequence */ protected static function getRandomSequence(): RandomSequence { if (!isset(static::$randomSequence)) { static::$randomSequence = new RandomSequence('orm.filter.expr'); } return static::$randomSequence; } /** * Creates filter object from array * * @param $filter * * @return ConditionTree * @throws ArgumentException */ public static function createFromArray($filter) { $conditionTree = Query::filter(); if (isset($filter['logic'])) { $conditionTree->logic($filter['logic']); unset($filter['logic']); } if (isset($filter['negative'])) { $conditionTree->negative($filter['negative']); unset($filter['negative']); } foreach ($filter as $condition) { if (isset($condition[0]) && is_array($condition[0])) { $conditionTree->where(static::createFromArray($condition)); } else { // parse regular filter condition $valueKey = key(array_reverse($condition, true)); $valueElement = $condition[$valueKey]; if (is_array($valueElement)) { if (isset($valueElement['value'])) { $value = $valueElement['value']; } elseif (isset($valueElement['column'])) { $value = new ColumnExpression($valueElement['column']); } else { // could be an array of columns foreach ($valueElement as $k => $singleValue) { if (is_array($singleValue)) { if (isset($singleValue['value'])) { $valueElement[$k] = $singleValue['value']; } elseif (isset($singleValue['column'])) { $valueElement[$k] = new ColumnExpression($singleValue['column']); } } } $value = $valueElement; } $condition[$valueKey] = $value; } $conditionTree->where(...$condition); } } return $conditionTree; } }
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.28 |
proxy
|
phpinfo
|
Settings