Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add case expression class for simple SQL case statements
- Loading branch information
Walther Lalk
committed
Aug 11, 2014
1 parent
ad2567f
commit 369d15d
Showing
3 changed files
with
260 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
<?php | ||
/** | ||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org) | ||
* Copyright (c) Cake Software Foundation, Inc. (http://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. (http://cakefoundation.org) | ||
* @link http://cakephp.org CakePHP(tm) Project | ||
* @since 3.0.0 | ||
* @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
*/ | ||
namespace Cake\Database\Expression; | ||
|
||
use Cake\Database\ExpressionInterface; | ||
use Cake\Database\Expression\QueryExpression; | ||
use Cake\Database\ValueBinder; | ||
|
||
/** | ||
* This class represents a SQL Case statement | ||
* | ||
* @internal | ||
*/ | ||
class CaseExpression implements ExpressionInterface { | ||
|
||
protected $_expression; | ||
|
||
protected $_isTrue; | ||
|
||
protected $_isFalse; | ||
|
||
/** | ||
* Constructs the case expression | ||
* | ||
* @param QueryExpression $expression The expression to test | ||
* @param mixed $isTrue Value if the expression is true | ||
* @param mixed $isFalse Value if the expression is false | ||
*/ | ||
public function __construct(QueryExpression $expression, $isTrue = 1, $isFalse = 0) { | ||
$this->_expression = $expression; | ||
$this->_isTrue = $this->_getValue($isTrue); | ||
$this->_isFalse = $this->_getValue($isFalse); | ||
} | ||
|
||
/** | ||
* Gets/sets the isTrue part | ||
* | ||
* @param mixed $value Value to set | ||
* | ||
* @return array|mixed | ||
*/ | ||
public function isTrue($value = null) { | ||
return $this->_part('isTrue', $value); | ||
} | ||
|
||
/** | ||
* Gets/sets the isFalse part | ||
* | ||
* @param mixed $value Value to set | ||
* | ||
* @return array|mixed | ||
* @codeCoverageIgnore | ||
*/ | ||
public function isFalse($value = null) { | ||
return $this->_part('isFalse', $value); | ||
} | ||
|
||
/** | ||
* Gets/sets the passed part | ||
* | ||
* @param string $part The part to get or set | ||
* @param mixed $value Value to set | ||
* | ||
* @return array|mixed | ||
*/ | ||
protected function _part($part, $value) { | ||
if ($value !== null) { | ||
$this->{'_' . $part} = $this->_getValue($value); | ||
} | ||
|
||
return $this->{'_' . $part}; | ||
} | ||
|
||
/** | ||
* Parses the value into a understandable format | ||
* | ||
* @param mixed $value The value to parse | ||
* | ||
* @return array|mixed | ||
*/ | ||
protected function _getValue($value) { | ||
if (is_string($value)) { | ||
$value = [ | ||
'value' => $value, | ||
'type' => null | ||
]; | ||
} elseif (is_array($value) && !isset($value['value'])) { | ||
$value = array_keys($value); | ||
$value = end($value); | ||
} | ||
return $value; | ||
} | ||
|
||
/** | ||
* Compiles the true or false part into sql | ||
* | ||
* @param mixed $part The part to compile | ||
* @param ValueBinder $generator Sql generator | ||
* | ||
* @return string | ||
*/ | ||
protected function _compile($part, ValueBinder $generator) { | ||
$part = $this->{'_' . $part}; | ||
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 WHEN'; | ||
$parts[] = $this->_expression->sql($generator); | ||
$parts[] = 'THEN'; | ||
$parts[] = $this->_compile('isTrue', $generator); | ||
$parts[] = 'ELSE'; | ||
$parts[] = $this->_compile('isFalse', $generator); | ||
$parts[] = 'END'; | ||
|
||
return implode(' ', $parts); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* | ||
*/ | ||
public function traverse(callable $visitor) { | ||
foreach (['_expression', '_isTrue', '_isFalse'] as $c) { | ||
if ($this->{$c} instanceof ExpressionInterface) { | ||
$visitor($this->{$c}); | ||
$this->{$c}->traverse($visitor); | ||
} | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
<?php | ||
/** | ||
* Project: hesa-mbit. | ||
* User: walther | ||
* Date: 2014/08/08 | ||
* Time: 9:37 AM | ||
*/ | ||
|
||
namespace Cake\Test\TestCase\Database\Expression; | ||
|
||
use Cake\Database\Expression\QueryExpression; | ||
use Cake\Database\ValueBinder; | ||
use Cake\TestSuite\TestCase; | ||
use Cake\Database\Expression\CaseExpression; | ||
|
||
/** | ||
* Tests CaseExpression class | ||
*/ | ||
class CaseExpressionTest extends TestCase { | ||
|
||
/** | ||
* Test that the sql output works correctly | ||
* | ||
* @return void | ||
*/ | ||
public function testSqlOutput() { | ||
$expr = new QueryExpression(); | ||
$expr->eq('test', 'true'); | ||
$caseExpression = new CaseExpression($expr); | ||
|
||
$this->assertInstanceOf('Cake\Database\ExpressionInterface', $caseExpression); | ||
$expected = 'CASE WHEN test = :c0 THEN 1 ELSE 0 END'; | ||
$this->assertSame($expected, $caseExpression->sql(new ValueBinder())); | ||
} | ||
|
||
/** | ||
* Test that we can pass in things as the isTrue/isFalse part | ||
* | ||
* @return void | ||
*/ | ||
public function testSetTrue() { | ||
$expr = new QueryExpression(); | ||
$expr->eq('test', 'true'); | ||
$caseExpression = new CaseExpression($expr); | ||
$expr2 = new QueryExpression(); | ||
|
||
$caseExpression->isTrue($expr2); | ||
$this->assertSame($expr2, $caseExpression->isTrue()); | ||
|
||
$caseExpression->isTrue('test_string'); | ||
$this->assertSame(['value' => 'test_string', 'type' => null], $caseExpression->isTrue()); | ||
|
||
$caseExpression->isTrue(['test_string' => 'literal']); | ||
$this->assertSame('test_string', $caseExpression->isTrue()); | ||
} | ||
|
||
/** | ||
* Test that things are compiled correctly | ||
* | ||
* @return void | ||
*/ | ||
public function testSqlCompiler() { | ||
$expr = new QueryExpression(); | ||
$expr->eq('test', 'true'); | ||
$caseExpression = new CaseExpression($expr); | ||
$expr2 = new QueryExpression(); | ||
$expr2->eq('test', 'false'); | ||
|
||
$caseExpression->isTrue($expr2); | ||
$this->assertSame('CASE WHEN test = :c0 THEN test = :c1 ELSE 0 END', $caseExpression->sql(new ValueBinder())); | ||
|
||
$caseExpression->isTrue('test_string'); | ||
$this->assertSame('CASE WHEN test = :c0 THEN :c1 ELSE 0 END', $caseExpression->sql(new ValueBinder())); | ||
|
||
$caseExpression->isTrue(['test_string' => 'literal']); | ||
$this->assertSame('CASE WHEN test = :c0 THEN test_string ELSE 0 END', $caseExpression->sql(new ValueBinder())); | ||
} | ||
|
||
/** | ||
* Tests that the expression is correctly traversed | ||
* | ||
* @return void | ||
*/ | ||
public function testTraverse() { | ||
$count = 0; | ||
$visitor = function() use (&$count) { | ||
$count++; | ||
}; | ||
|
||
$expr = new QueryExpression(); | ||
$expr->eq('test', 'true'); | ||
$caseExpression = new CaseExpression($expr); | ||
$caseExpression->traverse($visitor); | ||
$this->assertSame(2, $count); | ||
} | ||
} |