diff --git a/src/Database/Type/IntegerType.php b/src/Database/Type/IntegerType.php index f61eb89bff6..f3140f2185c 100644 --- a/src/Database/Type/IntegerType.php +++ b/src/Database/Type/IntegerType.php @@ -28,9 +28,9 @@ class IntegerType extends \Cake\Database\Type /** * Convert integer data into the database format. * - * @param string|resource $value The value to convert. + * @param mixed $value The value to convert. * @param Driver $driver The driver instance to convert with. - * @return string|resource + * @return int */ public function toDatabase($value, Driver $driver) { @@ -46,10 +46,9 @@ public function toDatabase($value, Driver $driver) /** * Convert integer values to PHP integers * - * @param null|string|resource $value The value to convert. + * @param mixed $value The value to convert. * @param Driver $driver The driver instance to convert with. - * @return resource - * @throws \Cake\Core\Exception\Exception + * @return int */ public function toPHP($value, Driver $driver) { diff --git a/src/ORM/ResultSet.php b/src/ORM/ResultSet.php index 355e3fe7709..6b6650933af 100644 --- a/src/ORM/ResultSet.php +++ b/src/ORM/ResultSet.php @@ -151,6 +151,15 @@ class ResultSet implements ResultSetInterface */ protected $_types = []; + /** + * The Database driver object. + * + * Cached in a property to avoid multiple calls to the same function. + * + * @var \Cake\Database\Driver + */ + protected $_driver; + /** * Constructor * @@ -162,6 +171,7 @@ public function __construct($query, $statement) $repository = $query->repository(); $this->_query = $query; $this->_statement = $statement; + $this->_driver = $driver = $this->_query->connection()->driver(); $this->_defaultTable = $this->_query->repository(); $this->_calculateAssociationMap(); $this->_hydrate = $this->_query->hydrate(); @@ -169,6 +179,7 @@ public function __construct($query, $statement) $this->_useBuffering = $query->bufferResults(); $this->_defaultAlias = $this->_defaultTable->alias(); $this->_calculateColumnMap(); + $this->_calculateTypeMap(); if ($this->_useBuffering) { $count = $this->count(); @@ -380,6 +391,74 @@ protected function _calculateColumnMap() $this->_map = $map; } + /** + * Creates a map of Type converter classes for each of the columns that should + * be fetched by this object. + * + * @return void + */ + protected function _calculateTypeMap() + { + if (isset($this->_map[$this->_defaultAlias])) { + $this->_types[$this->_defaultAlias] = $this->_getTypes( + $this->_defaultTable, + $this->_map[$this->_defaultAlias] + ); + } + + foreach ($this->_matchingMapColumns as $alias => $keys) { + $this->_types[$alias] = $this->_getTypes( + $this->_matchingMap[$alias]['instance']->target(), + $keys + ); + } + + foreach ($this->_containMap as $assoc) { + $alias = $assoc['alias']; + if (isset($this->_types[$alias]) || !$assoc['canBeJoined'] || !isset($this->_map[$alias])) { + continue; + } + $this->_types[$alias] = $this->_getTypes( + $assoc['instance']->target(), + $this->_map[$alias] + ); + } + } + + /** + * Returns the Type classes for each of the passed fields belonging to the + * table. + * + * @param \Cake\ORM\Table $table The table from which to get the schema + * @param array $fields The fields whitelist to use for fields in the schema. + * @return array + */ + protected function _getTypes($table, $fields) + { + $types = []; + $schema = $table->schema(); + $map = array_keys(Type::map() + ['string' => 1, 'text' => 1, 'boolean' => 1]); + $typeMap = array_combine( + $map, + array_map(['Cake\Database\Type', 'build'], $map) + ); + + foreach (['string', 'text'] as $t) { + if (get_class($typeMap[$t]) === 'Cake\Database\Type') { + unset($typeMap[$t]); + } + } + + foreach (array_intersect($fields, $schema->columns()) as $col) { + $typeName = $schema->columnType($col); + if (isset($typeMap[$typeName])) { + $types[$col] = $typeMap[$typeName]; + } + } + + return $types; + } + /** * Helper function to fetch the next result from the statement or * seeded results. @@ -419,7 +498,7 @@ protected function _groupResult($row) foreach ($this->_matchingMapColumns as $alias => $keys) { $matching = $this->_matchingMap[$alias]; $results['_matchingData'][$alias] = $this->_castValues( - $matching['instance']->target(), + $alias, array_combine( $keys, array_intersect_key($row, $keys) @@ -440,7 +519,7 @@ protected function _groupResult($row) if (isset($presentAliases[$defaultAlias])) { $results[$defaultAlias] = $this->_castValues( - $this->_defaultTable, + $defaultAlias, $results[$defaultAlias] ); } @@ -464,7 +543,7 @@ protected function _groupResult($row) unset($presentAliases[$alias]); if ($assoc['canBeJoined']) { - $results[$alias] = $this->_castValues($target, $results[$alias]); + $results[$alias] = $this->_castValues($assoc['alias'], $results[$alias]); $hasData = false; foreach ($results[$alias] as $v) { @@ -512,26 +591,14 @@ protected function _groupResult($row) * Casts all values from a row brought from a table to the correct * PHP type. * - * @param Table $table The table object + * @param string $alias The table object alias * @param array $values The values to cast * @return array */ - protected function _castValues($table, $values) + protected function _castValues($alias, $values) { - $alias = $table->alias(); - $driver = $this->_query->connection()->driver(); - if (empty($this->_types[$alias])) { - $schema = $table->schema(); - foreach ($schema->columns() as $col) { - $this->_types[$alias][$col] = Type::build($schema->columnType($col)); - } - } - - foreach ($values as $field => $value) { - if (!isset($this->_types[$alias][$field])) { - continue; - } - $values[$field] = $this->_types[$alias][$field]->toPHP($value, $driver); + foreach ($this->_types[$alias] as $field => $type) { + $values[$field] = $type->toPHP($values[$field], $this->_driver); } return $values;