Adding hasMany optimization fixes for failing model tests

git-svn-id: https://svn.cakephp.org/repo/branches/1.2.x.x@5587 3807eeeb-6ff5-0310-8944-8be069107fe0
This commit is contained in:
nate 2007-08-27 03:15:11 +00:00
parent 656247427f
commit ce51d009ac
3 changed files with 160 additions and 189 deletions

View file

@ -84,12 +84,6 @@ class DataSource extends Object {
* @access public * @access public
*/ */
var $took = null; var $took = null;
/**
* Enter description here...
*
* @var boolean
*/
var $cacheSources = true;
/** /**
* Enter description here... * Enter description here...
* *
@ -183,6 +177,12 @@ class DataSource extends Object {
* @access protected * @access protected
*/ */
var $_transactionStarted = false; var $_transactionStarted = false;
/**
* Enter description here...
*
* @var boolean
*/
var $cacheSources = true;
/** /**
* Constructor. * Constructor.
*/ */
@ -192,129 +192,6 @@ class DataSource extends Object {
$this->setConfig(func_get_arg(0)); $this->setConfig(func_get_arg(0));
} }
} }
/**
* Datsrouce Query abstraction
*
* @return resource Result resource identifier
*/
function query() {
$args = func_get_args();
$fields = null;
$order = null;
$limit = null;
$page = null;
$recursive = null;
if (count($args) == 1) {
return $this->fetchAll($args[0]);
} elseif (count($args) > 1 && (strpos(low($args[0]), 'findby') === 0 || strpos(low($args[0]), 'findallby') === 0)) {
$params = $args[1];
if (strpos(strtolower($args[0]), 'findby') === 0) {
$all = false;
$field = Inflector::underscore(preg_replace('/findBy/i', '', $args[0]));
} else {
$all = true;
$field = Inflector::underscore(preg_replace('/findAllBy/i', '', $args[0]));
}
$or = (strpos($field, '_or_') !== false);
if ($or) {
$field = explode('_or_', $field);
} else {
$field = explode('_and_', $field);
}
$off = count($field) - 1;
if (isset($params[1 + $off])) {
$fields = $params[1 + $off];
}
if (isset($params[2 + $off])) {
$order = $params[2 + $off];
}
if (!array_key_exists(0, $params)) {
return false;
}
$c = 0;
$query = array();
foreach ($field as $f) {
if (!is_array($params[$c]) && !empty($params[$c]) && $params[$c] !== true && $params[$c] !== false) {
$query[$args[2]->name . '.' . $f] = '= ' . $params[$c];
} else {
$query[$args[2]->name . '.' . $f] = $params[$c];
}
$c++;
}
if ($or) {
$query = array('OR' => $query);
}
if ($all) {
if (isset($params[3 + $off])) {
$limit = $params[3 + $off];
}
if (isset($params[4 + $off])) {
$page = $params[4 + $off];
}
if (isset($params[5 + $off])) {
$recursive = $params[5 + $off];
}
return $args[2]->findAll($query, $fields, $order, $limit, $page, $recursive);
} else {
if (isset($params[3 + $off])) {
$recursive = $params[3 + $off];
}
return $args[2]->find($query, $fields, $order, $recursive);
}
} else {
if (isset($args[1]) && $args[1] === true) {
return $this->fetchAll($args[0], true);
}
return $this->fetchAll($args[0], false);
}
}
/**
* Returns an array of all result rows for a given SQL query.
* Returns false if no rows matched.
*
* @param string $sql SQL statement
* @param boolean $cache Enables returning/storing cached query results
* @return array Array of resultset rows, or false if no rows matched
*/
function fetchAll($sql, $cache = true, $modelName = null) {
if ($cache && isset($this->_queryCache[$sql])) {
if (preg_match('/^\s*select/i', $sql)) {
return $this->_queryCache[$sql];
}
}
if ($this->execute($sql)) {
$out = array();
while ($item = $this->fetchRow()) {
$out[] = $item;
}
if ($cache) {
if (strpos(trim(strtolower($sql)), 'select') !== false) {
$this->_queryCache[$sql] = $out;
}
}
return $out;
} else {
return false;
}
}
/** /**
* Caches/returns cached results for child instances * Caches/returns cached results for child instances
* *
@ -365,14 +242,13 @@ class DataSource extends Object {
if ($this->cacheSources === false) { if ($this->cacheSources === false) {
return null; return null;
} }
if (isset($this->__descriptions[$model->tablePrefix . $model->table])) {
if (isset($this->__descriptions[$model->tablePrefix.$model->table])) { return $this->__descriptions[$model->tablePrefix . $model->table];
return $this->__descriptions[$model->tablePrefix.$model->table];
} }
$cache = $this->__cacheDescription($model->tablePrefix.$model->table); $cache = $this->__cacheDescription($model->tablePrefix . $model->table);
if ($cache !== null) { if ($cache !== null) {
$this->__descriptions[$model->tablePrefix.$model->table] =& $cache; $this->__descriptions[$model->tablePrefix . $model->table] =& $cache;
return $cache; return $cache;
} }
return null; return null;

View file

@ -169,6 +169,95 @@ class DboSource extends DataSource {
return $this->_result; return $this->_result;
} }
} }
/**
* DataSource Query abstraction
*
* @return resource Result resource identifier
*/
function query() {
$args = func_get_args();
$fields = null;
$order = null;
$limit = null;
$page = null;
$recursive = null;
if (count($args) == 1) {
return $this->fetchAll($args[0]);
} elseif (count($args) > 1 && (strpos(low($args[0]), 'findby') === 0 || strpos(low($args[0]), 'findallby') === 0)) {
$params = $args[1];
if (strpos(strtolower($args[0]), 'findby') === 0) {
$all = false;
$field = Inflector::underscore(preg_replace('/findBy/i', '', $args[0]));
} else {
$all = true;
$field = Inflector::underscore(preg_replace('/findAllBy/i', '', $args[0]));
}
$or = (strpos($field, '_or_') !== false);
if ($or) {
$field = explode('_or_', $field);
} else {
$field = explode('_and_', $field);
}
$off = count($field) - 1;
if (isset($params[1 + $off])) {
$fields = $params[1 + $off];
}
if (isset($params[2 + $off])) {
$order = $params[2 + $off];
}
if (!array_key_exists(0, $params)) {
return false;
}
$c = 0;
$query = array();
foreach ($field as $f) {
if (!is_array($params[$c]) && !empty($params[$c]) && $params[$c] !== true && $params[$c] !== false) {
$query[$args[2]->name . '.' . $f] = '= ' . $params[$c];
} else {
$query[$args[2]->name . '.' . $f] = $params[$c];
}
$c++;
}
if ($or) {
$query = array('OR' => $query);
}
if ($all) {
if (isset($params[3 + $off])) {
$limit = $params[3 + $off];
}
if (isset($params[4 + $off])) {
$page = $params[4 + $off];
}
if (isset($params[5 + $off])) {
$recursive = $params[5 + $off];
}
return $args[2]->findAll($query, $fields, $order, $limit, $page, $recursive);
} else {
if (isset($params[3 + $off])) {
$recursive = $params[3 + $off];
}
return $args[2]->find($query, $fields, $order, $recursive);
}
} else {
if (isset($args[1]) && $args[1] === true) {
return $this->fetchAll($args[0], true);
}
return $this->fetchAll($args[0], false);
}
}
/** /**
* Returns a row from current resultset as an array . * Returns a row from current resultset as an array .
* *
@ -574,9 +663,8 @@ class DboSource extends DataSource {
* @param array $stack * @param array $stack
*/ */
function queryAssociation(&$model, &$linkModel, $type, $association, $assocData, &$queryData, $external = false, &$resultSet, $recursive, $stack) { function queryAssociation(&$model, &$linkModel, $type, $association, $assocData, &$queryData, $external = false, &$resultSet, $recursive, $stack) {
$query = $this->generateAssociationQuery($model, $linkModel, $type, $association, $assocData, $queryData, $external, $resultSet);
if ($query) {
if ($query = $this->generateAssociationQuery($model, $linkModel, $type, $association, $assocData, $queryData, $external, $resultSet)) {
if (!isset($resultSet) || !is_array($resultSet)) { if (!isset($resultSet) || !is_array($resultSet)) {
if (Configure::read() > 0) { if (Configure::read() > 0) {
e('<div style = "font: Verdana bold 12px; color: #FF0000">SQL Error in model ' . $model->name . ': '); e('<div style = "font: Verdana bold 12px; color: #FF0000">SQL Error in model ' . $model->name . ': ');
@ -588,20 +676,19 @@ class DboSource extends DataSource {
return null; return null;
} }
$count = count($resultSet); $count = count($resultSet);
if ($type === 'hasMany' && (!isset($assocData['limit']) || empty($assocData['limit']))) { if ($type === 'hasMany' && (!isset($assocData['limit']) || empty($assocData['limit']))) {
$ins = array(); //if ($type === 'hasMany' && !isset($assocData['limit'])) {
$ins = $fetch = array();
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
$in = $this->insertQueryData('{$__cakeID__$}', $resultSet[$i], $association, $assocData, $model, $linkModel, $stack); if ($in = $this->insertQueryData('{$__cakeID__$}', $resultSet[$i], $association, $assocData, $model, $linkModel, $stack)) {
if ($in !== false) {
$ins[] = $in; $ins[] = $in;
} }
} }
if (!empty($ins)) { if (!empty($ins)) {
$query = r('{$__cakeID__$}', join(', ',$ins), $query); $query = r('{$__cakeID__$}', join(', ', $ins), $query);
$fetch = $this->fetchAll($query, $model->cacheQueries, $model->name); $fetch = $this->fetchAll($query, $model->cacheQueries, $model->name);
} else {
$fetch = array();
} }
if (!empty($fetch) && is_array($fetch)) { if (!empty($fetch) && is_array($fetch)) {
@ -670,15 +757,15 @@ class DboSource extends DataSource {
} }
function __mergeHasMany(&$resultSet, $merge, $association, &$model, &$linkModel) { function __mergeHasMany(&$resultSet, $merge, $association, &$model, &$linkModel) {
foreach ($resultSet as $key => $value) { foreach ($resultSet as $i => $value) {
$merged[$association] = array();
$count = 0; $count = 0;
foreach ($merge as $assoc => $data) { $merged[$association] = array();
foreach ($merge as $j => $data) {
if (isset($value[$model->name]) && $value[$model->name][$model->primaryKey] === $data[$association][$model->hasMany[$association]['foreignKey']]) { if (isset($value[$model->name]) && $value[$model->name][$model->primaryKey] === $data[$association][$model->hasMany[$association]['foreignKey']]) {
if (count($data) > 1) { if (count($data) > 1) {
$temp[] = Set::pushDiff($data[$association], $data); $data = am($data[$association], $data);
unset($temp[$count][$association]); unset($data[$association]);
$merged[$association] = $temp; $merged[$association][] = $data;
} else { } else {
$merged[$association][] = $data[$association]; $merged[$association][] = $data[$association];
} }
@ -686,8 +773,7 @@ class DboSource extends DataSource {
$count++; $count++;
} }
if (isset($value[$model->name])) { if (isset($value[$model->name])) {
$resultSet[$key] = Set::pushDiff($resultSet[$key], $merged); $resultSet[$i] = Set::pushDiff($resultSet[$i], $merged);
unset($merged);
unset($temp); unset($temp);
} }
} }
@ -787,19 +873,22 @@ class DboSource extends DataSource {
if (!isset($queryData['selfJoin'])) { if (!isset($queryData['selfJoin'])) {
$queryData['selfJoin'] = array(); $queryData['selfJoin'] = array();
$self = array('fields' => $this->fields($model, null, $queryData['fields']), $self = array(
'fields' => $this->fields($model, null, $queryData['fields']),
'joins' => array(array( 'joins' => array(array(
'table' => $this->fullTableName($linkModel), 'table' => $this->fullTableName($linkModel),
'alias' => $alias, 'alias' => $alias,
'type' => 'LEFT', 'type' => 'LEFT',
'conditions' => array($model->escapeField($assocData['foreignKey']) => 'conditions' => array(
'{$__cakeIdentifier[' . "{$alias}.{$linkModel->primaryKey}" . ']__$}'))), $model->escapeField($assocData['foreignKey']) => '{$__cakeIdentifier[' . "{$alias}.{$linkModel->primaryKey}" . ']__$}'))
),
'table' => $this->fullTableName($model), 'table' => $this->fullTableName($model),
'alias' => $model->name, 'alias' => $model->name,
'limit' => $queryData['limit'], 'limit' => $queryData['limit'],
'offset' => $queryData['offset'], 'offset' => $queryData['offset'],
'conditions'=> $queryData['conditions'], 'conditions'=> $queryData['conditions'],
'order' => $queryData['order']); 'order' => $queryData['order']
);
if (!empty($assocData['conditions'])) { if (!empty($assocData['conditions'])) {
$self['joins'][0]['conditions'] = trim($this->conditions(am($self['joins'][0]['conditions'], $assocData['conditions']), true, false)); $self['joins'][0]['conditions'] = trim($this->conditions(am($self['joins'][0]['conditions'], $assocData['conditions']), true, false));
@ -865,6 +954,7 @@ class DboSource extends DataSource {
} elseif (!empty($model->hasMany) && $model->recursive > -1) { } elseif (!empty($model->hasMany) && $model->recursive > -1) {
$assocFields = $this->fields($model, $model->name, array("{$model->name}.{$model->primaryKey}")); $assocFields = $this->fields($model, $model->name, array("{$model->name}.{$model->primaryKey}"));
$passedFields = $this->fields($model, $model->name, $queryData['fields']); $passedFields = $this->fields($model, $model->name, $queryData['fields']);
if (count($passedFields) === 1) { if (count($passedFields) === 1) {
$match = strpos($passedFields[0], $assocFields[0]); $match = strpos($passedFields[0], $assocFields[0]);
$match1 = strpos($passedFields[0], 'COUNT('); $match1 = strpos($passedFields[0], 'COUNT(');
@ -876,22 +966,23 @@ class DboSource extends DataSource {
} else { } else {
$queryData['fields'] = array_unique(array_merge($passedFields, $assocFields)); $queryData['fields'] = array_unique(array_merge($passedFields, $assocFields));
} }
unset($assocFields); unset($assocFields, $passedFields);
unset($passedFields);
} }
if ($linkModel == null) { if ($linkModel == null) {
if (array_key_exists('selfJoin', $queryData)) { if (array_key_exists('selfJoin', $queryData)) {
return $this->generateSelfAssociationQuery($model, $linkModel, $type, $association, $assocData, $queryData, $external, $resultSet); return $this->generateSelfAssociationQuery($model, $linkModel, $type, $association, $assocData, $queryData, $external, $resultSet);
} else { } else {
return $this->buildStatement(array('fields' => array_unique($queryData['fields']), return $this->buildStatement(array(
'fields' => array_unique($queryData['fields']),
'table' => $this->fullTableName($model), 'table' => $this->fullTableName($model),
'alias' => $model->name, 'alias' => $model->name,
'limit' => $queryData['limit'], 'limit' => $queryData['limit'],
'offset' => $queryData['offset'], 'offset' => $queryData['offset'],
'joins' => $queryData['joins'], 'joins' => $queryData['joins'],
'conditions' => $queryData['conditions'], 'conditions' => $queryData['conditions'],
'order' => $queryData['order']), $model); 'order' => $queryData['order']), $model
);
} }
} }
$alias = $association; $alias = $association;
@ -931,16 +1022,17 @@ class DboSource extends DataSource {
} elseif ($type == 'belongsTo') { } elseif ($type == 'belongsTo') {
$conditions = $this->__mergeConditions($assocData['conditions'], array("{$alias}.{$linkModel->primaryKey}" => '{$__cakeForeignKey__$}')); $conditions = $this->__mergeConditions($assocData['conditions'], array("{$alias}.{$linkModel->primaryKey}" => '{$__cakeForeignKey__$}'));
} }
$query = am($assocData, array('conditions' => $conditions, $query = am($assocData, array(
'conditions' => $conditions,
'table' => $this->fullTableName($linkModel), 'table' => $this->fullTableName($linkModel),
'fields' => $fields, 'fields' => $fields,
'alias' => $alias)); 'alias' => $alias
));
if ($type == 'belongsTo') { if ($type == 'belongsTo') {
// Dunno if we should be doing this for hasOne also...? // Dunno if we should be doing this for hasOne also...?
// Or maybe not doing it at all...? // Or maybe not doing it at all...?
$query = am($query, array('order' => $assocData['order'], $query = am($query, array('order' => $assocData['order'], 'limit' => $limit));
'limit' => $limit));
} }
} else { } else {
if ($type == 'hasOne') { if ($type == 'hasOne') {
@ -949,10 +1041,12 @@ class DboSource extends DataSource {
$conditions = $this->__mergeConditions($assocData['conditions'], array("{$model->name}.{$assocData['foreignKey']}" => '{$__cakeIdentifier[' . "{$alias}.{$linkModel->primaryKey}" . ']__$}')); $conditions = $this->__mergeConditions($assocData['conditions'], array("{$model->name}.{$assocData['foreignKey']}" => '{$__cakeIdentifier[' . "{$alias}.{$linkModel->primaryKey}" . ']__$}'));
} }
$join = array('table' => $this->fullTableName($linkModel), $join = array(
'table' => $this->fullTableName($linkModel),
'alias' => $alias, 'alias' => $alias,
'type' => 'LEFT', 'type' => 'LEFT',
'conditions' => trim($this->conditions($conditions, true, false))); 'conditions' => trim($this->conditions($conditions, true, false))
);
$queryData['fields'] = am($queryData['fields'], $fields); $queryData['fields'] = am($queryData['fields'], $fields);
@ -966,11 +1060,11 @@ class DboSource extends DataSource {
} }
break; break;
case 'hasMany': case 'hasMany':
$assocFields = $this->fields($linkModel, $alias, array("{$alias}.{$assocData['foreignKey']}")); $assocData['fields'] = array_unique(array_merge(
$passedFields = $this->fields($linkModel, $alias, $assocData['fields']); $this->fields($linkModel, $alias, $assocData['fields']),
$assocData['fields'] = array_unique(array_merge($passedFields, $assocFields)); $this->fields($linkModel, $alias, array("{$alias}.{$assocData['foreignKey']}"))
unset($assocFields); ));
unset($passedFields);
$query = array( $query = array(
'conditions' => $this->__mergeConditions(array("{$alias}.{$assocData['foreignKey']}" => array('{$__cakeID__$}')), $assocData['conditions']), 'conditions' => $this->__mergeConditions(array("{$alias}.{$assocData['foreignKey']}" => array('{$__cakeID__$}')), $assocData['conditions']),
'fields' => $assocData['fields'], 'fields' => $assocData['fields'],
@ -990,6 +1084,7 @@ class DboSource extends DataSource {
$joinAssoc = $joinAlias = $model->{$assocData['with']}->name; $joinAssoc = $joinAlias = $model->{$assocData['with']}->name;
$joinFields = $model->{$assocData['with']}->loadInfo(); $joinFields = $model->{$assocData['with']}->loadInfo();
$joinFields = $joinFields->extract('{n}.name'); $joinFields = $joinFields->extract('{n}.name');
if (is_array($joinFields) && !empty($joinFields)) { if (is_array($joinFields) && !empty($joinFields)) {
$joinFields = $this->fields($model->{$assocData['with']}, $model->{$assocData['with']}->name, $joinFields); $joinFields = $this->fields($model->{$assocData['with']}, $model->{$assocData['with']}->name, $joinFields);
} }

View file

@ -128,7 +128,7 @@ class Set extends Object {
* Pushes the differences in $array2 onto the end of $array * Pushes the differences in $array2 onto the end of $array
* *
* @param mixed $array Original array * @param mixed $array Original array
* @param mixed $array2 Diferences to push * @param mixed $array2 Differences to push
* @return array Combined array * @return array Combined array
* @access public * @access public
*/ */