From c1c35b2e13d939fe279d42b47e8d6d0190dc0325 Mon Sep 17 00:00:00 2001 From: nate Date: Tue, 11 Mar 2008 02:44:33 +0000 Subject: [PATCH] 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 --- cake/libs/model/behaviors/tree.php | 3 +- cake/libs/model/datasources/datasource.php | 10 +- cake/libs/model/datasources/dbo/dbo_mssql.php | 37 +-- .../libs/model/datasources/dbo/dbo_mysqli.php | 37 +-- .../model/datasources/dbo/dbo_postgres.php | 62 +--- cake/libs/model/datasources/dbo_source.php | 296 ++++++------------ cake/libs/model/model.php | 24 +- .../datasources/dbo/dbo_postgres.test.php | 16 + .../model/datasources/dbo_source.test.php | 84 ++--- cake/tests/cases/libs/model/model.test.php | 85 +++-- cake/tests/cases/libs/model/models.php | 14 +- 11 files changed, 278 insertions(+), 390 deletions(-) diff --git a/cake/libs/model/behaviors/tree.php b/cake/libs/model/behaviors/tree.php index e953b6610..2473b5efd 100644 --- a/cake/libs/model/behaviors/tree.php +++ b/cake/libs/model/behaviors/tree.php @@ -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]); } /** diff --git a/cake/libs/model/datasources/datasource.php b/cake/libs/model/datasources/datasource.php index 7bcd9b388..247d0046f 100644 --- a/cake/libs/model/datasources/datasource.php +++ b/cake/libs/model/datasources/datasource.php @@ -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(); } diff --git a/cake/libs/model/datasources/dbo/dbo_mssql.php b/cake/libs/model/datasources/dbo/dbo_mssql.php index 7ea974057..e7ee5b8d4 100644 --- a/cake/libs/model/datasources/dbo/dbo_mssql.php +++ b/cake/libs/model/datasources/dbo/dbo_mssql.php @@ -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; } diff --git a/cake/libs/model/datasources/dbo/dbo_mysqli.php b/cake/libs/model/datasources/dbo/dbo_mysqli.php index 669215b01..2c1e8e15d 100644 --- a/cake/libs/model/datasources/dbo/dbo_mysqli.php +++ b/cake/libs/model/datasources/dbo/dbo_mysqli.php @@ -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; } diff --git a/cake/libs/model/datasources/dbo/dbo_postgres.php b/cake/libs/model/datasources/dbo/dbo_postgres.php index 0d85f31c6..815ed94d5 100644 --- a/cake/libs/model/datasources/dbo/dbo_postgres.php +++ b/cake/libs/model/datasources/dbo/dbo_postgres.php @@ -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; } diff --git a/cake/libs/model/datasources/dbo_source.php b/cake/libs/model/datasources/dbo_source.php index 358ed92be..4074e64e9 100644 --- a/cake/libs/model/datasources/dbo_source.php +++ b/cake/libs/model/datasources/dbo_source.php @@ -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 * diff --git a/cake/libs/model/model.php b/cake/libs/model/model.php index 38d7c069a..f3ee75a03 100644 --- a/cake/libs/model/model.php +++ b/cake/libs/model/model.php @@ -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; diff --git a/cake/tests/cases/libs/model/datasources/dbo/dbo_postgres.test.php b/cake/tests/cases/libs/model/datasources/dbo/dbo_postgres.test.php index aed87c038..964b8524e 100644 --- a/cake/tests/cases/libs/model/datasources/dbo/dbo_postgres.test.php +++ b/cake/tests/cases/libs/model/datasources/dbo/dbo_postgres.test.php @@ -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;")); + } } ?> \ No newline at end of file diff --git a/cake/tests/cases/libs/model/datasources/dbo_source.test.php b/cake/tests/cases/libs/model/datasources/dbo_source.test.php index dc23b6826..a945f33af 100644 --- a/cake/tests/cases/libs/model/datasources/dbo_source.test.php +++ b/cake/tests/cases/libs/model/datasources/dbo_source.test.php @@ -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`'); + } } ?> \ No newline at end of file diff --git a/cake/tests/cases/libs/model/model.test.php b/cake/tests/cases/libs/model/model.test.php index f23fe8aca..09789e994 100644 --- a/cake/tests/cases/libs/model/model.test.php +++ b/cake/tests/cases/libs/model/model.test.php @@ -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(); diff --git a/cake/tests/cases/libs/model/models.php b/cake/tests/cases/libs/model/models.php index 49f14150d..d9869cacf 100644 --- a/cake/tests/cases/libs/model/models.php +++ b/cake/tests/cases/libs/model/models.php @@ -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. *