Skip to content
Permalink
Browse files

Optimizing type casting in ResultSet.

This is still the heaviest part of the hydration process, but these
changes help reduce in half the amount of method calls.
  • Loading branch information...
lorenzo committed Mar 29, 2015
1 parent 448f54f commit 70906e89b74e4fac18fe7987e0afe46e35a9c7d1
Showing with 90 additions and 24 deletions.
  1. +4 −5 src/Database/Type/IntegerType.php
  2. +86 −19 src/ORM/ResultSet.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)
{
@@ -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,13 +171,15 @@ 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();
$this->_entityClass = $repository->entityClass();
$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;

0 comments on commit 70906e8

Please sign in to comment.
You can’t perform that action at this time.