Refactoring DboSource - removes all SQL from Model, refactoring transaction handling

git-svn-id: https://svn.cakephp.org/repo/branches/1.2.x.x@6557 3807eeeb-6ff5-0310-8944-8be069107fe0
This commit is contained in:
nate 2008-03-11 02:44:33 +00:00
parent d326c6eefe
commit c1c35b2e13
11 changed files with 278 additions and 390 deletions

View file

@ -782,7 +782,8 @@ class TreeBehavior extends ModelBehavior {
* @access private
*/
function __getMax($model, $scope, $right) {
list($edge) = array_values($model->find('first', array('conditions' => $scope, 'fields' => 'MAX(' . $right . ') AS ' . $right, 'recursive' => -1)));
$db =& ConnectionManager::getDataSource($model->useDbConfig);
list($edge) = array_values($model->find('first', array('conditions' => $scope, 'fields' => $db->calculate('max', array($right)), 'recursive' => -1)));
return ife(empty ($edge[$right]), 0, $edge[$right]);
}
/**

View file

@ -252,7 +252,7 @@ class DataSource extends Object {
* @return boolean True
*/
function begin() {
return true;
return !$this->_transactionStarted;
}
/**
* Commit a transaction
@ -260,7 +260,7 @@ class DataSource extends Object {
* @return boolean True
*/
function commit(&$model) {
return true;
return $this->_transactionStarted;
}
/**
* Rollback a transaction
@ -268,7 +268,7 @@ class DataSource extends Object {
* @return boolean True
*/
function rollback(&$model) {
return true;
return $this->_transactionStarted;
}
/**
* Converts column types to basic types
@ -499,6 +499,10 @@ class DataSource extends Object {
*
*/
function __destruct() {
if ($this->_transactionStarted) {
$null = null;
$this->rollback($null);
}
if ($this->connected) {
$this->close();
}

View file

@ -301,40 +301,9 @@ class DboMssql extends DboSource {
* (i.e. if the database/model does not support transactions).
*/
function begin(&$model) {
if (parent::begin($model)) {
if ($this->execute('BEGIN TRANSACTION')) {
$this->_transactionStarted = true;
return true;
}
}
return false;
}
/**
* Commit a transaction
*
* @param unknown_type $model
* @return boolean True on success, false on fail
* (i.e. if the database/model does not support transactions,
* or a transaction has not started).
*/
function commit(&$model) {
if (parent::commit($model)) {
$this->_transactionStarted = false;
return $this->execute('COMMIT');
}
return false;
}
/**
* Rollback a transaction
*
* @param unknown_type $model
* @return boolean True on success, false on fail
* (i.e. if the database/model does not support transactions,
* or a transaction has not started).
*/
function rollback(&$model) {
if (parent::rollback($model)) {
return $this->execute('ROLLBACK');
if (parent::begin($model) && $this->execute('BEGIN TRANSACTION')) {
$this->_transactionStarted = true;
return true;
}
return false;
}

View file

@ -253,40 +253,9 @@ class DboMysqli extends DboSource {
* (i.e. if the database/model does not support transactions).
*/
function begin(&$model) {
if (parent::begin($model)) {
if ($this->execute('START TRANSACTION')) {
$this->_transactionStarted = true;
return true;
}
}
return false;
}
/**
* Commit a transaction
*
* @param unknown_type $model
* @return boolean True on success, false on fail
* (i.e. if the database/model does not support transactions,
* or a transaction has not started).
*/
function commit(&$model) {
if (parent::commit($model)) {
$this->_transactionStarted = false;
return $this->execute('COMMIT');
}
return false;
}
/**
* Rollback a transaction
*
* @param unknown_type $model
* @return boolean True on success, false on fail
* (i.e. if the database/model does not support transactions,
* or a transaction has not started).
*/
function rollback(&$model) {
if (parent::rollback($model)) {
return $this->execute('ROLLBACK');
if (parent::begin($model) && $this->execute('START TRANSACTION')) {
$this->_transactionStarted = true;
return true;
}
return false;
}

View file

@ -84,34 +84,36 @@ class DboPostgres extends DboSource {
* @return True if successfully connected.
*/
function connect() {
$config = $this->config;
$connect = $config['connect'];
$this->connection = $connect("host='{$config['host']}' port='{$config['port']}' dbname='{$config['database']}' user='{$config['login']}' password='{$config['password']}'");
$this->connected = false;
if ($this->connection) {
$this->connected = true;
$this->_execute("SET search_path TO " . $config['schema']);
} else {
$this->connected = false;
}
if (!empty($config['encoding'])) {
$this->setEncoding($config['encoding']);
}
return $this->connected;
}
/**
* Disconnects from database.
*
* @return boolean True if the database could be disconnected, else false
*/
function disconnect() {
@pg_free_result($this->results);
$this->connected = !@pg_close($this->connection);
if (is_resource($this->results)) {
pg_free_result($this->results);
}
if (is_resource($this->connected)) {
$this->connected = !pg_close($this->connection);
} else {
$this->connected = false;
}
return !$this->connected;
}
/**
* Executes given SQL statement.
*
@ -265,55 +267,21 @@ class DboPostgres extends DboSource {
* (i.e. if the database/model does not support transactions).
*/
function begin(&$model) {
if (parent::begin($model)) {
if ($this->execute('BEGIN')) {
$this->_transactionStarted = true;
return true;
}
if (parent::begin($model) && $this->execute('BEGIN')) {
$this->_transactionStarted = true;
return true;
}
return false;
}
/**
* Commit a transaction
*
* @param unknown_type $model
* @return boolean True on success, false on fail
* (i.e. if the database/model does not support transactions,
* or a transaction has not started).
*/
function commit(&$model) {
if (parent::commit($model)) {
$this->_transactionStarted = false;
return $this->execute('COMMIT');
}
return false;
}
/**
* Rollback a transaction
*
* @param unknown_type $model
* @return boolean True on success, false on fail
* (i.e. if the database/model does not support transactions,
* or a transaction has not started).
*/
function rollback(&$model) {
if (parent::rollback($model)) {
return $this->execute('ROLLBACK');
}
return false;
}
/**
* Returns a formatted error message from previous database operation.
*
* @return string Error message
*/
function lastError() {
$last_error = pg_last_error($this->connection);
if ($last_error) {
return $last_error;
$error = pg_last_error($this->connection);
if ($error) {
return $error;
}
return null;
}

View file

@ -534,7 +534,7 @@ class DboSource extends DataSource {
*/
function read(&$model, $queryData = array(), $recursive = null) {
$this->__scrubQueryData($queryData);
$queryData = $this->__scrubQueryData($queryData);
$null = null;
$array = array();
$linkedModels = array();
@ -561,18 +561,12 @@ class DboSource extends DataSource {
foreach ($model->{$type} as $assoc => $assocData) {
if ($model->recursive > -1) {
$linkModel =& $model->{$assoc};
$external = isset($assocData['external']);
if ($model->alias == $linkModel->alias && $type != 'hasAndBelongsToMany' && $type != 'hasMany') {
if (true === $this->generateSelfAssociationQuery($model, $linkModel, $type, $assoc, $assocData, $queryData, $external, $null)) {
if ($model->useDbConfig == $linkModel->useDbConfig) {
if (true === $this->generateAssociationQuery($model, $linkModel, $type, $assoc, $assocData, $queryData, $external, $null)) {
$linkedModels[] = $type . '/' . $assoc;
}
} else {
if ($model->useDbConfig == $linkModel->useDbConfig) {
if (true === $this->generateAssociationQuery($model, $linkModel, $type, $assoc, $assocData, $queryData, $external, $null)) {
$linkedModels[] = $type . '/' . $assoc;
}
}
}
}
}
@ -923,105 +917,21 @@ class DboSource extends DataSource {
}
}
/**
* Enter description here...
*
* @param unknown_type $model
* @param unknown_type $linkModel
* @param unknown_type $type
* @param unknown_type $association
* @param unknown_type $assocData
* @param unknown_type $queryData
* @param unknown_type $external
* @param unknown_type $resultSet
* @return unknown
*/
function generateSelfAssociationQuery(&$model, &$linkModel, $type, $association = null, $assocData = array(), &$queryData, $external = false, &$resultSet) {
$alias = $association;
if (empty($alias) && !empty($linkModel)) {
$alias = $linkModel->alias;
}
if (!isset($queryData['selfJoin'])) {
$queryData['selfJoin'] = array();
$self = array(
'fields' => $this->fields($model, null, $queryData['fields']),
'joins' => array(array(
'table' => $this->fullTableName($linkModel),
'alias' => $alias,
'type' => 'LEFT',
'conditions' => array(
$model->escapeField($assocData['foreignKey']) => '{$__cakeIdentifier[' . "{$alias}.{$linkModel->primaryKey}" . ']__$}'))
),
'table' => $this->fullTableName($model),
'alias' => $model->alias,
'limit' => $queryData['limit'],
'offset' => $queryData['offset'],
'conditions'=> $queryData['conditions'],
'order' => $queryData['order']
);
if (isset($assocData['type'])) {
$self['joins'][0]['type'] = $assocData['type'];
}
if (!empty($assocData['conditions'])) {
$self['joins'][0]['conditions'] = trim($this->conditions(array_merge($self['joins'][0]['conditions'], (array)$assocData['conditions']), true, false));
}
if (!empty($queryData['joins'])) {
foreach ($queryData['joins'] as $join) {
$self['joins'][] = $join;
}
}
if ($this->__bypass === false) {
$self['fields'] = array_merge($self['fields'], $this->fields($linkModel, $alias, (isset($assocData['fields']) ? $assocData['fields'] : '')));
}
if (!in_array($self, $queryData['selfJoin'])) {
$queryData['selfJoin'][] = $self;
return true;
}
} elseif (isset($linkModel)) {
return $this->generateAssociationQuery($model, $linkModel, $type, $association, $assocData, $queryData, $external, $resultSet);
} else {
$result = $queryData['selfJoin'][0];
if (!empty($queryData['joins'])) {
foreach ($queryData['joins'] as $join) {
if (!in_array($join, $result['joins'])) {
$result['joins'][] = $join;
}
}
}
if (!empty($queryData['conditions'])) {
$result['conditions'] = trim($this->conditions(array_merge($result['conditions'], $assocData['conditions']), true, false));
}
if (!empty($queryData['fields'])) {
$result['fields'] = array_unique(array_merge($result['fields'], $queryData['fields']));
}
$sql = $this->buildStatement($result, $model);
return $sql;
}
}
/**
* Enter description here...
* Generates an array representing a query or part of a query from a single model or two associated models
*
* @param Model $model
* @param unknown_type $linkModel
* @param unknown_type $type
* @param unknown_type $association
* @param unknown_type $assocData
* @param unknown_type $queryData
* @param unknown_type $external
* @param unknown_type $resultSet
* @return unknown
* @param Model $linkModel
* @param string $type
* @param string $association
* @param array $assocData
* @param array $queryData
* @param boolean $external
* @param array $resultSet
* @return mixed
*/
function generateAssociationQuery(&$model, &$linkModel, $type, $association = null, $assocData = array(), &$queryData, $external = false, &$resultSet) {
$this->__scrubQueryData($queryData);
$this->__scrubQueryData($assocData);
$joinedOnSelf = false;
$queryData = $this->__scrubQueryData($queryData);
$assocData = $this->__scrubQueryData($assocData);
if (empty($queryData['fields'])) {
$queryData['fields'] = $this->fields($model, $model->alias);
@ -1033,21 +943,19 @@ class DboSource extends DataSource {
$match = strpos($passedFields[0], $assocFields[0]);
$match1 = strpos($passedFields[0], 'COUNT(');
if ($match === false && $match1 === false) {
$queryData['fields'] = array_unique(array_merge($passedFields, $assocFields));
$queryData['fields'] = array_merge($passedFields, $assocFields);
} else {
$queryData['fields'] = $passedFields;
}
} else {
$queryData['fields'] = array_unique(array_merge($passedFields, $assocFields));
$queryData['fields'] = array_merge($passedFields, $assocFields);
}
unset($assocFields, $passedFields);
}
if ($linkModel == null) {
if (array_key_exists('selfJoin', $queryData)) {
return $this->generateSelfAssociationQuery($model, $linkModel, $type, $association, $assocData, $queryData, $external, $resultSet);
} else {
return $this->buildStatement(array(
return $this->buildStatement(
array(
'fields' => array_unique($queryData['fields']),
'table' => $this->fullTableName($model),
'alias' => $model->alias,
@ -1055,44 +963,33 @@ class DboSource extends DataSource {
'offset' => $queryData['offset'],
'joins' => $queryData['joins'],
'conditions' => $queryData['conditions'],
'order' => $queryData['order']), $model
);
}
'order' => $queryData['order']
),
$model
);
}
if ($external && !empty($assocData['finderQuery'])) {
return $assocData['finderQuery'];
}
$alias = $association;
if ($model->alias == $linkModel->alias) {
$joinedOnSelf = true;
}
if ($external && isset($assocData['finderQuery'])) {
if (!empty($assocData['finderQuery'])) {
return $assocData['finderQuery'];
}
}
$self = ($model->name == $linkModel->name);
$fields = array();
if ((!$external && in_array($type, array('hasOne', 'belongsTo')) && $this->__bypass === false) || $external) {
$fields = $this->fields($linkModel, $alias, $assocData['fields']);
} else {
$fields = array();
}
$limit = '';
if (isset($assocData['limit'])) {
if ((!isset($assocData['offset']) || (empty($assocData['offset']))) && isset($assocData['page'])) {
$assocData['offset'] = ($assocData['page'] - 1) * $assocData['limit'];
} elseif (!isset($assocData['offset'])) {
$assocData['offset'] = null;
}
$limit = $this->limit($assocData['limit'], $assocData['offset']);
if (empty($assocData['offset']) && !empty($assocData['page'])) {
$assocData['offset'] = ($assocData['page'] - 1) * $assocData['limit'];
}
$assocData['limit'] = $this->limit($assocData['limit'], $assocData['offset']);
switch($type) {
case 'hasOne':
case 'belongsTo':
$conditions = $this->__mergeConditions(
$assocData['conditions'],
$this->getConstraint($type, $model, $linkModel, $alias, array_merge($assocData, compact('external')))
$this->getConstraint($type, $model, $linkModel, $alias, array_merge($assocData, compact('external', 'self')))
);
if ($external) {
$query = array_merge($assocData, array(
@ -1101,22 +998,14 @@ class DboSource extends DataSource {
'fields' => $fields,
'alias' => $alias
));
if ($type == 'belongsTo') {
// Dunno if we should be doing this for hasOne also...?
// Or maybe not doing it at all...?
$query = array_merge($query, array('order' => $assocData['order'], 'limit' => $limit));
}
$query = array_merge(array('order' => $assocData['order'], 'limit' => $assocData['limit']), $query);
} else {
$join = array(
'table' => $this->fullTableName($linkModel),
'alias' => $alias,
'type' => 'LEFT',
'type' => isset($assocData['type']) ? $assocData['type'] : 'LEFT',
'conditions' => trim($this->conditions($conditions, true, false))
);
if (isset($assocData['type'])) {
$join['type'] = $assocData['type'];
}
$queryData['fields'] = array_merge($queryData['fields'], $fields);
if (!empty($assocData['order'])) {
@ -1131,17 +1020,15 @@ class DboSource extends DataSource {
case 'hasMany':
$assocData['fields'] = $this->fields($linkModel, $alias, $assocData['fields']);
if (!empty($assocData['foreignKey'])) {
$assocData['fields'] = array_unique(array_merge(
$assocData['fields'], $this->fields($linkModel, $alias, array("{$alias}.{$assocData['foreignKey']}"))
));
$assocData['fields'] = array_merge($assocData['fields'], $this->fields($linkModel, $alias, array("{$alias}.{$assocData['foreignKey']}")));
}
$query = array(
'conditions' => $this->__mergeConditions($this->getConstraint('hasMany', $model, $linkModel, $alias, $assocData), $assocData['conditions']),
'fields' => $assocData['fields'],
'fields' => array_unique($assocData['fields']),
'table' => $this->fullTableName($linkModel),
'alias' => $alias,
'order' => $assocData['order'],
'limit' => $limit
'limit' => $assocData['limit']
);
break;
case 'hasAndBelongsToMany':
@ -1164,7 +1051,7 @@ class DboSource extends DataSource {
$query = array(
'conditions' => $assocData['conditions'],
'limit' => $limit,
'limit' => $assocData['limit'],
'table' => $this->fullTableName($linkModel),
'alias' => $alias,
'fields' => array_merge($this->fields($linkModel, $alias, $assocData['fields']), $joinFields),
@ -1191,7 +1078,7 @@ class DboSource extends DataSource {
* @return array Conditions array defining the constraint between $model and $association
*/
function getConstraint($type, $model, $linkModel, $alias, $assoc, $alias2 = null) {
$assoc = array_merge(array('external' => false), $assoc);
$assoc = array_merge(array('external' => false, 'self' => false), $assoc);
if (array_key_exists('foreignKey', $assoc) && empty($assoc['foreignKey'])) {
return array();
@ -1459,20 +1346,42 @@ class DboSource extends DataSource {
foreach ($joins as $assoc) {
if (isset($model->{$assoc}) && $model->useDbConfig == $model->{$assoc}->useDbConfig) {
$assocData = $model->getAssociated($assoc);
$type = 'LEFT';
if (isset($assocData['type'])) {
$type = $assocData['type'];
}
$join[] = $this->buildJoinStatement(array(
'table' => $this->fullTableName($model->{$assoc}),
'alias' => $assoc,
'type' => $type,
'type' => isset($assocData['type']) ? $assocData['type'] : 'LEFT',
'conditions' => trim($this->conditions($this->getConstraint($assocData['association'], $model, $model->{$assoc}, $assoc, $assocData), true, false))
));
}
}
return $join;
}
/**
* Returns the an SQL calculation, i.e. COUNT() or MAX()
*
* @param string $func Lowercase name of SQL function, i.e. 'count' or 'max'
* @param array $params Function parameters (any values must be quoted manually)
* @return string An SQL calculation function
* @access public
*/
function calculate($func, $params = array()) {
switch (strtolower($func)) {
case 'count':
if (!isset($params[0])) {
$params[0] = '*';
}
if (!isset($params[1])) {
$params[1] = 'count';
}
return 'COUNT(' . $this->name($params[0]) . ') AS ' . $this->name($params[1]);
case 'max':
if (!isset($params[1])) {
$params[1] = $params[0];
}
return 'MAX(' . $this->name($params[0]) . ') AS ' . $this->name($params[1]);
}
}
/**
* Deletes all the records in a table and resets the count of the auto-incrementing
* primary key, where applicable.
@ -1484,6 +1393,36 @@ class DboSource extends DataSource {
function truncate($table) {
return $this->execute('TRUNCATE TABLE ' . $this->fullTableName($table));
}
/**
* Commit a transaction
*
* @param model $model
* @return boolean True on success, false on fail
* (i.e. if the database/model does not support transactions,
* or a transaction has not started).
*/
function commit(&$model) {
if (parent::commit($model) && $this->execute('COMMIT')) {
$this->_transactionStarted = false;
return true;
}
return false;
}
/**
* Rollback a transaction
*
* @param model $model
* @return boolean True on success, false on fail
* (i.e. if the database/model does not support transactions,
* or a transaction has not started).
*/
function rollback(&$model) {
if (parent::rollback($model) && $this->execute('ROLLBACK')) {
$this->_transactionStarted = false;
return true;
}
return false;
}
/**
* Creates a default set of conditions from the model if $conditions is null/empty.
*
@ -1523,26 +1462,19 @@ class DboSource extends DataSource {
}
return $key;
}
/**
* Returns the column type of a given
*
* @param Model $model
* @param string $field
*/
function getColumnType(&$model, $field) {
return $model->getColumnType($field);
}
/**
* Private helper method to remove query metadata in given data array.
*
* @param array $data
* @return array
*/
function __scrubQueryData(&$data) {
function __scrubQueryData($data) {
foreach (array('conditions', 'fields', 'joins', 'order', 'limit', 'offset') as $key) {
if (!isset($data[$key]) || empty($data[$key])) {
$data[$key] = array();
}
}
return $data;
}
/**
* Generates the fields list of an SQL query.
@ -1782,14 +1714,12 @@ class DboSource extends DataSource {
* @access private
*/
function __quoteFields($conditions) {
$start = null;
$end = null;
$start = $end = null;
$original = $conditions;
if (!empty($this->startQuote)) {
$start = preg_quote($this->startQuote);
}
if (!empty($this->endQuote)) {
$end = preg_quote($this->endQuote);
}
@ -1840,8 +1770,7 @@ class DboSource extends DataSource {
*/
function order($keys, $direction = 'ASC') {
if (is_string($keys) && strpos($keys, ',') && !preg_match('/\(.+\,.+\)/', $keys)) {
$keys = explode(',', $keys);
array_map('trim', $keys);
$keys = array_map('trim', explode(',', $keys));
}
if (is_array($keys)) {
@ -1949,9 +1878,8 @@ class DboSource extends DataSource {
if (is_array($out)) {
return $out[0]['count'];
} else {
return false;
}
return false;
}
/**
* Gets the length of a database-native column description, or null if no length
@ -1966,7 +1894,6 @@ class DboSource extends DataSource {
if (strpos($col, '(') !== false) {
list($col, $limit) = explode('(', $col);
}
if ($limit != null) {
return intval($limit);
}
@ -1984,25 +1911,10 @@ class DboSource extends DataSource {
return 1;
}
return 0;
} else {
if (!empty($data)) {
return true;
}
return false;
return !empty($data);
}
}
/**
* Destructor. Closes connection to the database.
*
*/
function __destruct() {
if ($this->_transactionStarted) {
$null = null;
$this->rollback($null);
}
parent::__destruct();
}
/**
* Inserts multiple values into a join table
*

View file

@ -1549,21 +1549,15 @@ class Model extends Overloadable {
$db =& ConnectionManager::getDataSource($this->useDbConfig);
foreach ($this->hasAndBelongsToMany as $assoc => $data) {
if (isset($data['with'])) {
$records = $this->{$data['with']}->find('all', array(
'conditions' => array($data['foreignKey'] => $id),
'fields' => $this->{$data['with']}->primaryKey,
'recursive' => -1
));
if (!empty($records)) {
foreach ($records as $record) {
$this->{$data['with']}->delete($record[$this->{$data['with']}->alias][$this->{$data['with']}->primaryKey]);
}
$records = $this->{$data['with']}->find('all', array(
'conditions' => array($data['foreignKey'] => $id),
'fields' => $this->{$data['with']}->primaryKey,
'recursive' => -1
));
if (!empty($records)) {
foreach ($records as $record) {
$this->{$data['with']}->delete($record[$this->{$data['with']}->alias][$this->{$data['with']}->primaryKey]);
}
} else {
$table = $db->name($db->fullTableName($data['joinTable']));
$conditions = $db->name($data['foreignKey']) . ' = ' . $db->value($id);
$db->query("DELETE FROM {$table} WHERE {$conditions}");
}
}
}
@ -1785,7 +1779,7 @@ class Model extends Overloadable {
if ($state == 'before') {
if (empty($query['fields'])) {
$db =& ConnectionManager::getDataSource($this->useDbConfig);
$query['fields'] = 'COUNT(*) AS ' . $db->name('count');
$query['fields'] = $db->calculate('count');
}
$query['order'] = false;
return $query;

View file

@ -114,16 +114,27 @@ class DboPostgresTest extends CakeTestCase {
* @access public
*/
function skip() {
$this->_initDb();
$db = ConnectionManager::getDataSource('test_suite');
$this->skipif ($this->db->config['driver'] != 'postgres', 'PostgreSQL connection not available');
}
/**
* Set up test suite database connection
*
* @access public
*/
function startTest() {
$this->_initDb();
}
/**
* Sets up a Dbo class instance for testing
*
* @access public
*/
function setUp() {
$this->startTest();
$db = ConnectionManager::getDataSource('test_suite');
$this->db = new DboPostgresTestDb($db->config);
$this->model = new PostgresTestModel();
@ -173,6 +184,11 @@ class DboPostgresTest extends CakeTestCase {
$result = $this->db->value('1,2', 'float');
$this->assertIdentical($expected, $result);
}
function testColumnParsing() {
var_export($this->db->isConnected());
var_export($this->db->fetchAll("SELECT table_name as name FROM INFORMATION_SCHEMA.tables;"));
}
}
?>

View file

@ -247,7 +247,7 @@ class TestModel8 extends CakeTestModel {
var $hasOne = array(
'TestModel9' => array(
'className' => 'TestModel9',
'foreignKey' => 'test_model9_id',
'foreignKey' => 'test_model8_id',
'conditions' => 'TestModel9.name != \'mariano\''
)
);
@ -678,7 +678,7 @@ class DboSourceTest extends CakeTestCase {
$external = isset($assocData['external']);
if ($this->Model->Category2->alias == $linkModel->alias && $type != 'hasAndBelongsToMany' && $type != 'hasMany') {
$result = $this->db->generateSelfAssociationQuery($this->Model->Category2, $linkModel, $type, $assoc, $assocData, $queryData, $external, $null);
$result = $this->db->generateAssociationQuery($this->Model->Category2, $linkModel, $type, $assoc, $assocData, $queryData, $external, $null);
$this->assertTrue($result);
} else {
if ($this->Model->Category2->useDbConfig == $linkModel->useDbConfig) {
@ -690,7 +690,7 @@ class DboSourceTest extends CakeTestCase {
}
$query = $this->db->generateAssociationQuery($this->Model->Category2, $null, null, null, null, $queryData, false, $null);
$this->assertPattern('/^SELECT\s+(.+)FROM(.+)`Category2`\.`group_id`\s+=\s+`Group`\.`id`\)\s+WHERE/', $query);
$this->assertPattern('/^SELECT\s+(.+)FROM(.+)`Category2`\.`group_id`\s+=\s+`Group`\.`id`\)\s+LEFT JOIN(.+)WHERE\s+1 = 1\s*$/', $query);
$this->Model = new TestModel4();
$this->Model->schema();
@ -703,10 +703,11 @@ class DboSourceTest extends CakeTestCase {
$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
$result = $this->db->generateSelfAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
$_queryData = $queryData;
$result = $this->db->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
$this->assertTrue($result);
$expected = array(array(
$expected = array(
'fields' => array(
'`TestModel4`.`id`',
'`TestModel4`.`name`',
@ -722,18 +723,15 @@ class DboSourceTest extends CakeTestCase {
'table' => '`test_model4`',
'alias' => 'TestModel4Parent',
'type' => 'LEFT',
'conditions' => array('`TestModel4`.`parent_id`' => '{$__cakeIdentifier[TestModel4Parent.id]__$}')
'conditions' => '`TestModel4`.`parent_id` = `TestModel4Parent`.`id`'
)
),
'table' => '`test_model4`',
'alias' => 'TestModel4',
'limit' => array(),
'offset' => array(),
'conditions' => array(),
'order' => array()
));
$this->assertEqual($queryData['selfJoin'], $expected);
);
$this->assertEqual($queryData, $expected);
$result = $this->db->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
$this->assertPattern('/^SELECT\s+`TestModel4`\.`id`, `TestModel4`\.`name`, `TestModel4`\.`created`, `TestModel4`\.`updated`, `TestModel4Parent`\.`id`, `TestModel4Parent`\.`name`, `TestModel4Parent`\.`created`, `TestModel4Parent`\.`updated`\s+/', $result);
@ -743,9 +741,9 @@ class DboSourceTest extends CakeTestCase {
$params['assocData']['type'] = 'INNER';
$this->Model->belongsTo['TestModel4Parent']['type'] = 'INNER';
$result = $this->db->generateSelfAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
$result = $this->db->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $_queryData, $params['external'], $resultSet);
$this->assertTrue($result);
$this->assertEqual($queryData['joins'][0]['type'], 'INNER');
$this->assertEqual($_queryData['joins'][0]['type'], 'INNER');
}
function testGenerateInnerJoinAssociationQuery() {
@ -777,16 +775,14 @@ class DboSourceTest extends CakeTestCase {
$null = null;
$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
$result = $this->db->generateSelfAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
$_queryData = $queryData;
$result = $this->db->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
$this->assertTrue($result);
$result = $this->db->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
$this->assertPattern('/^SELECT\s+`TestModel8`\.`id`, `TestModel8`\.`test_model9_id`, `TestModel8`\.`name`, `TestModel8`\.`created`, `TestModel8`\.`updated`, `TestModel9`\.`id`, `TestModel9`\.`test_model8_id`, `TestModel9`\.`name`, `TestModel9`\.`created`, `TestModel9`\.`updated`\s+/', $result);
$this->assertPattern('/FROM\s+`test_model8` AS `TestModel8`\s+LEFT JOIN\s+`test_model9` AS `TestModel9`/', $result);
$this->assertPattern('/\s+ON\s+\(`TestModel8`.`test_model9_id` = `TestModel9`.`id`\s+AND\s+`TestModel9`\.`name` != \'mariano\'\)\s+WHERE/', $result);
$this->assertPattern('/\s+ON\s+\(`TestModel9`\.`name` != \'mariano\'\s+AND\s+`TestModel9`.`test_model8_id` = `TestModel8`.`id`\)\s+WHERE/', $result);
$this->assertPattern('/\s+WHERE\s+(?:\()?1\s+=\s+1(?:\))?\s*$/', $result);
}
@ -801,16 +797,13 @@ class DboSourceTest extends CakeTestCase {
$null = null;
$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
$result = $this->db->generateSelfAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
$result = $this->db->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
$this->assertTrue($result);
$result = $this->db->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
$this->assertPattern('/^SELECT\s+`TestModel9`\.`id`, `TestModel9`\.`test_model8_id`, `TestModel9`\.`name`, `TestModel9`\.`created`, `TestModel9`\.`updated`, `TestModel8`\.`id`, `TestModel8`\.`test_model9_id`, `TestModel8`\.`name`, `TestModel8`\.`created`, `TestModel8`\.`updated`\s+/', $result);
$this->assertPattern('/FROM\s+`test_model9` AS `TestModel9`\s+LEFT JOIN\s+`test_model8` AS `TestModel8`/', $result);
$this->assertPattern('/\s+ON\s+\(`TestModel9`.`test_model8_id` = `TestModel8`.`id`\s+AND\s+`TestModel8`\.`name` != \'larry\'\)\s+WHERE/', $result);
$this->assertPattern('/\s+ON\s+\(`TestModel8`\.`name` != \'larry\'\s+AND\s+`TestModel9`.`test_model8_id` = `TestModel8`.`id`\)\s+WHERE/', $result);
$this->assertPattern('/\s+WHERE\s+(?:\()?1\s+=\s+1(?:\))?\s*$/', $result);
}
@ -826,8 +819,7 @@ class DboSourceTest extends CakeTestCase {
$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
$result = $this->db->generateSelfAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
$result = $this->db->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
$this->assertTrue($result);
$result = $this->db->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
@ -857,8 +849,7 @@ class DboSourceTest extends CakeTestCase {
$params = &$this->_prepareAssociationQuery($this->Featured2, $queryData, $binding);
$result = $this->db->generateSelfAssociationQuery($this->Featured2, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
$result = $this->db->generateAssociationQuery($this->Featured2, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
$this->assertTrue($result);
$result = $this->db->generateAssociationQuery($this->Featured2, $null, null, null, null, $queryData, false, $null);
@ -867,9 +858,10 @@ class DboSourceTest extends CakeTestCase {
'/^SELECT\s+`Featured2`\.`id`, `Featured2`\.`article_id`, `Featured2`\.`category_id`, `Featured2`\.`name`,\s+'.
'`ArticleFeatured2`\.`id`, `ArticleFeatured2`\.`title`, `ArticleFeatured2`\.`user_id`, `ArticleFeatured2`\.`published`\s+' .
'FROM\s+`featured2` AS `Featured2`\s+LEFT JOIN\s+`article_featured` AS `ArticleFeatured2`' .
'\s+ON\s+\(`Featured2`\.`article_featured2_id` = `ArticleFeatured2`\.`id`\s+AND\s+`ArticleFeatured2`.`published` = \'Y\'\)' .
'\s+ON\s+\(`ArticleFeatured2`.`published` = \'Y\'\s+AND\s+`Featured2`\.`article_featured2_id` = `ArticleFeatured2`\.`id`\)' .
'\s+WHERE\s+1\s+=\s+1\s*$/',
$result);
$result
);
}
function testGenerateAssociationQueryHasOne() {
@ -1367,16 +1359,9 @@ class DboSourceTest extends CakeTestCase {
$linkModel =& $model->{$className};
$external = isset($assocData['external']);
$queryData = $this->db->__scrubQueryData($queryData);
$this->db->__scrubQueryData($queryData);
$result = array(
'linkModel'=> &$linkModel,
'type'=> $type,
'assoc'=> $assoc,
'assocData'=> $assocData,
'external'=> $external
);
$result = array_merge(array('linkModel' => &$linkModel), compact('type', 'assoc', 'assocData', 'external'));
return $result;
}
@ -2152,6 +2137,29 @@ class DboSourceTest extends CakeTestCase {
$result = $this->db->order("Model.name+0 ASC");
$this->assertPattern("/^\s*ORDER BY\s+`Model`\.`name`\+0\s+ASC\s*$/", $result);
}
function testCalculations() {
$result = $this->db->calculate('count');
$this->assertEqual($result, 'COUNT(*) AS `count`');
$result = $this->db->calculate('count', array('id'));
$this->assertEqual($result, 'COUNT(`id`) AS `count`');
$result = $this->db->calculate('count', array('id', 'id_count'));
$this->assertEqual($result, 'COUNT(`id`) AS `id_count`');
$result = $this->db->calculate('count', array('Model.id', 'id_count'));
$this->assertEqual($result, 'COUNT(`Model`.`id`) AS `id_count`');
$result = $this->db->calculate('max', array('id'));
$this->assertEqual($result, 'MAX(`id`) AS `id`');
$result = $this->db->calculate('max', array('Model.id', 'id'));
$this->assertEqual($result, 'MAX(`Model`.`id`) AS `id`');
$result = $this->db->calculate('max', array('`Model`.`id`', 'id'));
$this->assertEqual($result, 'MAX(`Model`.`id`) AS `id`');
}
}
?>

View file

@ -83,6 +83,7 @@ class ModelTest extends CakeTestCase {
$this->DeviceType->recursive = 2;
$result = $this->DeviceType->read(null, 1);
$expected = array(
'DeviceType' => array(
'id' => 1, 'device_type_category_id' => 1, 'feature_set_id' => 1, 'exterior_type_category_id' => 1, 'image_id' => 1,
@ -143,12 +144,33 @@ class ModelTest extends CakeTestCase {
$this->assertEqual($result['Tag'], $expected);
}
function testHasManyOptimization() {
function testHabtmLimitOptimization() {
$this->loadFixtures('Article', 'User', 'Comment', 'Tag', 'ArticlesTag');
$this->model =& new Article();
$this->model->hasAndBelongsToMany['Tag']['limit'] = 1;
$result = $this->model->read(null, 2);
$expected = array(
'Article' => array('id' => '2', 'user_id' => '3', 'title' => 'Second Article', 'body' => 'Second Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'),
'User' => array('id' => '3', 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'),
'Comment' => array(
array('id' => '5', 'article_id' => '2', 'user_id' => '1', 'comment' => 'First Comment for Second Article', 'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31'),
array('id' => '6', 'article_id' => '2', 'user_id' => '2', 'comment' => 'Second Comment for Second Article', 'published' => 'Y', 'created' => '2007-03-18 10:55:23', 'updated' => '2007-03-18 10:57:31')
),
'Tag' => array(
array('id' => '1', 'tag' => 'tag1', 'created' => '2007-03-18 12:22:23', 'updated' => '2007-03-18 12:24:31'),
array('id' => '3', 'tag' => 'tag3', 'created' => '2007-03-18 12:26:23', 'updated' => '2007-03-18 12:28:31')
)
);
//$this->assertEqual($result, $expected);
}
function testHasManyLimitOptimization() {
$this->loadFixtures('Project', 'Thread', 'Message', 'Bid');
$this->Project =& new Project();
$this->Project->recursive = 3;
$result = $this->Project->findAll();
$result = $this->Project->find('all');
$expected = array(
array('Project' => array('id' => 1, 'name' => 'Project 1'),
'Thread' => array(array('id' => 1, 'project_id' => 1, 'name' => 'Project 1, Thread 1',
@ -1702,14 +1724,11 @@ class ModelTest extends CakeTestCase {
'Tag' => array('Tag' => array(1, 2))
);
$result = $this->model->set($data);
$this->assertTrue($result);
$result = $this->model->save();
$this->assertTrue($result);
$this->assertTrue($this->model->set($data));
$this->assertTrue($this->model->save());
$this->model->unbindModel(array('belongsTo' => array('User'), 'hasMany' => array('Comment')));
$result = $this->model->find(array('Article.id'=>2), array('id', 'user_id', 'title', 'body'));
$result = $this->model->find(array('Article.id' => 2), array('id', 'user_id', 'title', 'body'));
$expected = array(
'Article' => array('id' => '2', 'user_id' => '3', 'title' => 'New Second Article', 'body' => 'Second Article Body'),
'Tag' => array(
@ -1981,13 +2000,16 @@ class ModelTest extends CakeTestCase {
$this->model =& new Article();
$this->model->belongsTo = $this->model->hasAndBelongsToMany = array();
$this->assertTrue($this->model->saveAll(array(
'Article' => array('id' => 2),
'Comment' => array(
array('comment' => 'First new comment', 'published' => 'Y', 'user_id' => 1),
array('comment' => 'Second new comment', 'published' => 'Y', 'user_id' => 2)
)
)));
$this->assertTrue($this->model->saveAll(
array(
'Article' => array('id' => 2),
'Comment' => array(
array('comment' => 'First new comment', 'published' => 'Y', 'user_id' => 1),
array('comment' => 'Second new comment', 'published' => 'Y', 'user_id' => 2)
)
),
array('atomic' => false)
));
$result = $this->model->findById(2);
$expected = array('First Comment for Second Article', 'Second Comment for Second Article', 'First new comment', 'Second new comment');
$this->assertEqual(Set::extract($result['Comment'], '{n}.comment'), $expected);
@ -2000,7 +2022,7 @@ class ModelTest extends CakeTestCase {
$data = array(
array('id' => '1', 'title' => 'Baleeted First Post', 'body' => 'Baleeted!', 'published' => 'N'),
array('id' => '2', 'title' => 'Just update the title'),
array('title' => 'Creating a fourth post', 'body' => 'Fourth post body')
array('title' => 'Creating a fourth post', 'body' => 'Fourth post body', 'author_id' => 2)
);
$ts = date('Y-m-d H:i:s');
$this->assertTrue($this->model->saveAll($data));
@ -2010,7 +2032,7 @@ class ModelTest extends CakeTestCase {
array('Post' => array('id' => '1', 'author_id' => '1', 'title' => 'Baleeted First Post', 'body' => 'Baleeted!', 'published' => 'N', 'created' => '2007-03-18 10:39:23', 'updated' => $ts)),
array('Post' => array('id' => '2', 'author_id' => '3', 'title' => 'Just update the title', 'body' => 'Second Post Body', 'published' => 'N', 'created' => '2007-03-18 10:41:23', 'updated' => $ts)),
array('Post' => array('id' => '3', 'author_id' => '1', 'title' => 'Third Post', 'body' => 'Third Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31')),
array('Post' => array('id' => '4', 'author_id' => '0', 'title' => 'Creating a fourth post', 'body' => 'Fourth post body', 'published' => 'N', 'created' => $ts, 'updated' => $ts))
array('Post' => array('id' => '4', 'author_id' => '2', 'title' => 'Creating a fourth post', 'body' => 'Fourth post body', 'published' => 'N', 'created' => $ts, 'updated' => $ts))
);
$this->assertEqual($result, $expected);
@ -2077,17 +2099,17 @@ class ModelTest extends CakeTestCase {
$this->model =& new Syfile();
$this->model2 =& new Item();
$this->model2->belongsTo['Syfile']['counterCache'] = true;
$this->model2->belongsTo['Syfile']['counterScope'] = 'published = 1';
$this->model2->belongsTo['Syfile']['counterScope'] = array('published' => true);
$result = $this->model->findById(1);
$this->assertIdentical($result['Syfile']['item_count'], null);
$this->model2->save(array('name' => 'Item 7', 'syfile_id' => 1, 'published'=> 1));
$this->model2->save(array('name' => 'Item 7', 'syfile_id' => 1, 'published'=> true));
$result = $this->model->findById(1);
$this->assertIdentical($result['Syfile']['item_count'], '1');
$this->model2->id = 1;
$this->model2->saveField('published', 1);
$this->model2->saveField('published', true);
$result = $this->model->findById(1);
$this->assertIdentical($result['Syfile']['item_count'], '2');
}
@ -2220,6 +2242,29 @@ class ModelTest extends CakeTestCase {
$this->assertEqual($this->model->Comment->find('count'), 2);
}
function testDeleteLinks() {
$this->loadFixtures('Article', 'ArticlesTag', 'Tag');
$this->model =& new Article();
$result = $this->model->ArticlesTag->find('all');
$expected = array(
array('ArticlesTag' => array('article_id' => '1', 'tag_id' => '1')),
array('ArticlesTag' => array('article_id' => '1', 'tag_id' => '2')),
array('ArticlesTag' => array('article_id' => '2', 'tag_id' => '1')),
array('ArticlesTag' => array('article_id' => '2', 'tag_id' => '3'))
);
$this->assertEqual($result, $expected);
$this->Article->delete(1);
$result = $this->model->ArticlesTag->find('all');
$expected = array(
array('ArticlesTag' => array('article_id' => '2', 'tag_id' => '1')),
array('ArticlesTag' => array('article_id' => '2', 'tag_id' => '3'))
);
$this->assertEqual($result, $expected);
}
function testFindAllThreaded() {
$this->loadFixtures('Category');
$this->model =& new Category();

View file

@ -130,7 +130,7 @@ class ArticleFeatured extends CakeTestModel {
var $name = 'ArticleFeatured';
var $belongsTo = array('User', 'Category');
var $hasOne = array('Featured');
var $hasMany = array('Comment' => array('className'=>'Comment', 'dependent' => true));
var $hasMany = array('Comment' => array('className' => 'Comment', 'dependent' => true));
var $hasAndBelongsToMany = array('Tag');
var $validate = array('user_id' => VALID_NUMBER, 'title' => VALID_NOT_EMPTY, 'body' => VALID_NOT_EMPTY);
}
@ -142,10 +142,7 @@ class ArticleFeatured extends CakeTestModel {
*/
class Featured extends CakeTestModel {
var $name = 'Featured';
var $belongsTo = array(
'ArticleFeatured'=> array('className' => 'ArticleFeatured'),
'Category'=> array('className' => 'Category')
);
var $belongsTo = array('ArticleFeatured', 'Category');
}
/**
@ -354,7 +351,7 @@ class NodeNoAfterFind extends CakeTestModel {
var $validate = array('name' => VALID_NOT_EMPTY);
var $useTable = 'apples';
var $hasOne = array('Sample' => array('className' => 'NodeAfterFindSample'));
var $hasMany = array('Child' => array( 'className' => 'NodeAfterFind', 'dependent' => true));
var $hasMany = array('Child' => array('className' => 'NodeAfterFind', 'dependent' => true));
var $belongsTo = array('Parent' => array('className' => 'NodeAfterFind', 'foreignKey' => 'apple_id'));
}
class ModelA extends CakeTestModel {
@ -455,6 +452,11 @@ class JoinC extends CakeTestModel {
var $name = 'JoinC';
var $hasAndBelongsToMany = array('JoinA');
}
class ThePaper extends CakeTestModel {
var $name = 'ThePaper';
var $useTable = 'apples';
var $hasOne = array('Itself' => array('className' => 'ThePaper', 'foreignKey' => 'apple_id'));
}
/**
* Short description for class.
*