Skip to content

Commit

Permalink
Defrost the CakeExpression snow flake by replacing the rather unique …
Browse files Browse the repository at this point in the history
…way it dealt with types with the same technique used in other Expression classes.
  • Loading branch information
Walther Lalk committed Aug 13, 2014
1 parent 5cc3ef0 commit 83e09d8
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 92 deletions.
138 changes: 59 additions & 79 deletions src/Database/Expression/CaseExpression.php
Expand Up @@ -35,54 +35,64 @@ 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);
}
}

/**
* 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|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;
}
Expand All @@ -96,81 +106,49 @@ public function add($conditions = [], $trueValues = []) {
*
* @return void
*/
protected function _addExpressions($conditions, $trueValues) {
protected function _addExpressions($conditions, $values, $types) {
foreach ($conditions as $k => $c) {
$numericKey = is_numeric($k);

if ($numericKey && empty($c)) {
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;
}

/**
Expand Down Expand Up @@ -204,11 +182,13 @@ public function sql(ValueBinder $generator) {
$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);
Expand All @@ -219,17 +199,17 @@ public function sql(ValueBinder $generator) {
*
*/
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);
$c->traverse($visitor);
}
}
}
if ($this->_defaultValue instanceof ExpressionInterface) {
$visitor($this->_defaultValue);
$this->_defaultValue->traverse($visitor);
if ($this->_elseValue instanceof ExpressionInterface) {
$visitor($this->_elseValue);
$this->_elseValue->traverse($visitor);
}
}

Expand Down
13 changes: 8 additions & 5 deletions src/Database/Expression/QueryExpression.php
Expand Up @@ -269,14 +269,17 @@ public function in($field, $values, $type = null) {
/**
* 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));
}

/**
Expand Down
9 changes: 6 additions & 3 deletions tests/TestCase/Database/Expression/CaseExpressionTest.php
Expand Up @@ -32,14 +32,17 @@ public function testSqlOutput() {
$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()));
}

Expand Down
11 changes: 6 additions & 5 deletions tests/TestCase/Database/QueryTest.php
Expand Up @@ -2727,13 +2727,13 @@ public function testSqlCaseStatement() {
->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
Expand Down Expand Up @@ -2769,15 +2769,16 @@ public function testSqlCaseStatement() {
->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()
Expand Down

0 comments on commit 83e09d8

Please sign in to comment.