Browse Source

Add TypeMap for expressions
Pass TypeMap to query expressions

Tigran Gabrielyan 12 years ago
parent
commit
cd7065944b

+ 20 - 9
src/Database/Expression/QueryExpression.php

@@ -16,6 +16,8 @@ namespace Cake\Database\Expression;
 
 use Cake\Database\ExpressionInterface;
 use Cake\Database\Query;
+use Cake\Database\TypeMap;
+use Cake\Database\TypeMapTrait;
 use Cake\Database\ValueBinder;
 use \Countable;
 
@@ -27,6 +29,8 @@ use \Countable;
  */
 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.
@@ -52,16 +56,19 @@ 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') {
+		$typeMap = ($types instanceof TypeMap) ? $types : new TypeMap($types);
+		$this->typeMap($typeMap);
 		$this->type(strtoupper($conjunction));
 		if (!empty($conditions)) {
-			$this->add($conditions, $types);
+			$this->add($conditions);
 		}
 	}
 
@@ -281,9 +288,9 @@ class QueryExpression implements ExpressionInterface, Countable {
  */
 	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));
 	}
 
 /**
@@ -297,9 +304,9 @@ class QueryExpression implements ExpressionInterface, Countable {
  */
 	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
 
@@ -425,12 +432,12 @@ class QueryExpression implements ExpressionInterface, Countable {
 			}
 
 			if ($numericKey && is_array($c) || in_array(strtolower($k), $operators)) {
-				$this->_conditions[] = new self($c, $types, $numericKey ? 'AND' : $k);
+				$this->_conditions[] = new self($c, $this->typeMap()->types($types), $numericKey ? 'AND' : $k);
 				continue;
 			}
 
 			if (strtolower($k) === 'not') {
-				$this->_conditions[] = new UnaryExpression(new self($c, $types), [], 'NOT');
+				$this->_conditions[] = new UnaryExpression(new self($c, $this->typeMap()->types($types)), [], 'NOT');
 				continue;
 			}
 
@@ -467,8 +474,12 @@ class QueryExpression implements ExpressionInterface, Countable {
 		if (count($parts) > 1) {
 			list($expression, $operator) = $parts;
 		}
+		
+		if (!empty($types)) {
+			$this->typeMap()->types($types);
+		}
 
-		$type = isset($types[$expression]) ? $types[$expression] : null;
+		$type = $this->typeMap()->type($expression);
 		$multi = false;
 
 		$typeMultiple = strpos($type, '[]') !== false;

+ 7 - 11
src/Database/Expression/ValuesExpression.php

@@ -16,6 +16,7 @@ namespace Cake\Database\Expression;
 
 use Cake\Database\ExpressionInterface;
 use Cake\Database\Query;
+use Cake\Database\TypeMapTrait;
 use Cake\Database\ValueBinder;
 use Cake\Error;
 use \Countable;
@@ -28,6 +29,8 @@ use \Countable;
  */
 class ValuesExpression implements ExpressionInterface {
 
+	use TypeMapTrait;
+
 /**
  * Array of values to insert.
  *
@@ -43,13 +46,6 @@ class ValuesExpression implements ExpressionInterface {
 	protected $_columns = [];
 
 /**
- * List of column types.
- *
- * @var array
- */
-	protected $_types = [];
-
-/**
  * The Query object to use as a values expression
  *
  * @var \Cake\Database\Query
@@ -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);
 	}
 
 /**
@@ -152,7 +148,7 @@ class ValuesExpression implements ExpressionInterface {
 		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);
 			}
 		}

+ 13 - 47
src/Database/Query.php

@@ -33,6 +33,8 @@ use IteratorAggregate;
  */
 class Query implements ExpressionInterface, IteratorAggregate {
 
+	use TypeMapTrait;
+
 /**
  * Connection instance to be used to execute this query.
  *
@@ -129,15 +131,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.
  *
@@ -665,7 +658,6 @@ class Query implements ExpressionInterface, IteratorAggregate {
 			$tables = [$tables];
 		}
 
-		$types += $this->defaultTypes();
 		$joins = [];
 		$i = count($this->_parts['join']);
 		foreach ($tables as $alias => $t) {
@@ -854,7 +846,7 @@ class Query implements ExpressionInterface, IteratorAggregate {
 		if ($overwrite) {
 			$this->_parts['where'] = $this->newExpr();
 		}
-		$this->_conjugate('where', $conditions, 'AND', $types + $this->defaultTypes());
+		$this->_conjugate('where', $conditions, 'AND', $types);
 		return $this;
 	}
 
@@ -915,7 +907,7 @@ class Query implements ExpressionInterface, IteratorAggregate {
  * @return Query
  */
 	public function andWhere($conditions, $types = []) {
-		$this->_conjugate('where', $conditions, 'AND', $types + $this->defaultTypes());
+		$this->_conjugate('where', $conditions, 'AND', $types);
 		return $this;
 	}
 
@@ -976,7 +968,7 @@ class Query implements ExpressionInterface, IteratorAggregate {
  * @return Query
  */
 	public function orWhere($conditions, $types = []) {
-		$this->_conjugate('where', $conditions, 'OR', $types + $this->defaultTypes());
+		$this->_conjugate('where', $conditions, 'OR', $types);
 		return $this;
 	}
 
@@ -1081,7 +1073,7 @@ class Query implements ExpressionInterface, IteratorAggregate {
 		if ($overwrite) {
 			$this->_parts['having'] = $this->newExpr();
 		}
-		$this->_conjugate('having', $conditions, 'AND', $types + $this->defaultTypes());
+		$this->_conjugate('having', $conditions, 'AND', $types);
 		return $this;
 	}
 
@@ -1097,7 +1089,7 @@ class Query implements ExpressionInterface, IteratorAggregate {
  * @return Query
  */
 	public function andHaving($conditions, $types = []) {
-		$this->_conjugate('having', $conditions, 'AND', $types + $this->defaultTypes());
+		$this->_conjugate('having', $conditions, 'AND', $types);
 		return $this;
 	}
 
@@ -1113,7 +1105,7 @@ class Query implements ExpressionInterface, IteratorAggregate {
  * @return Query
  */
 	public function orHaving($conditions, $types = []) {
-		$this->_conjugate('having', $conditions, 'OR', $types + $this->defaultTypes());
+		$this->_conjugate('having', $conditions, 'OR', $types);
 		return $this;
 	}
 
@@ -1343,7 +1335,7 @@ class Query implements ExpressionInterface, IteratorAggregate {
 		$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;
@@ -1429,14 +1421,14 @@ class Query implements ExpressionInterface, IteratorAggregate {
 
 		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;
 	}
@@ -1498,7 +1490,7 @@ class Query implements ExpressionInterface, IteratorAggregate {
  * @return QueryExpression
  */
 	public function newExpr() {
-		return new QueryExpression;
+		return new QueryExpression([], $this->typeMap());
 	}
 
 /**
@@ -1644,32 +1636,6 @@ class Query implements ExpressionInterface, IteratorAggregate {
 	}
 
 /**
- * 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.
  *
  * If type is expressed as "atype[]" (note braces) then it will cause the
@@ -1835,7 +1801,7 @@ class Query implements ExpressionInterface, IteratorAggregate {
 		return [
 			'sql' => $this->sql(),
 			'params' => $this->valueBinder()->bindings(),
-			'defaultTypes' => $this->_defaultTypes,
+			'defaultTypes' => $this->defaultTypes(),
 			'decorators' => count($this->_resultDecorators),
 			'executed' => $this->_iterator ? true : false
 		];

+ 76 - 0
src/Database/TypeMap.php

@@ -0,0 +1,76 @@
+<?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       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+namespace Cake\Database;
+
+class TypeMap {
+
+/**
+ * @var array
+ */
+	protected $_defaults;
+
+/**
+ * @var array
+ */
+	protected $_types = [];
+/**
+ * @param array $defaults
+ */
+	public function __construct(array $defaults = []) {
+			$this->defaults($defaults);
+	}
+
+/**
+ * Set/Get defaults
+ *
+ * @var this|array
+ */
+	public function defaults(array $defaults = null) {
+		if ($defaults === null) {
+				return $this->_defaults;
+		}
+		$this->_defaults = $defaults;
+		return $this;
+	}
+
+/**
+ * Set/Get types
+ *
+ * @var this|array
+ */
+	public function types(array $types = null) {
+		if ($types === null) {
+			return $this->_types;
+		}
+		$this->_types = $types;
+		return $this;
+	}
+
+/**
+ * Get column type
+ *
+ * @var string
+ */
+	public function type($column) {
+		if (isset($this->_types[$column])) {
+			return $this->_types[$column];
+		}
+		if (isset($this->_defaults[$column])) {
+			return $this->_defaults[$column];
+		}
+		return null;
+	}
+
+}

+ 55 - 0
src/Database/TypeMapTrait.php

@@ -0,0 +1,55 @@
+<?php
+/**
+ * PHP Version 5.4
+ *
+ * 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       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+namespace Cake\Database;
+
+use Cake\Database\TypeMap;
+
+trait TypeMapTrait {
+
+/**
+ * @var \Cake\Database\TypeMap
+ */
+	protected $_typeMap;
+
+/**
+ * Setter/Getter for type map
+ *
+ * @return this|TypeMap
+ */
+	public function typeMap($typeMap = null) {
+		if ($typeMap === null) {
+			$this->_typeMap = ($this->_typeMap) ?: new TypeMap();
+			return $this->_typeMap;
+		}
+		$this->_typeMap = $typeMap;
+		return $this;
+	}
+
+/**
+ * Allows setting default types when chaining query
+ *
+ * @return this|array
+ */
+	public function defaultTypes(array $types = null) {
+		if ($types === null) {
+			return $this->typeMap()->defaults();
+		}
+		$this->typeMap()->defaults($types);
+		return $this;
+	}
+
+}

+ 2 - 2
src/ORM/Query.php

@@ -102,7 +102,7 @@ class Query extends DatabaseQuery {
  * @param \Cake\ORM\Table $table
  */
 	public function __construct($connection, $table) {
-		$this->connection($connection);
+		parent::__construct($connection);
 		$this->repository($table);
 
 		if ($this->_repository) {
@@ -128,7 +128,7 @@ class Query extends DatabaseQuery {
 		foreach ($schema->columns() as $f) {
 			$fields[$f] = $fields[$alias . '.' . $f] = $schema->columnType($f);
 		}
-		$this->defaultTypes($this->defaultTypes() + $fields);
+		$this->defaultTypes($fields);
 
 		return $this;
 	}

+ 53 - 8
tests/TestCase/ORM/EagerLoaderTest.php

@@ -14,6 +14,7 @@
  */
 namespace Cake\Test\TestCase\ORM;
 
+use Cake\Database\TypeMap;
 use Cake\Database\Expression\IdentifierExpression;
 use Cake\Database\Expression\QueryExpression;
 use Cake\Datasource\ConnectionManager;
@@ -109,73 +110,117 @@ class EagerLoaderTest extends TestCase {
 
 		$query = $this->getMock('\Cake\ORM\Query', ['join'], [$this->connection, $this->table]);
 
+		$typeMap = new TypeMap([
+			'clients.id' => 'integer',
+			'id' => 'integer',
+			'clients.name' => 'string',
+			'name' => 'string',
+			'clients.phone' => 'string',
+			'phone' => 'string',
+		]);
+
+		$query->typeMap($typeMap);
+
 		$query->expects($this->at(0))->method('join')
 			->with(['clients' => [
 				'table' => 'clients',
 				'type' => 'LEFT',
 				'conditions' => new QueryExpression([
-					['clients.id' => new IdentifierExpression('foo.client_id')]
-				], ['clients.id' => 'integer'])
+					['clients.id' => new IdentifierExpression('foo.client_id')],
+				], $query->typeMap())
 			]])
 			->will($this->returnValue($query));
 
+		$typeMap = new TypeMap([
+			'orders.id' => 'integer',
+			'id' => 'integer',
+			'orders.total' => 'string',
+			'total' => 'string',
+			'orders.placed' => 'datetime',
+			'placed' => 'datetime',
+		]);
+
 		$query->expects($this->at(1))->method('join')
 			->with(['orders' => [
 				'table' => 'orders',
 				'type' => 'INNER',
 				'conditions' => new QueryExpression([
 					['clients.id' => new IdentifierExpression('orders.client_id')]
-				])
+				], $typeMap)
 			]])
 			->will($this->returnValue($query));
 
+		$typeMap = new TypeMap([
+			'orderTypes.id' => 'integer',
+			'id' => 'integer',
+		]);
+
 		$query->expects($this->at(2))->method('join')
 			->with(['orderTypes' => [
 				'table' => 'order_types',
 				'type' => 'LEFT',
 				'conditions' => new QueryExpression([
 					['orderTypes.id' => new IdentifierExpression('orders.order_type_id')]
-				], ['orderTypes.id' => 'integer'])
+				], $typeMap)
 			]])
 			->will($this->returnValue($query));
 
+		$typeMap = new TypeMap([
+			'stuff.id' => 'integer',
+			'id' => 'integer',
+		]);
+
 		$query->expects($this->at(3))->method('join')
 			->with(['stuff' => [
 				'table' => 'things',
 				'type' => 'INNER',
 				'conditions' => new QueryExpression([
 					['orders.id' => new IdentifierExpression('stuff.order_id')]
-				])
+				], $typeMap)
 			]])
 			->will($this->returnValue($query));
 
+		$typeMap = new TypeMap([
+			'stuffTypes.id' => 'integer',
+			'id' => 'integer',
+		]);
+
 		$query->expects($this->at(4))->method('join')
 			->with(['stuffTypes' => [
 				'table' => 'stuff_types',
 				'type' => 'LEFT',
 				'conditions' => new QueryExpression([
 					['stuffTypes.id' => new IdentifierExpression('stuff.stuff_type_id')]
-				], ['stuffTypes.id' => 'integer'])
+				], $typeMap)
 			]])
 			->will($this->returnValue($query));
 
+		$typeMap = new TypeMap([
+			'companies.id' => 'integer',
+			'id' => 'integer',
+		]);
+
 		$query->expects($this->at(5))->method('join')
 			->with(['companies' => [
 				'table' => 'organizations',
 				'type' => 'LEFT',
 				'conditions' => new QueryExpression([
 					['companies.id' => new IdentifierExpression('clients.organization_id')]
-				], ['companies.id' => 'integer'])
+				], $typeMap)
 			]])
 			->will($this->returnValue($query));
 
+		$typeMap = new TypeMap([
+			'categories.id' => 'integer',
+			'id' => 'integer',
+		]);
 		$query->expects($this->at(6))->method('join')
 			->with(['categories' => [
 				'table' => 'categories',
 				'type' => 'LEFT',
 				'conditions' => new QueryExpression([
 					['categories.id' => new IdentifierExpression('companies.category_id')]
-				], ['categories.id' => 'integer'])
+				], $typeMap)
 			]])
 			->will($this->returnValue($query));
 

+ 5 - 0
tests/TestCase/ORM/QueryTest.php

@@ -17,6 +17,7 @@ namespace Cake\Test\TestCase\ORM;
 use Cake\Database\Expression\IdentifierExpression;
 use Cake\Database\Expression\OrderByExpression;
 use Cake\Database\Expression\QueryExpression;
+use Cake\Database\TypeMap;
 use Cake\Datasource\ConnectionManager;
 use Cake\ORM\Query;
 use Cake\ORM\ResultSet;
@@ -764,13 +765,16 @@ class QueryTest extends TestCase {
 
 		$this->assertEquals(['field_a', 'field_b'], $query->clause('select'));
 
+		$typeMap = new TypeMap(['foo.id' => 'integer', 'id' => 'integer']);
 		$expected = new QueryExpression($options['conditions']);
+		$expected->typeMap($typeMap);
 		$result = $query->clause('where');
 		$this->assertEquals($expected, $result);
 
 		$this->assertEquals(1, $query->clause('limit'));
 
 		$expected = new QueryExpression(['a > b']);
+		$expected->typeMap($typeMap);
 		$result = $query->clause('join');
 		$this->assertEquals([
 			'table_a' => ['alias' => 'table_a', 'type' => 'INNER', 'conditions' => $expected]
@@ -783,6 +787,7 @@ class QueryTest extends TestCase {
 		$this->assertEquals(['field_a'], $query->clause('group'));
 
 		$expected = new QueryExpression($options['having']);
+		$expected->typeMap($typeMap);
 		$this->assertEquals($expected, $query->clause('having'));
 
 		$expected = ['table_a' => ['table_b' => []]];

+ 81 - 14
tests/TestCase/ORM/TableTest.php

@@ -15,6 +15,7 @@
 namespace Cake\Test\TestCase\ORM;
 
 use Cake\Core\Configure;
+use Cake\Database\TypeMap;
 use Cake\Database\Expression\OrderByExpression;
 use Cake\Database\Expression\QueryExpression;
 use Cake\Datasource\ConnectionManager;
@@ -2037,7 +2038,19 @@ class TableTest extends \Cake\TestSuite\TestCase {
 
 		$result = $table->findByUsername('garrett');
 		$this->assertInstanceOf('Cake\ORM\Query', $result);
-		$expected = new QueryExpression(['username' => 'garrett'], ['username' => 'string']);
+		$typeMap = new TypeMap([
+			'Users.id' => 'integer',
+			'id' => 'integer',
+			'Users.username' => 'string',
+			'username' => 'string',
+			'Users.password' => 'string',
+			'password' => 'string',
+			'Users.created' => 'timestamp',
+			'created' => 'timestamp',
+			'Users.updated' => 'timestamp',
+			'updated' => 'timestamp',
+		]);
+		$expected = new QueryExpression(['username' => 'garrett'], $typeMap);
 		$this->assertEquals($expected, $result->clause('where'));
 	}
 
@@ -2090,11 +2103,20 @@ class TableTest extends \Cake\TestSuite\TestCase {
 
 		$result = $table->findByUsernameAndId('garrett', 4);
 		$this->assertInstanceOf('Cake\ORM\Query', $result);
-		$expected = new QueryExpression(
-			['username' => 'garrett', 'id' => 4],
-			['username' => 'string', 'id' => 'integer'],
-			'AND'
-		);
+
+		$typeMap = new TypeMap([
+			'Users.id' => 'integer',
+			'id' => 'integer',
+			'Users.username' => 'string',
+			'username' => 'string',
+			'Users.password' => 'string',
+			'password' => 'string',
+			'Users.created' => 'timestamp',
+			'created' => 'timestamp',
+			'Users.updated' => 'timestamp',
+			'updated' => 'timestamp',
+		]);
+		$expected = new QueryExpression(['username' => 'garrett', 'id' => 4], $typeMap);
 		$this->assertEquals($expected, $result->clause('where'));
 	}
 
@@ -2108,13 +2130,25 @@ class TableTest extends \Cake\TestSuite\TestCase {
 
 		$result = $table->findByUsernameOrId('garrett', 4);
 		$this->assertInstanceOf('Cake\ORM\Query', $result);
-		$expected = new QueryExpression();
+
+		$typeMap = new TypeMap([
+			'Users.id' => 'integer',
+			'id' => 'integer',
+			'Users.username' => 'string',
+			'username' => 'string',
+			'Users.password' => 'string',
+			'password' => 'string',
+			'Users.created' => 'timestamp',
+			'created' => 'timestamp',
+			'Users.updated' => 'timestamp',
+			'updated' => 'timestamp',
+		]);
+		$expected = new QueryExpression([], $typeMap);
 		$expected->add([
 			'OR' => [
 				'username' => 'garrett',
 				'id' => 4
-			]],
-			['username' => 'string', 'id' => 'integer']
+			]]
 		);
 		$this->assertEquals($expected, $result->clause('where'));
 	}
@@ -2130,11 +2164,20 @@ class TableTest extends \Cake\TestSuite\TestCase {
 		$result = $table->findAllByAuthorId(1);
 		$this->assertInstanceOf('Cake\ORM\Query', $result);
 		$this->assertNull($result->clause('limit'));
-		$expected = new QueryExpression(
-			['author_id' => 1],
-			['author_id' => 'integer'],
-			'AND'
-		);
+
+		$typeMap = new TypeMap([
+			'Articles.id' => 'integer',
+			'id' => 'integer',
+			'Articles.author_id' => 'integer',
+			'author_id' => 'integer',
+			'Articles.title' => 'string',
+			'title' => 'string',
+			'Articles.body' => 'text',
+			'body' => 'text',
+			'Articles.published' => 'string',
+			'published' => 'string',
+		]);
+		$expected = new QueryExpression(['author_id' => 1], $typeMap);
 		$this->assertEquals($expected, $result->clause('where'));
 	}
 
@@ -2152,6 +2195,18 @@ class TableTest extends \Cake\TestSuite\TestCase {
 		$expected = new QueryExpression(
 			['author_id' => 1, 'published' => 'Y']
 		);
+		$expected->typeMap()->defaults([
+			'Users.id' => 'integer',
+			'id' => 'integer',
+			'Users.username' => 'string',
+			'username' => 'string',
+			'Users.password' => 'string',
+			'password' => 'string',
+			'Users.created' => 'timestamp',
+			'created' => 'timestamp',
+			'Users.updated' => 'timestamp',
+			'updated' => 'timestamp',
+		]);
 		$this->assertEquals($expected, $result->clause('where'));
 	}
 
@@ -2167,6 +2222,18 @@ class TableTest extends \Cake\TestSuite\TestCase {
 		$this->assertInstanceOf('Cake\ORM\Query', $result);
 		$this->assertNull($result->clause('limit'));
 		$expected = new QueryExpression();
+		$expected->typeMap()->defaults([
+			'Users.id' => 'integer',
+			'id' => 'integer',
+			'Users.username' => 'string',
+			'username' => 'string',
+			'Users.password' => 'string',
+			'password' => 'string',
+			'Users.created' => 'timestamp',
+			'created' => 'timestamp',
+			'Users.updated' => 'timestamp',
+			'updated' => 'timestamp',
+		]);
 		$expected->add(
 			['or' => ['author_id' => 1, 'published' => 'Y']]
 		);