_type = $type; } $this->setField($field); $this->setValue($value); $this->_operator = $operator; } /** * Sets the value * * @param mixed $value The value to compare * @return void */ public function setValue($value) { $hasType = isset($this->_type) && is_string($this->_type); $isMultiple = $hasType && strpos($this->_type, '[]') !== false; if ($hasType) { $value = $this->_castToExpression($value, $this->_type); } if ($isMultiple) { list($value, $this->_valueExpressions) = $this->_collectExpressions($value); } $this->_isMultiple = $isMultiple; $this->_value = $value; } /** * Returns the value used for comparison * * @return mixed */ public function getValue() { return $this->_value; } /** * Sets the operator to use for the comparison * * @param string $operator The operator to be used for the comparison. * @return void */ public function setOperator($operator) { $this->_operator = $operator; } /** * Returns the operator used for comparison * * @return string */ public function getOperator() { return $this->_operator; } /** * Convert the expression into a SQL fragment. * * @param \Cake\Database\ValueBinder $generator Placeholder generator object * @return string */ public function sql(ValueBinder $generator) { $field = $this->_field; if ($field instanceof ExpressionInterface) { $field = $field->sql($generator); } if ($this->_value instanceof ExpressionInterface) { $template = '%s %s (%s)'; $value = $this->_value->sql($generator); } else { list($template, $value) = $this->_stringExpression($generator); } return sprintf($template, $field, $this->_operator, $value); } /** * {@inheritDoc} * */ public function traverse(callable $callable) { if ($this->_field instanceof ExpressionInterface) { $callable($this->_field); $this->_field->traverse($callable); } if ($this->_value instanceof ExpressionInterface) { $callable($this->_value); $this->_value->traverse($callable); } foreach ($this->_valueExpressions as $v) { $callable($v); $v->traverse($callable); } } /** * Create a deep clone. * * Clones the field and value if they are expression objects. * * @return void */ public function __clone() { foreach (['_value', '_field'] as $prop) { if ($this->{$prop} instanceof ExpressionInterface) { $this->{$prop} = clone $this->{$prop}; } } } /** * Returns a template and a placeholder for the value after registering it * with the placeholder $generator * * @param \Cake\Database\ValueBinder $generator The value binder to use. * @return array First position containing the template and the second a placeholder */ protected function _stringExpression($generator) { $template = '%s '; if ($this->_field instanceof ExpressionInterface) { $template = '(%s) '; } if ($this->_isMultiple) { $template .= '%s (%s)'; $type = str_replace('[]', '', $this->_type); $value = $this->_flattenValue($this->_value, $generator, $type); // To avoid SQL errors when comparing a field to a list of empty values, // better just throw an exception here if ($value === '') { $field = $this->_field instanceof ExpressionInterface ? $this->_field->sql($generator) : $this->_field; throw new DatabaseException( "Impossible to generate condition with empty list of values for field ($field)" ); } } else { $template .= '%s %s'; $value = $this->_bindValue($this->_value, $generator, $this->_type); } return [$template, $value]; } /** * Registers a value in the placeholder generator and returns the generated placeholder * * @param mixed $value The value to bind * @param \Cake\Database\ValueBinder $generator The value binder to use * @param string $type The type of $value * @return string generated placeholder */ protected function _bindValue($value, $generator, $type) { $placeholder = $generator->placeholder('c'); $generator->bind($placeholder, $value, $type); return $placeholder; } /** * Converts a traversable value into a set of placeholders generated by * $generator and separated by `,` * * @param array|\Traversable $value the value to flatten * @param \Cake\Database\ValueBinder $generator The value binder to use * @param string|array|null $type the type to cast values to * @return string */ protected function _flattenValue($value, $generator, $type = 'string') { $parts = []; foreach ($this->_valueExpressions as $k => $v) { $parts[$k] = $v->sql($generator); unset($value[$k]); } if (!empty($value)) { $parts += $generator->generateManyNamed($value, $type); } return implode(',', $parts); } /** * Returns an array with the original $values in the first position * and all ExpressionInterface objects that could be found in the second * position. * * @param array|\Traversable $values The rows to insert * @return array */ protected function _collectExpressions($values) { if ($values instanceof ExpressionInterface) { return [$values, []]; } $expressions = $result = []; $isArray = is_array($values); if ($isArray) { $result = $values; } foreach ($values as $k => $v) { if ($v instanceof ExpressionInterface) { $expressions[$k] = $v; } if ($isArray) { $result[$k] = $v; } } return [$result, $expressions]; } }