| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 | <?php/** * @link http://www.yiiframework.com/ * @copyright Copyright (c) 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */namespace yii\db\conditions;use yii\db\ExpressionBuilderInterface;use yii\db\ExpressionBuilderTrait;use yii\db\ExpressionInterface;use yii\db\Query;/** * Class InConditionBuilder builds objects of [[InCondition]] * * @author Dmytro Naumenko <d.naumenko.a@gmail.com> * @since 2.0.14 */class InConditionBuilder implements ExpressionBuilderInterface{    use ExpressionBuilderTrait;    /**     * Method builds the raw SQL from the $expression that will not be additionally     * escaped or quoted.     *     * @param ExpressionInterface|InCondition $expression the expression to be built.     * @param array $params the binding parameters.     * @return string the raw SQL that will not be additionally escaped or quoted.     */    public function build(ExpressionInterface $expression, array &$params = [])    {        $operator = strtoupper($expression->getOperator());        $column = $expression->getColumn();        $values = $expression->getValues();        if ($column === []) {            // no columns to test against            return $operator === 'IN' ? '0=1' : '';        }        if ($values instanceof Query) {            return $this->buildSubqueryInCondition($operator, $column, $values, $params);        }        if (!is_array($values) && !$values instanceof \Traversable) {            // ensure values is an array            $values = (array) $values;        }        if (is_array($column)) {            if (count($column) > 1) {                return $this->buildCompositeInCondition($operator, $column, $values, $params);            } else {                $column = reset($column);            }        }        if ($column instanceof \Traversable) {            if (iterator_count($column) > 1) {                return $this->buildCompositeInCondition($operator, $column, $values, $params);            } else {                $column->rewind();                $column = $column->current();            }        }        if (is_array($values)) {            $rawValues = $values;        } elseif ($values instanceof \Traversable) {            $rawValues = $this->getRawValuesFromTraversableObject($values);        }        if (isset($rawValues) && in_array(null, $rawValues, true)) {            $nullCondition = $this->getNullCondition($operator, $column);            $nullConditionOperator = $operator === 'IN' ? 'OR' : 'AND';        }        $sqlValues = $this->buildValues($expression, $values, $params);        if (empty($sqlValues)) {            if (!isset($nullCondition)) {                return $operator === 'IN' ? '0=1' : '';            }            return $nullCondition;        }        if (strpos($column, '(') === false) {            $column = $this->queryBuilder->db->quoteColumnName($column);        }        if (count($sqlValues) > 1) {            $sql = "$column $operator (" . implode(', ', $sqlValues) . ')';        } else {            $operator = $operator === 'IN' ? '=' : '<>';            $sql = $column . $operator . reset($sqlValues);        }        return isset($nullCondition) ? sprintf('%s %s %s', $sql, $nullConditionOperator, $nullCondition) : $sql;    }    /**     * Builds $values to be used in [[InCondition]]     *     * @param ConditionInterface|InCondition $condition     * @param array $values     * @param array $params the binding parameters     * @return array of prepared for SQL placeholders     */    protected function buildValues(ConditionInterface $condition, $values, &$params)    {        $sqlValues = [];        $column = $condition->getColumn();        if (is_array($column)) {            $column = reset($column);        }        if ($column instanceof \Traversable) {            $column->rewind();            $column = $column->current();        }        foreach ($values as $i => $value) {            if (is_array($value) || $value instanceof \ArrayAccess) {                $value = isset($value[$column]) ? $value[$column] : null;            }            if ($value === null) {                continue;            } elseif ($value instanceof ExpressionInterface) {                $sqlValues[$i] = $this->queryBuilder->buildExpression($value, $params);            } else {                $sqlValues[$i] = $this->queryBuilder->bindParam($value, $params);            }        }        return $sqlValues;    }    /**     * Builds SQL for IN condition.     *     * @param string $operator     * @param array|string $columns     * @param Query $values     * @param array $params     * @return string SQL     */    protected function buildSubqueryInCondition($operator, $columns, $values, &$params)    {        $sql = $this->queryBuilder->buildExpression($values, $params);        if (is_array($columns)) {            foreach ($columns as $i => $col) {                if (strpos($col, '(') === false) {                    $columns[$i] = $this->queryBuilder->db->quoteColumnName($col);                }            }            return '(' . implode(', ', $columns) . ") $operator $sql";        }        if (strpos($columns, '(') === false) {            $columns = $this->queryBuilder->db->quoteColumnName($columns);        }        return "$columns $operator $sql";    }    /**     * Builds SQL for IN condition.     *     * @param string $operator     * @param array|\Traversable $columns     * @param array $values     * @param array $params     * @return string SQL     */    protected function buildCompositeInCondition($operator, $columns, $values, &$params)    {        $vss = [];        foreach ($values as $value) {            $vs = [];            foreach ($columns as $column) {                if (isset($value[$column])) {                    $vs[] = $this->queryBuilder->bindParam($value[$column], $params);                } else {                    $vs[] = 'NULL';                }            }            $vss[] = '(' . implode(', ', $vs) . ')';        }        if (empty($vss)) {            return $operator === 'IN' ? '0=1' : '';        }        $sqlColumns = [];        foreach ($columns as $i => $column) {            $sqlColumns[] = strpos($column, '(') === false ? $this->queryBuilder->db->quoteColumnName($column) : $column;        }        return '(' . implode(', ', $sqlColumns) . ") $operator (" . implode(', ', $vss) . ')';    }    /**     * Builds is null/is not null condition for column based on operator     *     * @param string $operator     * @param string $column     * @return string is null or is not null condition     * @since 2.0.31     */    protected function getNullCondition($operator, $column) {        $column = $this->queryBuilder->db->quoteColumnName($column);        if ($operator === 'IN') {            return sprintf('%s IS NULL', $column);        }        return sprintf('%s IS NOT NULL', $column);    }    /**     * @param \Traversable $traversableObject     * @return array raw values     * @since 2.0.31     */    protected function getRawValuesFromTraversableObject(\Traversable $traversableObject)    {        $rawValues = [];        foreach ($traversableObject as $value) {            if (is_array($value)) {                $values = array_values($value);                $rawValues = array_merge($rawValues, $values);            } else {                $rawValues[] = $value;            }        }        return $rawValues;    }}
 |