/src/Database/Expression/CaseExpression.php
https://github.com/LubosRemplik/cakephp · PHP · 249 lines · 127 code · 31 blank · 91 comment · 23 complexity · 726b0682355a5570838da4b0fdbb2115 MD5 · raw file
- <?php
- /**
- * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
- * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the LICENSE.txt
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- * @link https://cakephp.org CakePHP(tm) Project
- * @since 3.0.0
- * @license https://opensource.org/licenses/mit-license.php MIT License
- */
- namespace Cake\Database\Expression;
- use Cake\Database\ExpressionInterface;
- use Cake\Database\Type\ExpressionTypeCasterTrait;
- use Cake\Database\ValueBinder;
- /**
- * This class represents a SQL Case statement
- */
- class CaseExpression implements ExpressionInterface
- {
- use ExpressionTypeCasterTrait;
- /**
- * A list of strings or other expression objects that represent the conditions of
- * the case statement. For example one key of the array might look like "sum > :value"
- *
- * @var array
- */
- protected $_conditions = [];
- /**
- * Values that are associated with the conditions in the $_conditions array.
- * Each value represents the 'true' value for the condition with the corresponding key.
- *
- * @var array
- */
- protected $_values = [];
- /**
- * The `ELSE` value for the case statement. If null then no `ELSE` will be included.
- *
- * @var string|\Cake\Database\ExpressionInterface|array|null
- */
- protected $_elseValue;
- /**
- * Constructs the case expression
- *
- * @param array|\Cake\Database\ExpressionInterface $conditions The conditions to test. Must be a ExpressionInterface
- * instance, or an array of ExpressionInterface instances.
- * @param array|\Cake\Database\ExpressionInterface $values associative array of values to be associated with the conditions
- * passed in $conditions. If there are more $values than $conditions, the last $value is used as the `ELSE` value
- * @param array $types associative array of types to be associated with the values
- * passed in $values
- */
- public function __construct($conditions = [], $values = [], $types = [])
- {
- if (!empty($conditions)) {
- $this->add($conditions, $values, $types);
- }
- if (is_array($conditions) && is_array($values) && count($values) > count($conditions)) {
- end($values);
- $key = key($values);
- $this->elseValue($values[$key], isset($types[$key]) ? $types[$key] : null);
- }
- }
- /**
- * Adds one or more conditions and their respective true values to the case object.
- * Conditions must be a one dimensional array or a QueryExpression.
- * The trueValues must be a similar structure, but may contain a string value.
- *
- * @param array|\Cake\Database\ExpressionInterface $conditions Must be a ExpressionInterface instance, or an array of ExpressionInterface instances.
- * @param array|\Cake\Database\ExpressionInterface $values associative array of values of each condition
- * @param array $types associative array of types to be associated with the values
- *
- * @return $this
- */
- public function add($conditions = [], $values = [], $types = [])
- {
- if (!is_array($conditions)) {
- $conditions = [$conditions];
- }
- if (!is_array($values)) {
- $values = [$values];
- }
- if (!is_array($types)) {
- $types = [$types];
- }
- $this->_addExpressions($conditions, $values, $types);
- return $this;
- }
- /**
- * Iterates over the passed in conditions and ensures that there is a matching true value for each.
- * If no matching true value, then it is defaulted to '1'.
- *
- * @param array|\Cake\Database\ExpressionInterface $conditions Must be a ExpressionInterface instance, or an array of ExpressionInterface instances.
- * @param array|\Cake\Database\ExpressionInterface $values associative array of values of each condition
- * @param array $types associative array of types to be associated with the values
- *
- * @return void
- */
- protected function _addExpressions($conditions, $values, $types)
- {
- $rawValues = array_values($values);
- $keyValues = array_keys($values);
- foreach ($conditions as $k => $c) {
- $numericKey = is_numeric($k);
- if ($numericKey && empty($c)) {
- continue;
- }
- if (!$c instanceof ExpressionInterface) {
- continue;
- }
- $this->_conditions[] = $c;
- $value = isset($rawValues[$k]) ? $rawValues[$k] : 1;
- if ($value === 'literal') {
- $value = $keyValues[$k];
- $this->_values[] = $value;
- continue;
- }
- if ($value === 'identifier') {
- $value = new IdentifierExpression($keyValues[$k]);
- $this->_values[] = $value;
- continue;
- }
- $type = isset($types[$k]) ? $types[$k] : null;
- if ($type !== null && !$value instanceof ExpressionInterface) {
- $value = $this->_castToExpression($value, $type);
- }
- if ($value instanceof ExpressionInterface) {
- $this->_values[] = $value;
- continue;
- }
- $this->_values[] = ['value' => $value, 'type' => $type];
- }
- }
- /**
- * Sets the default value
- *
- * @param \Cake\Database\ExpressionInterface|string|array|null $value Value to set
- * @param string|null $type Type of value
- *
- * @return void
- */
- public function elseValue($value = null, $type = null)
- {
- if (is_array($value)) {
- end($value);
- $value = key($value);
- }
- if ($value !== null && !$value instanceof ExpressionInterface) {
- $value = $this->_castToExpression($value, $type);
- }
- if (!$value instanceof ExpressionInterface) {
- $value = ['value' => $value, 'type' => $type];
- }
- $this->_elseValue = $value;
- }
- /**
- * Compiles the relevant parts into sql
- *
- * @param array|string|\Cake\Database\ExpressionInterface $part The part to compile
- * @param \Cake\Database\ValueBinder $generator Sql generator
- *
- * @return string
- */
- protected function _compile($part, ValueBinder $generator)
- {
- if ($part instanceof ExpressionInterface) {
- $part = $part->sql($generator);
- } elseif (is_array($part)) {
- $placeholder = $generator->placeholder('param');
- $generator->bind($placeholder, $part['value'], $part['type']);
- $part = $placeholder;
- }
- return $part;
- }
- /**
- * Converts the Node into a SQL string fragment.
- *
- * @param \Cake\Database\ValueBinder $generator Placeholder generator object
- *
- * @return string
- */
- public function sql(ValueBinder $generator)
- {
- $parts = [];
- $parts[] = 'CASE';
- foreach ($this->_conditions as $k => $part) {
- $value = $this->_values[$k];
- $parts[] = 'WHEN ' . $this->_compile($part, $generator) . ' THEN ' . $this->_compile($value, $generator);
- }
- if ($this->_elseValue !== null) {
- $parts[] = 'ELSE';
- $parts[] = $this->_compile($this->_elseValue, $generator);
- }
- $parts[] = 'END';
- return implode(' ', $parts);
- }
- /**
- * {@inheritDoc}
- *
- */
- public function traverse(callable $visitor)
- {
- foreach (['_conditions', '_values'] as $part) {
- foreach ($this->{$part} as $c) {
- if ($c instanceof ExpressionInterface) {
- $visitor($c);
- $c->traverse($visitor);
- }
- }
- }
- if ($this->_elseValue instanceof ExpressionInterface) {
- $visitor($this->_elseValue);
- $this->_elseValue->traverse($visitor);
- }
- }
- }