Skip to content

Commit

Permalink
Making Comparison correctly traverse expressions in array values
Browse files Browse the repository at this point in the history
Started testing the type conversion to expressions
  • Loading branch information
lorenzo committed Mar 26, 2016
1 parent 3158cf6 commit 98a5267
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 5 deletions.
3 changes: 3 additions & 0 deletions src/Database/Expression/CaseExpression.php
Expand Up @@ -15,6 +15,7 @@
namespace Cake\Database\Expression; namespace Cake\Database\Expression;


use Cake\Database\ExpressionInterface; use Cake\Database\ExpressionInterface;
use Cake\Database\Type\TypeExpressionCasterTrait;
use Cake\Database\ValueBinder; use Cake\Database\ValueBinder;


/** /**
Expand All @@ -25,6 +26,8 @@
class CaseExpression implements ExpressionInterface class CaseExpression implements ExpressionInterface
{ {


use TypeExpressionCasterTrait;

/** /**
* A list of strings or other expression objects that represent the conditions of * 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" * the case statement. For example one key of the array might look like "sum > :value"
Expand Down
39 changes: 37 additions & 2 deletions src/Database/Expression/Comparison.php
Expand Up @@ -53,6 +53,10 @@ class Comparison implements ExpressionInterface, FieldInterface
*/ */
protected $_operator; protected $_operator;


protected $_isMultiple = false;

protected $_valueExpressions = [];

/** /**
* Constructor * Constructor
* *
Expand Down Expand Up @@ -80,10 +84,18 @@ public function __construct($field, $value, $type, $operator)
*/ */
public function setValue($value) public function setValue($value)
{ {
if (isset($this->_type)) { $hasType = isset($this->_type);
$isMultiple = $hasType && strpos($this->_type, '[]') !== false;

if ($hasType) {
$value = $this->_castToExpression($value, $this->_type); $value = $this->_castToExpression($value, $this->_type);
} }


if ($isMultiple) {
$this->_valueExpressions = $this->_collectExpressions($value);
}

$this->_isMultiple = $isMultiple;
$this->_value = $value; $this->_value = $value;
} }


Expand Down Expand Up @@ -157,6 +169,13 @@ public function traverse(callable $callable)
$callable($this->_value); $callable($this->_value);
$this->_value->traverse($callable); $this->_value->traverse($callable);
} }

if (!empty($this->_valueExpressions)) {
foreach ($this->_valueExpressions as $v) {
$callable($v);
$v->traverse($callable);
}
}
} }


/** /**
Expand Down Expand Up @@ -190,7 +209,7 @@ protected function _stringExpression($generator)
$template = '(%s) '; $template = '(%s) ';
} }


if (strpos($this->_type, '[]') !== false) { if ($this->_isMultiple) {
$template .= '%s (%s)'; $template .= '%s (%s)';
$type = str_replace('[]', '', $this->_type); $type = str_replace('[]', '', $this->_type);
$value = $this->_flattenValue($this->_value, $generator, $type); $value = $this->_flattenValue($this->_value, $generator, $type);
Expand Down Expand Up @@ -239,9 +258,25 @@ protected function _flattenValue($value, $generator, $type = null)
{ {
$parts = []; $parts = [];
foreach ($value as $k => $v) { foreach ($value as $k => $v) {
if (isset($this->_valueExpressions[$k])) {
$parts[] = $this->_valueExpressions[$k]->sql($generator);
continue;
}
$parts[] = $this->_bindValue($v, $generator, $type); $parts[] = $this->_bindValue($v, $generator, $type);
} }


return implode(',', $parts); return implode(',', $parts);
} }

protected function _collectExpressions($values)
{
$result = [];
foreach ($values as $k => $v) {
if ($v instanceof ExpressionInterface) {
$result[$k] = $v;
}
}

return $result;
}
} }
3 changes: 2 additions & 1 deletion src/Database/Type/ExpressionTypeInterface.php
Expand Up @@ -28,7 +28,8 @@ interface ExpressionTypeInterface
* Returns an ExpressionInterface object for the given value that can * Returns an ExpressionInterface object for the given value that can
* be used in queries. * be used in queries.
* *
* @param mixed $value The value to be converted to an expression
* @return \Cake\Database\ExpressionInterface * @return \Cake\Database\ExpressionInterface
*/ */
public function toExpression($value, Driver $driver); public function toExpression($value);
} }
4 changes: 2 additions & 2 deletions src/Database/Type/TypeExpressionCasterTrait.php
Expand Up @@ -26,15 +26,15 @@ trait TypeExpressionCasterTrait


protected function _castToExpression($value, $type) protected function _castToExpression($value, $type)
{ {
return $value;
$baseType = str_replace('[]', '', $type); $baseType = str_replace('[]', '', $type);
$multi = $type !== $baseType;
$converter = Type::build($baseType); $converter = Type::build($baseType);


if (!$converter instanceof ExpressionTypeInterface) { if (!$converter instanceof ExpressionTypeInterface) {
return $value; return $value;
} }


$multi = $type !== $baseType;

if ($multi) { if ($multi) {
$result = []; $result = [];
foreach ($value as $k => $v) { foreach ($value as $k => $v) {
Expand Down
71 changes: 71 additions & 0 deletions tests/TestCase/Database/ExpressionTypeCastingTest.php
@@ -0,0 +1,71 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The Open Group Test Suite License
* 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.3.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace Cake\Test\TestCase\Database;

use Cake\TestSuite\TestCase;
use Cake\Database\Type\StringType;
use Cake\Database\Type\ExpressionTypeInterface;
use Cake\Database\Expression\FunctionExpression;
use Cake\Database\ValueBinder;
use Cake\Database\Type;
use Cake\Database\Expression\Comparison;

class TestType extends StringType implements ExpressionTypeInterface
{

public function toExpression($value)
{
return new FunctionExpression('CONCAT', [$value, ' - foo']);
}
}

/**
* Tests for Expression objects casting values to other expressions
* using the type classes
*
*/
class FunctionsBuilderTest extends TestCase
{

/**
* Setups a mock for FunctionsBuilder
*
* @return void
*/
public function setUp()
{
parent::setUp();
Type::map('test', new TestType);
}

public function testComparisonSimple()
{
$comparison = new Comparison('field', 'the thing', 'test', '=');
$binder = new ValueBinder;
$sql = $comparison->sql($binder);
$this->assertEquals('field = (CONCAT(:c0, :c1))', $sql);
$this->assertEquals('the thing', $binder->bindings()[':c0']['value']);
}

public function testComparisonMultiple()
{
$comparison = new Comparison('field', ['2', '3'], 'test[]', 'IN');
$binder = new ValueBinder;
$sql = $comparison->sql($binder);
$this->assertEquals('field IN (CONCAT(:c0, :c1),CONCAT(:c2, :c3))', $sql);
$this->assertEquals('2', $binder->bindings()[':c0']['value']);
$this->assertEquals('3', $binder->bindings()[':c2']['value']);
}

}

0 comments on commit 98a5267

Please sign in to comment.