Browse Source

Defrost the CakeExpression snow flake by replacing the rather unique way it dealt with types with the same technique used in other Expression classes.

Walther Lalk 11 years ago
parent
commit
83e09d8878

+ 59 - 79
src/Database/Expression/CaseExpression.php

@@ -35,33 +35,39 @@ class CaseExpression implements ExpressionInterface {
 
 /**
  * Values that are associated with the conditions in the $_conditions array.
- * Each value represents the 'true' value for the condition with the corresponding key
+ * Each value represents the 'true' value for the condition with the corresponding key.
  *
  * @var array
  */
-	protected $_trueValues = [];
+	protected $_values = [];
 
 /**
- * The value to be used if none of the conditions match
+ * The `ELSE` value for the case statement. If null then no `ELSE` will be included.
  *
- * @var array|ExpressionInterface|string
+ * @var string|ExpressionInterface|array|null
  */
-	protected $_defaultValue;
+	protected $_elseValue = null;
 
 /**
  * Constructs the case expression
  *
- * @param array|ExpressionInterface $conditions The conditions to test.
- *                                          Must be a QueryExpression, or an array of QueryExpressions.
- * @param string|array|ExpressionInterface $trueValues Value of each condition if that condition is true
- * @param string|array|ExpressionInterface $defaultValue Default value if none of the conditiosn are true
+ * @param array|ExpressionInterface $conditions The conditions to test. Must be a ExpressionInterface
+ * instance,or an array of ExpressionInterface instances.
+ * @param array|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 = [], $trueValues = [], $defaultValue = 0) {
+	public function __construct($conditions = [], $values = [], $types = []) {
 		if (!empty($conditions)) {
-			$this->add($conditions, $trueValues);
+			$this->add($conditions, $values, $types);
 		}
 
-		$this->_defaultValue = $this->_parseValue($defaultValue);
+		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);
+		}
 	}
 
 /**
@@ -69,20 +75,24 @@ class CaseExpression implements ExpressionInterface {
  * 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|ExpressionInterface $conditions Must be a QueryExpression, or an array of QueryExpressions.
- * @param string|array|ExpressionInterface $trueValues Values of each condition if that condition is true
+ * @param array|ExpressionInterface $conditions Must be a ExpressionInterface instance, or an array of ExpressionInterface instances.
+ * @param array|ExpressionInterface $values associtative 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 = [], $trueValues = []) {
+	public function add($conditions = [], $values = [], $types = []) {
 		if (!is_array($conditions)) {
 			$conditions = [$conditions];
 		}
-		if (!is_array($trueValues)) {
-			$trueValues = [$trueValues];
+		if (!is_array($values)) {
+			$values = [$values];
+		}
+		if (!is_array($types)) {
+			$types = [$types];
 		}
 
-		$this->_addExpressions($conditions, $trueValues);
+		$this->_addExpressions($conditions, $values, $types);
 
 		return $this;
 	}
@@ -96,7 +106,7 @@ class CaseExpression implements ExpressionInterface {
  *
  * @return void
  */
-	protected function _addExpressions($conditions, $trueValues) {
+	protected function _addExpressions($conditions, $values, $types) {
 		foreach ($conditions as $k => $c) {
 			$numericKey = is_numeric($k);
 
@@ -104,73 +114,41 @@ class CaseExpression implements ExpressionInterface {
 				continue;
 			}
 
-			if (!$c instanceof QueryExpression) {
+			if (!$c instanceof ExpressionInterface) {
 				continue;
 			}
+			array_push($this->_conditions, $c);
 
-			$trueValue = !empty($trueValues[$k]) ? $trueValues[$k] : 1;
+			$value = !empty($values[$k]) ? $values[$k] : 1;
 
-			if ($trueValue === 'literal') {
-				$trueValue = $k;
-			} elseif (is_string($trueValue) || is_numeric($trueValue)) {
-				$trueValue = $this->_parseValue($trueValue);
+			if ($value === 'literal') {
+				$value = $k;
+				array_push($this->_values, $value);
+				continue;
 			}
 
-			$this->_conditions[] = $c;
-			$this->_trueValues[] = $trueValue;
-		}
-	}
-
-/**
- * Gets/sets the default value part
- *
- * @param array|string|ExpressionInterface $value Value to set
- *
- * @return array|string|ExpressionInterface
- */
-	public function defaultValue($value = null) {
-		if ($value !== null) {
-			$this->_defaultValue = $this->_parseValue($value);
-		}
-
-		return $this->_defaultValue;
-	}
-
-/**
- * Parses the value into a understandable format
- *
- * @param array|string|ExpressionInterface $value The value to parse
- *
- * @return array|string|ExpressionInterface
- */
-	protected function _parseValue($value) {
-		if (is_string($value) || is_numeric($value)) {
-			$value = [
-				'value' => $value,
-				'type' => is_string($value) ? null : $this->_getType($value)
-			];
-		} elseif (is_array($value) && !isset($value['value'])) {
-			$value = array_keys($value);
-			$value = end($value);
+			$type = isset($types[$k]) ? $types[$k] : null;
+			array_push($this->_values, ['value' => $value, 'type' => $type]);
 		}
-		return $value;
 	}
 
 /**
- * Gets the correct type for the value
+ * Sets the default value
  *
- * @param mixed $value The value to test
+ * @param $value Value to set
+ * @param $type Type of value
  *
- * @return null|string
+ * @return void
  */
-	protected function _getType($value) {
-		if (is_integer($value)) {
-			return 'integer';
-		} elseif (is_float($value)) {
-			return 'float';
+	public function elseValue($value = null, $type = null) {
+		if (is_array($value)) {
+			end($value);
+			$value = key($value);
+		} elseif ($value !== null && !$value instanceof ExpressionInterface) {
+			$value = ['value' => $value, 'type' => $type];
 		}
 
-		return null;
+		$this->_elseValue = $value;
 	}
 
 /**
@@ -204,11 +182,13 @@ class CaseExpression implements ExpressionInterface {
 		$parts = [];
 		$parts[] = 'CASE';
 		foreach ($this->_conditions as $k => $part) {
-			$trueValue = $this->_trueValues[$k];
-			$parts[] = 'WHEN ' . $this->_compile($part, $generator) . ' THEN ' . $this->_compile($trueValue, $generator);
+			$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[] = 'ELSE';
-		$parts[] = $this->_compile($this->_defaultValue, $generator);
 		$parts[] = 'END';
 
 		return implode(' ', $parts);
@@ -219,7 +199,7 @@ class CaseExpression implements ExpressionInterface {
  *
  */
 	public function traverse(callable $visitor) {
-		foreach (['_conditions', '_trueValues'] as $part) {
+		foreach (['_conditions', '_values'] as $part) {
 			foreach ($this->{$part} as $c) {
 				if ($c instanceof ExpressionInterface) {
 					$visitor($c);
@@ -227,9 +207,9 @@ class CaseExpression implements ExpressionInterface {
 				}
 			}
 		}
-		if ($this->_defaultValue instanceof ExpressionInterface) {
-			$visitor($this->_defaultValue);
-			$this->_defaultValue->traverse($visitor);
+		if ($this->_elseValue instanceof ExpressionInterface) {
+			$visitor($this->_elseValue);
+			$this->_elseValue->traverse($visitor);
 		}
 	}
 

+ 8 - 5
src/Database/Expression/QueryExpression.php

@@ -269,14 +269,17 @@ class QueryExpression implements ExpressionInterface, Countable {
 /**
  * Adds a new case expression to the expression object
  *
- * @param array|ExpressionInterface $conditions The conditions to test. Must be a QueryExpression, or an array of QueryExpressions.
- * @param string|array|ExpressionInterface $trueValues Value of each condition if that condition is true
- * @param string|array|ExpressionInterface $defaultValue Default value if none of the conditiosn are true
+ * @param array|ExpressionInterface $conditions The conditions to test. Must be a ExpressionInterface
+ * instance,or an array of ExpressionInterface instances.
+ * @param array|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
  *
  * @return QueryExpression
  */
-	public function addCase($conditions, $trueValues = [], $defaultValue = 0) {
-		return $this->add(new CaseExpression($conditions, $trueValues, $defaultValue));
+	public function addCase($conditions, $values = [], $types = []) {
+		return $this->add(new CaseExpression($conditions, $values, $types));
 	}
 
 /**

+ 6 - 3
tests/TestCase/Database/Expression/CaseExpressionTest.php

@@ -32,14 +32,17 @@ class CaseExpressionTest extends TestCase {
 		$expr = new QueryExpression();
 		$expr->eq('test', 'true');
 		$caseExpression = new CaseExpression($expr, 'foobar');
-
-		$expected = 'CASE WHEN test = :c0 THEN :c1 ELSE :c2 END';
+		$expected = 'CASE WHEN test = :c0 THEN :c1 END';
 		$this->assertSame($expected, $caseExpression->sql(new ValueBinder()));
 
 		$expr2 = new QueryExpression();
 		$expr2->eq('test2', 'false');
 		$caseExpression->add($expr2);
-		$expected = 'CASE WHEN test = :c0 THEN :c1 WHEN test2 = :c2 THEN :c3 ELSE :c4 END';
+		$expected = 'CASE WHEN test = :c0 THEN :c1 WHEN test2 = :c2 THEN :c3 END';
+		$this->assertSame($expected, $caseExpression->sql(new ValueBinder()));
+
+		$caseExpression = new CaseExpression([$expr], ['foobar', 'else']);
+		$expected = 'CASE WHEN test = :c0 THEN :c1 ELSE :c2 END';
 		$this->assertSame($expected, $caseExpression->sql(new ValueBinder()));
 	}
 

+ 6 - 5
tests/TestCase/Database/QueryTest.php

@@ -2727,13 +2727,13 @@ class QueryTest extends TestCase {
 			->newExpr()
 			->addCase($query
 				->newExpr()
-				->add(['published' => 'Y'])
+				->add(['published' => 'Y']), 1, 'integer'
 			);
 		$notPublishedCase = $query
 			->newExpr()
 			->addCase($query
 					->newExpr()
-					->add(['published' => 'N'])
+					->add(['published' => 'N']), 1, 'integer'
 			);
 
 		$results = $query
@@ -2769,15 +2769,16 @@ class QueryTest extends TestCase {
 				->newExpr()
 				->add(['published' => 'N'])
 		];
-		$trueValues = [
+		$values = [
 			'Published',
-			'Not published'
+			'Not published',
+			'None'
 		];
 		$results = $query
 			->select([
 				'id',
 				'comment',
-				'status' => $query->newExpr()->addCase($conditions, $trueValues, 'None')
+				'status' => $query->newExpr()->addCase($conditions, $values)
 			])
 			->from(['comments'])
 			->execute()