Skip to content

Commit

Permalink
Merge pull request #3133 from tigrang/type_mapping_objects
Browse files Browse the repository at this point in the history
Use objects for type maps and pass them to QueryExpressions (to be able to use default mapping from schema)
  • Loading branch information
markstory committed Apr 2, 2014
2 parents 2bd3326 + ea3d7a6 commit 10377c6
Show file tree
Hide file tree
Showing 13 changed files with 383 additions and 122 deletions.
34 changes: 20 additions & 14 deletions src/Database/Expression/QueryExpression.php
Expand Up @@ -16,6 +16,8 @@

use Cake\Database\ExpressionInterface;
use Cake\Database\Query;
use Cake\Database\TypeMap;
use Cake\Database\TypeMapTrait;
use Cake\Database\ValueBinder;
use \Countable;

Expand All @@ -27,6 +29,8 @@
*/
class QueryExpression implements ExpressionInterface, Countable {

use TypeMapTrait;

/**
* String to be used for joining each of the internal expressions
* this object internally stores for example "AND", "OR", etc.
Expand All @@ -52,16 +56,18 @@ class QueryExpression implements ExpressionInterface, Countable {
*
* @param array $conditions tree-like array structure containing all the conditions
* to be added or nested inside this expression object.
* @param array $types associative array of types to be associated with the values
* @param array|TypeMap $types associative array of types to be associated with the values
* passed in $conditions.
* @param string $conjunction the glue that will join all the string conditions at this
* level of the expression tree. For example "AND", "OR", "XOR"...
* @param TypeMap $typeMap contains default and call specific type mapping
* @see QueryExpression::add() for more details on $conditions and $types
*/
public function __construct($conditions = [], $types = [], $conjunction = 'AND') {
$this->typeMap($types);
$this->type(strtoupper($conjunction));
if (!empty($conditions)) {
$this->add($conditions, $types);
$this->add($conditions, $this->typeMap()->types());
}
}

Expand Down Expand Up @@ -281,9 +287,9 @@ public function notIn($field, $values, $type = null) {
*/
public function and_($conditions, $types = []) {
if (is_callable($conditions)) {
return $conditions(new self);
return $conditions(new self([], $this->typeMap()->types($types)));
}
return new self($conditions, $types);
return new self($conditions, $this->typeMap()->types($types));
}

/**
Expand All @@ -297,9 +303,9 @@ public function and_($conditions, $types = []) {
*/
public function or_($conditions, $types = []) {
if (is_callable($conditions)) {
return $conditions(new self([], [], 'OR'));
return $conditions(new self([], $this->typeMap()->types($types), 'OR'));
}
return new self($conditions, $types, 'OR');
return new self($conditions, $this->typeMap()->types($types), 'OR');
}
// @codingStandardsIgnoreEnd

Expand Down Expand Up @@ -412,6 +418,8 @@ public function iterateParts(callable $callable) {
protected function _addConditions(array $conditions, array $types) {
$operators = ['and', 'or', 'xor'];

$typeMap = $this->typeMap()->types($types);

foreach ($conditions as $k => $c) {
$numericKey = is_numeric($k);

Expand All @@ -425,12 +433,12 @@ protected function _addConditions(array $conditions, array $types) {
}

if ($numericKey && is_array($c) || in_array(strtolower($k), $operators)) {
$this->_conditions[] = new self($c, $types, $numericKey ? 'AND' : $k);
$this->_conditions[] = new self($c, $typeMap, $numericKey ? 'AND' : $k);
continue;
}

if (strtolower($k) === 'not') {
$this->_conditions[] = new UnaryExpression(new self($c, $types), [], 'NOT');
$this->_conditions[] = new UnaryExpression(new self($c, $typeMap), [], 'NOT');
continue;
}

Expand All @@ -440,7 +448,7 @@ protected function _addConditions(array $conditions, array $types) {
}

if (!$numericKey) {
$this->_conditions[] = $this->_parseCondition($k, $c, $types);
$this->_conditions[] = $this->_parseCondition($k, $c);
}
}
}
Expand All @@ -455,11 +463,9 @@ protected function _addConditions(array $conditions, array $types) {
* @param string $field The value from with the actual field and operator will
* be extracted.
* @param mixed $value The value to be bound to a placeholder for the field
* @param array $types List of types where the field can be found so the value
* can be converted accordingly.
* @return string|QueryExpression
*/
protected function _parseCondition($field, $value, $types) {
protected function _parseCondition($field, $value) {
$operator = '=';
$expression = $field;
$parts = explode(' ', trim($field), 2);
Expand All @@ -468,7 +474,7 @@ protected function _parseCondition($field, $value, $types) {
list($expression, $operator) = $parts;
}

$type = isset($types[$expression]) ? $types[$expression] : null;
$type = $this->typeMap()->type($expression);
$multi = false;

$typeMultiple = strpos($type, '[]') !== false;
Expand Down Expand Up @@ -505,4 +511,4 @@ protected function _bindMultiplePlaceholders($field, $values, $type) {
return implode(', ', $params);
}

}
}
18 changes: 7 additions & 11 deletions src/Database/Expression/ValuesExpression.php
Expand Up @@ -16,6 +16,7 @@

use Cake\Database\ExpressionInterface;
use Cake\Database\Query;
use Cake\Database\TypeMapTrait;
use Cake\Database\ValueBinder;
use Cake\Error;
use \Countable;
Expand All @@ -28,6 +29,8 @@
*/
class ValuesExpression implements ExpressionInterface {

use TypeMapTrait;

/**
* Array of values to insert.
*
Expand All @@ -42,13 +45,6 @@ class ValuesExpression implements ExpressionInterface {
*/
protected $_columns = [];

/**
* List of column types.
*
* @var array
*/
protected $_types = [];

/**
* The Query object to use as a values expression
*
Expand All @@ -60,11 +56,11 @@ class ValuesExpression implements ExpressionInterface {
* Constructor
*
* @param array $columns The list of columns that are going to be part of the values.
* @param array $types A dictionary of column -> type names
* @param TypeMap $types A dictionary of column -> type names
*/
public function __construct(array $columns, array $types = []) {
public function __construct(array $columns, $typeMap) {
$this->_columns = $columns;
$this->_types = $types;
$this->typeMap($typeMap);
}

/**
Expand Down Expand Up @@ -152,7 +148,7 @@ public function sql(ValueBinder $generator) {
foreach ($this->_values as $row) {
$row = array_merge($defaults, $row);
foreach ($row as $column => $value) {
$type = isset($this->_types[$column]) ? $this->_types[$column] : null;
$type = $this->typeMap()->type($column);
$generator->bind($i++, $value, $type);
}
}
Expand Down
60 changes: 13 additions & 47 deletions src/Database/Query.php
Expand Up @@ -33,6 +33,8 @@
*/
class Query implements ExpressionInterface, IteratorAggregate {

use TypeMapTrait;

/**
* Connection instance to be used to execute this query.
*
Expand Down Expand Up @@ -128,15 +130,6 @@ class Query implements ExpressionInterface, IteratorAggregate {
*/
protected $_iterator;

/**
* Associative array with the default fields and their types this query might contain
* used to avoid repetition when calling multiple times functions inside this class that
* may require a custom type for a specific field.
*
* @var array
*/
protected $_defaultTypes = [];

/**
* The object responsible for generating query placeholders and temporarily store values
* associated to each of those.
Expand Down Expand Up @@ -665,7 +658,6 @@ public function join($tables = null, $types = [], $overwrite = false) {
$tables = [$tables];
}

$types += $this->defaultTypes();
$joins = [];
$i = count($this->_parts['join']);
foreach ($tables as $alias => $t) {
Expand Down Expand Up @@ -854,7 +846,7 @@ public function where($conditions = null, $types = [], $overwrite = false) {
if ($overwrite) {
$this->_parts['where'] = $this->newExpr();
}
$this->_conjugate('where', $conditions, 'AND', $types + $this->defaultTypes());
$this->_conjugate('where', $conditions, 'AND', $types);
return $this;
}

Expand Down Expand Up @@ -915,7 +907,7 @@ public function where($conditions = null, $types = [], $overwrite = false) {
* @return Query
*/
public function andWhere($conditions, $types = []) {
$this->_conjugate('where', $conditions, 'AND', $types + $this->defaultTypes());
$this->_conjugate('where', $conditions, 'AND', $types);
return $this;
}

Expand Down Expand Up @@ -976,7 +968,7 @@ public function andWhere($conditions, $types = []) {
* @return Query
*/
public function orWhere($conditions, $types = []) {
$this->_conjugate('where', $conditions, 'OR', $types + $this->defaultTypes());
$this->_conjugate('where', $conditions, 'OR', $types);
return $this;
}

Expand Down Expand Up @@ -1081,7 +1073,7 @@ public function having($conditions = null, $types = [], $overwrite = false) {
if ($overwrite) {
$this->_parts['having'] = $this->newExpr();
}
$this->_conjugate('having', $conditions, 'AND', $types + $this->defaultTypes());
$this->_conjugate('having', $conditions, 'AND', $types);
return $this;
}

Expand All @@ -1097,7 +1089,7 @@ public function having($conditions = null, $types = [], $overwrite = false) {
* @return Query
*/
public function andHaving($conditions, $types = []) {
$this->_conjugate('having', $conditions, 'AND', $types + $this->defaultTypes());
$this->_conjugate('having', $conditions, 'AND', $types);
return $this;
}

Expand All @@ -1113,7 +1105,7 @@ public function andHaving($conditions, $types = []) {
* @return Query
*/
public function orHaving($conditions, $types = []) {
$this->_conjugate('having', $conditions, 'OR', $types + $this->defaultTypes());
$this->_conjugate('having', $conditions, 'OR', $types);
return $this;
}

Expand Down Expand Up @@ -1343,7 +1335,7 @@ public function insert($columns, $types = []) {
$this->_parts['insert'][1] = $columns;

if (!$this->_parts['values']) {
$this->_parts['values'] = new ValuesExpression($columns, $types + $this->defaultTypes());
$this->_parts['values'] = new ValuesExpression($columns, $this->typeMap()->types($types));
}

return $this;
Expand Down Expand Up @@ -1429,14 +1421,14 @@ public function set($key, $value = null, $types = []) {

if (is_array($key) || $key instanceof ExpressionInterface) {
$types = (array)$value;
$this->_parts['set']->add($key, $types + $this->defaultTypes());
$this->_parts['set']->add($key, $types);
return $this;
}

if (is_string($types) && is_string($key)) {
$types = [$key => $types];
}
$this->_parts['set']->eq($key, $value, $types + $this->defaultTypes());
$this->_parts['set']->eq($key, $value, $types);

return $this;
}
Expand Down Expand Up @@ -1498,7 +1490,7 @@ public function type() {
* @return QueryExpression
*/
public function newExpr() {
return new QueryExpression;
return new QueryExpression([], $this->typeMap());
}

/**
Expand Down Expand Up @@ -1643,32 +1635,6 @@ public function traverseExpressions(callable $callback) {
return $this->traverse($visitor);
}

/**
* Configures a map of default fields and their associated types to be
* used as the default list of types for every function in this class
* with a $types param. Useful to avoid repetition when calling the same
* functions using the same fields and types.
*
* If called with no arguments it will return the currently configured types.
*
* ## Example
*
* {{{
* $query->defaultTypes(['created' => 'datetime', 'is_visible' => 'boolean']);
* }}}
*
* @param array $types associative array where keys are field names and values
* are the correspondent type.
* @return Query|array
*/
public function defaultTypes(array $types = null) {
if ($types === null) {
return $this->_defaultTypes;
}
$this->_defaultTypes = $types;
return $this;
}

/**
* Associates a query placeholder to a value and a type.
*
Expand Down Expand Up @@ -1835,7 +1801,7 @@ public function __debugInfo() {
return [
'sql' => $this->sql(),
'params' => $this->valueBinder()->bindings(),
'defaultTypes' => $this->_defaultTypes,
'defaultTypes' => $this->defaultTypes(),
'decorators' => count($this->_resultDecorators),
'executed' => $this->_iterator ? true : false
];
Expand Down

0 comments on commit 10377c6

Please sign in to comment.