Implemented 'counterCache' and 'counterScope' for belongsTo associations

Exempted 'created', 'updated' and 'modified' fields from whitelist when saving, closes #3720
Updated Model::bind(), closes #2355
Adding Model::saveAll() to save multiple records and associated records, closes #3615
Replacing Model::generateList() with Model::find('list'), deprecates Model::generateList()



git-svn-id: https://svn.cakephp.org/repo/branches/1.2.x.x@6295 3807eeeb-6ff5-0310-8944-8be069107fe0
This commit is contained in:
nate 2008-01-01 19:34:40 +00:00
parent 43e05117fa
commit 5abe66289d
9 changed files with 339 additions and 71 deletions

View file

@ -293,7 +293,7 @@ class ControllerTask extends Shell {
$habtmModelName = $this->_modelName($associationName); $habtmModelName = $this->_modelName($associationName);
$habtmSingularName = $this->_singularName($associationName); $habtmSingularName = $this->_singularName($associationName);
$habtmPluralName = $this->_pluralName($associationName); $habtmPluralName = $this->_pluralName($associationName);
$actions .= "\t\t\${$habtmPluralName} = \$this->{$currentModelName}->{$habtmModelName}->generateList();\n"; $actions .= "\t\t\${$habtmPluralName} = \$this->{$currentModelName}->{$habtmModelName}->find('list');\n";
$compact[] = "'{$habtmPluralName}'"; $compact[] = "'{$habtmPluralName}'";
} }
} }
@ -301,7 +301,7 @@ class ControllerTask extends Shell {
if (!empty($associationName)) { if (!empty($associationName)) {
$belongsToModelName = $this->_modelName($associationName); $belongsToModelName = $this->_modelName($associationName);
$belongsToPluralName = $this->_pluralName($associationName); $belongsToPluralName = $this->_pluralName($associationName);
$actions .= "\t\t\${$belongsToPluralName} = \$this->{$currentModelName}->{$belongsToModelName}->generateList();\n"; $actions .= "\t\t\${$belongsToPluralName} = \$this->{$currentModelName}->{$belongsToModelName}->find('list');\n";
$compact[] = "'{$belongsToPluralName}'"; $compact[] = "'{$belongsToPluralName}'";
} }
} }
@ -347,7 +347,7 @@ class ControllerTask extends Shell {
$habtmModelName = $this->_modelName($associationName); $habtmModelName = $this->_modelName($associationName);
$habtmSingularName = $this->_singularName($associationName); $habtmSingularName = $this->_singularName($associationName);
$habtmPluralName = $this->_pluralName($associationName); $habtmPluralName = $this->_pluralName($associationName);
$actions .= "\t\t\${$habtmPluralName} = \$this->{$currentModelName}->{$habtmModelName}->generateList();\n"; $actions .= "\t\t\${$habtmPluralName} = \$this->{$currentModelName}->{$habtmModelName}->find('list');\n";
$compact[] = "'{$habtmPluralName}'"; $compact[] = "'{$habtmPluralName}'";
} }
} }
@ -355,7 +355,7 @@ class ControllerTask extends Shell {
if (!empty($associationName)) { if (!empty($associationName)) {
$belongsToModelName = $this->_modelName($associationName); $belongsToModelName = $this->_modelName($associationName);
$belongsToPluralName = $this->_pluralName($associationName); $belongsToPluralName = $this->_pluralName($associationName);
$actions .= "\t\t\${$belongsToPluralName} = \$this->{$currentModelName}->{$belongsToModelName}->generateList();\n"; $actions .= "\t\t\${$belongsToPluralName} = \$this->{$currentModelName}->{$belongsToModelName}->find('list');\n";
$compact[] = "'{$belongsToPluralName}'"; $compact[] = "'{$belongsToPluralName}'";
} }
} }

View file

@ -310,11 +310,11 @@ class Scaffold extends Object {
foreach ($this->ScaffoldModel->belongsTo as $assocName => $assocData) { foreach ($this->ScaffoldModel->belongsTo as $assocName => $assocData) {
$varName = Inflector::variable(Inflector::pluralize(preg_replace('/_id$/', '', $assocData['foreignKey']))); $varName = Inflector::variable(Inflector::pluralize(preg_replace('/_id$/', '', $assocData['foreignKey'])));
$this->controller->set($varName, $this->ScaffoldModel->{$assocName}->generateList()); $this->controller->set($varName, $this->ScaffoldModel->{$assocName}->find('list'));
} }
foreach ($this->ScaffoldModel->hasAndBelongsToMany as $assocName => $assocData) { foreach ($this->ScaffoldModel->hasAndBelongsToMany as $assocName => $assocData) {
$varName = Inflector::variable(Inflector::pluralize($assocName)); $varName = Inflector::variable(Inflector::pluralize($assocName));
$this->controller->set($varName, $this->ScaffoldModel->{$assocName}->generateList()); $this->controller->set($varName, $this->ScaffoldModel->{$assocName}->find('list'));
} }
return $this->__scaffoldForm($formAction); return $this->__scaffoldForm($formAction);

View file

@ -239,7 +239,7 @@ class TranslateBehavior extends ModelBehavior {
$RuntimeModel =& $this->translateModel($model); $RuntimeModel =& $this->translateModel($model);
if (empty($created)) { if (empty($created)) {
$translations = $RuntimeModel->generateList(array_merge($conditions, array($RuntimeModel->displayField => array_keys($tempData)))); $translations = $RuntimeModel->find('list', array('conditions' => array_merge($conditions, array($RuntimeModel->displayField => array_keys($tempData)))));
if ($translations) { if ($translations) {
foreach ($translations as $id => $field) { foreach ($translations as $id => $field) {

View file

@ -246,6 +246,30 @@ class DataSource extends Object {
} }
return null; return null;
} }
/**
* Begin a transaction
*
* @return boolean True
*/
function begin() {
return true;
}
/**
* Commit a transaction
*
* @return boolean True
*/
function commit(&$model) {
return true;
}
/**
* Rollback a transaction
*
* @return boolean True
*/
function rollback(&$model) {
return true;
}
/** /**
* Converts column types to basic types * Converts column types to basic types
* *

View file

@ -310,7 +310,7 @@ class Model extends Overloadable {
* @var array * @var array
* @access private * @access private
*/ */
var $__findMethods = array('all' => true, 'first' => true, 'count' => true, 'neighbors' => true); var $__findMethods = array('all' => true, 'first' => true, 'count' => true, 'neighbors' => true, 'list' => true);
/** /**
* Constructor. Binds the Model's database table to the object. * Constructor. Binds the Model's database table to the object.
* *
@ -495,13 +495,14 @@ class Model extends Overloadable {
* If $permanent is true, association will not be reset * If $permanent is true, association will not be reset
* to the originals defined in the model. * to the originals defined in the model.
* *
* @param array $model Set of binding options (indexed by model name type) * @param mixed $model A model or association name (string) or set of binding options (indexed by model name type)
* @param array $options Currently not used * @param array $options If $model is a string, this is the list of association properties with which $model will
* be bound
* @param boolean $permanent Set to true to make the binding permanent * @param boolean $permanent Set to true to make the binding permanent
* @access public * @access public
* @todo * @todo
*/ */
function bind($model, $options, $permanent = true) { function bind($model, $options = array(), $permanent = true) {
if (!is_array($model)) { if (!is_array($model)) {
$model = array($model => $options); $model = array($model => $options);
} }
@ -511,21 +512,28 @@ class Model extends Overloadable {
$assoc = $options['type']; $assoc = $options['type'];
} elseif (isset($options[0])) { } elseif (isset($options[0])) {
$assoc = $options[0]; $assoc = $options[0];
} else {
$assoc = 'belongsTo';
} }
if (!$permanent) { if (!$permanent) {
$this->__backAssociation[$assoc] = $this->{$assoc}; $this->__backAssociation[$assoc] = $this->{$assoc};
} }
foreach ($model as $key => $value) { foreach ($model as $key => $value) {
$assocName = $key; $assocName = $modelName = $key;
$modelName = $key;
if (isset($value['className'])) { if (isset($this->{$assoc}[$assocName])) {
$modelName = $value['className']; $this->{$assoc}[$assocName] = array_merge($this->{$assoc}[$assocName], $options);
} else {
if (isset($value['className'])) {
$modelName = $value['className'];
}
$this->__constructLinkedModel($assocName, $modelName);
$this->{$assoc}[$assocName] = $model[$assocName];
$this->__generateAssociation($assoc);
} }
unset($this->{$assoc}[$assocName]['type'], $this->{$assoc}[$assocName][0]);
$this->__constructLinkedModel($assocName, $modelName);
$this->{$assoc}[$assocName] = $model[$assocName];
$this->__generateAssociation($assoc);
} }
} }
} }
@ -1124,6 +1132,11 @@ class Model extends Overloadable {
$fields = array_keys($this->data[$this->alias]); $fields = array_keys($this->data[$this->alias]);
} }
if ($validate && !$this->validates()) {
$this->whitelist = $_whitelist;
return false;
}
foreach ($dateFields as $updateCol) { foreach ($dateFields as $updateCol) {
if ($this->hasField($updateCol) && !in_array($updateCol, $fields)) { if ($this->hasField($updateCol) && !in_array($updateCol, $fields)) {
$colType = array_merge(array('formatter' => 'date'), $db->columns[$this->getColumnType($updateCol)]); $colType = array_merge(array('formatter' => 'date'), $db->columns[$this->getColumnType($updateCol)]);
@ -1132,15 +1145,13 @@ class Model extends Overloadable {
} else { } else {
$time = $colType['formatter']($colType['format']); $time = $colType['formatter']($colType['format']);
} }
if (!empty($this->whitelist)) {
$this->whitelist[] = $updateCol;
}
$this->set($updateCol, $time); $this->set($updateCol, $time);
} }
} }
if ($validate && !$this->validates()) {
$this->whitelist = $_whitelist;
return false;
}
if (!empty($this->behaviors)) { if (!empty($this->behaviors)) {
$behaviors = array_keys($this->behaviors); $behaviors = array_keys($this->behaviors);
$ct = count($behaviors); $ct = count($behaviors);
@ -1207,11 +1218,7 @@ class Model extends Overloadable {
} else { } else {
$created = true; $created = true;
if (!empty($this->belongsTo)) { if (!empty($this->belongsTo)) {
foreach ($this->belongsTo as $parent => $assoc) { $this->updateCounterCache();
if (isset($assoc['counterCache']) && !empty($assoc['counterCache'])) {
$parentObj =& $this->{$assoc['className']};
}
}
} }
} }
} }
@ -1294,6 +1301,103 @@ class Model extends Overloadable {
} }
} }
} }
/**
* Updates the counter cache of belongsTo associations after a save or delete operation
*
* @param array $keys Optional foreign key data, defaults to the information $this->data
* @return void
* @access public
*/
function updateCounterCache($keys = array()) {
if (empty($keys)) {
$keys = $this->data[$this->alias];
}
foreach ($this->belongsTo as $parent => $assoc) {
if (!empty($assoc['counterCache'])) {
if ($assoc['counterCache'] === true) {
$assoc['counterCache'] = Inflector::underscore($this->alias) . '_count';
}
if ($this->{$parent}->hasField($assoc['counterCache'])) {
$conditions = array($this->escapeField($assoc['foreignKey']) => $keys[$assoc['foreignKey']]);
if (isset($assoc['counterScope'])) {
$conditions = array_merge($conditions, (array)$assoc['counterScope']);
}
$this->{$parent}->updateAll(
array($assoc['counterCache'] => intval($this->find('count', compact('conditions')))),
array($this->{$parent}->escapeField() => $keys[$assoc['foreignKey']])
);
}
}
}
}
/**
* Saves (a) multiple individual records for a single model or (b) this record, as well as
* all associated records
*
* @param array $data Record data to save
* @param array $options
* @return mixed True on success, or an array of validation errors on failure
* @access public
*/
function saveAll($data = null, $options = array()) {
if (empty($data)) {
return false;
}
$db =& ConnectionManager::getDataSource($this->useDbConfig);
$options = array_merge(
array('validate' => true, 'fieldList' => array(), 'atomic' => true),
$options
);
if ($options['atomic']) {
$db->begin($this);
}
if (Set::numeric(array_keys($data))) {
foreach ($data as $record) {
if (!($this->create($record) && $this->save(null, $options['validate'], $options['fieldList'])) && $options['atomic']) {
$db->rollback($this);
return false;
}
}
} else {
$associations = $this->getAssociated();
foreach ($data as $association => $values) {
if (isset($associations[$association]) && ($type = $associations[$association]) == 'belongsTo') {
$alias = $this->{$association}->alias;
$foreignKey = $this->{$type}[$association]['foreignKey'];
if (!$result = $this->{$association}->save($values, $options['validate'], $options['fieldList'])) {
$db->rollback($this);
return false;
} elseif (!isset($data[$foreignKey]) || empty($data[$foreignKey])) {
$data[$this->alias][$foreignKey] = $this->{$association}->id;
}
}
}
if (!$this->save($data[$this->alias], $options['validate'], $options['fieldList'])) {
$db->rollback($this);
return false;
}
foreach ($data as $association => $values) {
if (isset($associations[$association])) {
switch ($associations[$association]) {
case 'hasMany':
$this->{$association}->saveAll($values);
break;
}
}
}
}
if ($options['atomic']) {
$db->commit($this);
}
}
/** /**
* Allows model records to be updated based on a set of conditions * Allows model records to be updated based on a set of conditions
* *
@ -1348,7 +1452,14 @@ class Model extends Overloadable {
$this->_deleteLinks($id); $this->_deleteLinks($id);
$this->id = $id; $this->id = $id;
if (!empty($this->belongsTo)) {
$keys = $this->find('first', array('fields', $this->__collectForeignKeys()));
}
if ($db->delete($this)) { if ($db->delete($this)) {
if (!empty($this->belongsTo)) {
$this->updateCounterCache($keys[$this->alias]);
}
if (!empty($this->behaviors)) { if (!empty($this->behaviors)) {
for ($i = 0; $i < $ct; $i++) { for ($i = 0; $i < $ct; $i++) {
$this->behaviors[$behaviors[$i]]->afterDelete($this); $this->behaviors[$behaviors[$i]]->afterDelete($this);
@ -1393,11 +1504,16 @@ class Model extends Overloadable {
$model =& $this->{$assoc}; $model =& $this->{$assoc};
$field = $model->escapeField($data['foreignKey']); $field = $model->escapeField($data['foreignKey']);
$model->recursive = -1; $model->recursive = -1;
$records = $model->findAll(array($field => $id), $model->primaryKey);
if (!empty($records)) { if (isset($data['exclusive']) && $data['exclusive']) {
foreach ($records as $record) { $model->deleteAll(array($field => $id));
$model->delete($record[$model->alias][$model->primaryKey]); } else {
$records = $model->findAll(array($field => $id), $model->primaryKey);
if (!empty($records)) {
foreach ($records as $record) {
$model->delete($record[$model->alias][$model->primaryKey]);
}
} }
} }
} }
@ -1476,6 +1592,21 @@ class Model extends Overloadable {
} }
} }
} }
/**
* Collects foreign keys from associations
*
* @access private
*/
function __collectForeignKeys($type = 'belongsTo') {
$result = array();
foreach ($this->{$type} as $assoc => $data) {
if (isset($data['foreignKey']) && is_string($data['foreignKey'])) {
$result[$assoc] = $data['foreignKey'];
}
}
return $result;
}
/** /**
* Returns true if a record with set id exists. * Returns true if a record with set id exists.
* *
@ -1547,13 +1678,26 @@ class Model extends Overloadable {
$query $query
); );
if ($type == 'count') { switch ($type) {
if (empty($query['fields'])) { case 'count' :
$query['fields'] = 'COUNT(*) AS ' . $db->name('count'); if (empty($query['fields'])) {
} $query['fields'] = 'COUNT(*) AS ' . $db->name('count');
$query['order'] = false; }
} elseif ($type == 'first') { $query['order'] = false;
$query['limit'] = 1; break;
case 'first' :
$query['limit'] = 1;
if (empty($query['conditions']) && !empty($this->id)) {
$query['conditions'] = array($this->escapeField() => $this->id);
}
break;
case 'list' :
if (empty($query['fields'])) {
$query['fields'] = array("{$this->alias}.{$this->primaryKey}", "{$this->alias}.{$this->displayField}");
}
$keyPath = "{n}.{$this->alias}.{$this->primaryKey}";
$valuePath = "{n}.{$this->alias}.{$this->displayField}";
break;
} }
if (!is_numeric($query['page']) || intval($query['page']) < 1) { if (!is_numeric($query['page']) || intval($query['page']) < 1) {
@ -1618,6 +1762,12 @@ class Model extends Overloadable {
} }
return false; return false;
break; break;
case 'list':
if (empty($results)) {
return array();
}
return Set::combine($results, $keyPath, $valuePath);
break;
} }
} }
/** /**
@ -1761,7 +1911,7 @@ class Model extends Overloadable {
if ($or) { if ($or) {
$fields = array('or' => $fields); $fields = array('or' => $fields);
} }
return ($this->findCount($fields) == 0); return ($this->find('count', array('conditions' => $fields)) == 0);
} }
/** /**
* Special findAll variation for tables joined to themselves. * Special findAll variation for tables joined to themselves.
@ -2028,6 +2178,8 @@ class Model extends Overloadable {
* @access public * @access public
*/ */
function generateList($conditions = null, $order = null, $limit = null, $keyPath = null, $valuePath = null, $groupPath = null) { function generateList($conditions = null, $order = null, $limit = null, $keyPath = null, $valuePath = null, $groupPath = null) {
trigger_error(__('(Model::generateList) Deprecated, use Model::find("list") or Model::find("all") and Set::combine()', true), E_USER_WARNING);
if ($keyPath == null && $valuePath == null && $groupPath == null && $this->hasField($this->displayField)) { if ($keyPath == null && $valuePath == null && $groupPath == null && $this->hasField($this->displayField)) {
$fields = array($this->primaryKey, $this->displayField); $fields = array($this->primaryKey, $this->displayField);
} else { } else {
@ -2048,11 +2200,11 @@ class Model extends Overloadable {
} }
if ($keyPath == null) { if ($keyPath == null) {
$keyPath = '{n}.' . $this->alias . '.' . $this->primaryKey; $keyPath = "{n}.{$this->alias}.{$this->primaryKey}";
} }
if ($valuePath == null) { if ($valuePath == null) {
$valuePath = '{n}.' . $this->alias . '.' . $this->displayField; $valuePath = "{n}.{$this->alias}.{$this->displayField}";
} }
return Set::combine($result, $keyPath, $valuePath, $groupPath); return Set::combine($result, $keyPath, $valuePath, $groupPath);

View file

@ -118,6 +118,17 @@ class Article extends CakeTestModel {
return true; return true;
} }
} }
/**
* Short description for class.
*
* @package cake.tests
* @subpackage cake.tests.cases.libs.model
*/
class Article10 extends CakeTestModel {
var $name = 'Article10';
var $useTable = 'articles';
var $hasMany = array('Comment' => array('dependent' => true, 'exclusive' => true));
}
/** /**
* Short description for class. * Short description for class.
* *
@ -392,7 +403,7 @@ class Portfolio extends CakeTestModel {
} }
class Item extends CakeTestModel { class Item extends CakeTestModel {
var $name = 'Item'; var $name = 'Item';
var $belongsTo = array('Syfile'); var $belongsTo = array('Syfile' => array('counterCache' => true));
var $hasAndBelongsToMany = array('Portfolio'); var $hasAndBelongsToMany = array('Portfolio');
} }
class ItemsPortfolio extends CakeTestModel { class ItemsPortfolio extends CakeTestModel {
@ -515,12 +526,12 @@ class ModelTest extends CakeTestCase {
'Item' => array( 'Item' => array(
array('id' => 2, 'syfile_id' => 2, 'name' => 'Item 2', array('id' => 2, 'syfile_id' => 2, 'name' => 'Item 2',
'ItemsPortfolio' => array('id' => 2, 'item_id' => 2, 'portfolio_id' => 2), 'ItemsPortfolio' => array('id' => 2, 'item_id' => 2, 'portfolio_id' => 2),
'Syfile' => array('id' => 2, 'image_id' => 2, 'name' => 'Syfile 2', 'Syfile' => array('id' => 2, 'image_id' => 2, 'name' => 'Syfile 2', 'item_count' => null,
'Image' => array('id' => 2, 'name' => 'Image 2'))), 'Image' => array('id' => 2, 'name' => 'Image 2'))),
array('id' => 6, 'syfile_id' => 6, 'name' => 'Item 6', array('id' => 6, 'syfile_id' => 6, 'name' => 'Item 6',
'ItemsPortfolio' => array('id' => 6, 'item_id' => 6, 'portfolio_id' => 2), 'ItemsPortfolio' => array('id' => 6, 'item_id' => 6, 'portfolio_id' => 2),
'Syfile' => array('id' => 6, 'image_id' => null, 'name' => 'Syfile 6', 'Syfile' => array('id' => 6, 'image_id' => null, 'name' => 'Syfile 6', 'item_count' => null,
'Image' => array())))); 'Image' => array()))));
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
unset($this->Portfolio); unset($this->Portfolio);
@ -915,35 +926,30 @@ class ModelTest extends CakeTestCase {
$this->model =& new Article(); $this->model =& new Article();
$this->model->displayField = 'title'; $this->model->displayField = 'title';
$result = $this->model->generateList(null, 'Article.title ASC'); $result = $this->model->find('list', array('order' => 'Article.title ASC'));
$expected = array(1 => 'First Article', 2 => 'Second Article', 3 => 'Third Article'); $expected = array(1 => 'First Article', 2 => 'Second Article', 3 => 'Third Article');
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
$result = $this->model->generateList(null, 'Article.title ASC', null, '{n}.Article.id'); $result = Set::combine($this->model->find('all', array('order' => 'Article.title ASC', 'fields' => array('id', 'title'))), '{n}.Article.id', '{n}.Article.title');
$expected = array(1 => 'First Article', 2 => 'Second Article', 3 => 'Third Article'); $expected = array(1 => 'First Article', 2 => 'Second Article', 3 => 'Third Article');
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
$result = $this->model->generateList(null, 'Article.title ASC', null, '{n}.Article.id', '{n}.Article'); $result = Set::combine($this->model->find('all', array('order' => 'Article.title ASC')), '{n}.Article.id', '{n}.Article');
$expected = array( $expected = array(
1 => array('id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'), 1 => array('id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
2 => 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'), 2 => 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'),
3 => array('id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31')); 3 => array('id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'));
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
$result = $this->model->generateList(null, 'Article.title ASC', null, '{n}.Article.id', '{n}.Article.title'); $result = Set::combine($this->model->find('all', array('order' => 'Article.title ASC')), '{n}.Article.id', '{n}.Article', '{n}.Article.user_id');
$expected = array(1 => 'First Article', 2 => 'Second Article', 3 => 'Third Article');
$this->assertEqual($result, $expected);
$result = $this->model->generateList(null, 'Article.title ASC', null, '{n}.Article.id', '{n}.Article', '{n}.Article.user_id');
$expected = array(1 => array( $expected = array(1 => array(
1 => array('id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'), 1 => array('id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
3 => array('id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31')), 3 => array('id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31')),
3 => array(2 => 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'))); 3 => array(2 => 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')));
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
$result = $this->model->generateList(null, 'Article.title ASC', null, '{n}.Article.id', '{n}.Article.title', '{n}.Article.user_id'); $result = Set::combine($this->model->find('all', array('order' => 'Article.title ASC', 'fields' => array('id', 'title', 'user_id'))), '{n}.Article.id', '{n}.Article.title', '{n}.Article.user_id');
$expected = array(1 => array(1 => 'First Article', 3 => 'Third Article'), $expected = array(1 => array(1 => 'First Article', 3 => 'Third Article'), 3 => array(2 => 'Second Article'));
3 => array(2 => 'Second Article'));
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
} }
@ -1099,6 +1105,25 @@ class ModelTest extends CakeTestCase {
array('User' => array('id' => '4', 'user' => 'garrett'), 'Comment' => array( array('User' => array('id' => '4', 'user' => 'garrett'), 'Comment' => array(
array('id' => '2', 'article_id' => '1', 'user_id' => '4', 'comment' => 'Second Comment for First Article', 'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31')))); array('id' => '2', 'article_id' => '1', 'user_id' => '4', 'comment' => 'Second Comment for First Article', 'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31'))));
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
$this->model2 =& new DeviceType();
$expected = array('className' => 'FeatureSet', 'foreignKey' => 'feature_set_id', 'conditions' => '', 'fields' => '', 'order' => '', 'counterCache' => '');
$this->assertEqual($this->model2->belongsTo['FeatureSet'], $expected);
$this->model2->bind('FeatureSet', array('conditions' => array('active' => true)));
$expected['conditions'] = array('active' => true);
$this->assertEqual($this->model2->belongsTo['FeatureSet'], $expected);
$this->model2->bind('FeatureSet', array('foreignKey' => false, 'conditions' => array('Feature.name' => 'DeviceType.name')));
$expected['conditions'] = array('Feature.name' => 'DeviceType.name');
$expected['foreignKey'] = false;
$this->assertEqual($this->model2->belongsTo['FeatureSet'], $expected);
$this->model2->bind('NewFeatureSet', array('type' => 'hasMany', 'className' => 'FeatureSet', 'conditions' => array('active' => true)));
$expected = array('className' => 'FeatureSet', 'conditions' => array('active' => true), 'foreignKey' => 'device_type_id', 'fields' => '', 'order' => '', 'limit' => '', 'offset' => '', 'dependent' => '', 'exclusive' => '', 'finderQuery' => '', 'counterQuery' => '');
$this->assertEqual($this->model2->hasMany['NewFeatureSet'], $expected);
$this->assertTrue(is_object($this->model2->NewFeatureSet));
} }
function testFindCount() { function testFindCount() {
@ -2296,6 +2321,60 @@ class ModelTest extends CakeTestCase {
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
} }
function testSaveAll() {
$this->model =& new Post();
$result = $this->model->find('all');
$this->assertEqual(count($result), 3);
$this->assertFalse(isset($result[3]));
$ts = date('Y-m-d H:i:s');
$this->model->saveAll(array(
'Post' => array('title' => 'Post with Author', 'body' => 'This post will be saved with an author'),
'Author' => array('user' => 'bob', 'password' => '5f4dcc3b5aa765d61d8327deb882cf90')
));
$result = $this->model->find('all');
$expected = array(
'Post' => array('id' => '4', 'author_id' => '5', 'title' => 'Post with Author', 'body' => 'This post will be saved with an author', 'published' => 'N', 'created' => $ts, 'updated' => $ts),
'Author' => array('id' => '5', 'user' => 'bob', 'password' => '5f4dcc3b5aa765d61d8327deb882cf90', 'created' => $ts, 'updated' => $ts, 'test' => 'working')
);
$this->assertEqual($result[3], $expected);
$this->assertEqual(count($result), 4);
$this->model->deleteAll(true);
$this->assertEqual($this->model->find('all'), array());
$ts = date('Y-m-d H:i:s');
$this->model->saveAll(array(
array('title' => 'Multi-record post 1', 'body' => 'First multi-record post', 'author_id' => 2),
array('title' => 'Multi-record post 2', 'body' => 'Second multi-record post', 'author_id' => 2)
));
$result = $this->model->find('all', array('recursive' => -1));
$expected = array(
array('Post' => array('id' => '6', 'author_id' => '2', 'title' => 'Multi-record post 2', 'body' => 'Second multi-record post', 'published' => 'N', 'created' => $ts, 'updated' => $ts)),
array('Post' => array('id' => '5', 'author_id' => '2', 'title' => 'Multi-record post 1', 'body' => 'First multi-record post', 'published' => 'N', 'created' => $ts, 'updated' => $ts))
);
$this->assertEqual($result, $expected);
}
function testSaveWithCounterCache() {
$this->model =& new Syfile();
$this->model2 =& new Item();
$result = $this->model->findById(1);
$this->assertIdentical($result['Syfile']['item_count'], null);
$this->model2->save(array('name' => 'Item 7', 'syfile_id' => 1));
$result = $this->model->findById(1);
$this->assertIdentical($result['Syfile']['item_count'], '2');
$this->model2->delete(1);
$result = $this->model->findById(1);
$this->assertIdentical($result['Syfile']['item_count'], '1');
}
function testDel() { function testDel() {
$this->model =& new Article(); $this->model =& new Article();
@ -2412,6 +2491,18 @@ class ModelTest extends CakeTestCase {
$this->assertEqual($result, 0); $this->assertEqual($result, 0);
} }
function testDependentExclusiveDelete() {
$this->model =& new Article10();
$result = $this->model->find('all');
$this->assertEqual(count($result[0]['Comment']), 4);
$this->assertEqual(count($result[1]['Comment']), 2);
$this->assertEqual($this->model->Comment->find('count'), 6);
$this->model->delete(1);
$this->assertEqual($this->model->Comment->find('count'), 2);
}
function testFindAllThreaded() { function testFindAllThreaded() {
$this->model =& new Category(); $this->model =& new Category();

View file

@ -35,7 +35,7 @@
class AuthorFixture extends CakeTestFixture { class AuthorFixture extends CakeTestFixture {
var $name = 'Author'; var $name = 'Author';
var $fields = array( var $fields = array(
'id' => array('type' => 'integer', 'key' => 'primary', 'extra'=> 'auto_increment'), 'id' => array('type' => 'integer', 'key' => 'primary', 'extra' => 'auto_increment'),
'user' => array('type' => 'string', 'null' => false), 'user' => array('type' => 'string', 'null' => false),
'password' => array('type' => 'string', 'null' => false), 'password' => array('type' => 'string', 'null' => false),
'created' => 'datetime', 'created' => 'datetime',

View file

@ -40,12 +40,12 @@ class ItemFixture extends CakeTestFixture {
'name' => array('type' => 'string', 'null' => false) 'name' => array('type' => 'string', 'null' => false)
); );
var $records = array( var $records = array(
array ('id' => 1, 'syfile_id' => 1, 'name' => 'Item 1'), array('id' => 1, 'syfile_id' => 1, 'name' => 'Item 1'),
array ('id' => 2, 'syfile_id' => 2, 'name' => 'Item 2'), array('id' => 2, 'syfile_id' => 2, 'name' => 'Item 2'),
array ('id' => 3, 'syfile_id' => 3, 'name' => 'Item 3'), array('id' => 3, 'syfile_id' => 3, 'name' => 'Item 3'),
array ('id' => 4, 'syfile_id' => 4, 'name' => 'Item 4'), array('id' => 4, 'syfile_id' => 4, 'name' => 'Item 4'),
array ('id' => 5, 'syfile_id' => 5, 'name' => 'Item 5'), array('id' => 5, 'syfile_id' => 5, 'name' => 'Item 5'),
array ('id' => 6, 'syfile_id' => 6, 'name' => 'Item 6') array('id' => 6, 'syfile_id' => 6, 'name' => 'Item 6')
); );
} }
?> ?>

View file

@ -37,15 +37,16 @@ class SyfileFixture extends CakeTestFixture {
var $fields = array( var $fields = array(
'id' => array('type' => 'integer', 'key' => 'primary', 'extra'=> 'auto_increment'), 'id' => array('type' => 'integer', 'key' => 'primary', 'extra'=> 'auto_increment'),
'image_id' => array('type' => 'integer', 'null' => true), 'image_id' => array('type' => 'integer', 'null' => true),
'name' => array('type' => 'string', 'null' => false) 'name' => array('type' => 'string', 'null' => false),
'item_count' => array('type' => 'integer', 'null' => true)
); );
var $records = array( var $records = array(
array ('id' => 1, 'image_id' => 1, 'name' => 'Syfile 1'), array('id' => 1, 'image_id' => 1, 'name' => 'Syfile 1'),
array ('id' => 2, 'image_id' => 2, 'name' => 'Syfile 2'), array('id' => 2, 'image_id' => 2, 'name' => 'Syfile 2'),
array ('id' => 3, 'image_id' => 5, 'name' => 'Syfile 3'), array('id' => 3, 'image_id' => 5, 'name' => 'Syfile 3'),
array ('id' => 4, 'image_id' => 3, 'name' => 'Syfile 4'), array('id' => 4, 'image_id' => 3, 'name' => 'Syfile 4'),
array ('id' => 5, 'image_id' => 4, 'name' => 'Syfile 5'), array('id' => 5, 'image_id' => 4, 'name' => 'Syfile 5'),
array ('id' => 6, 'image_id' => null, 'name' => 'Syfile 6') array('id' => 6, 'image_id' => null, 'name' => 'Syfile 6')
); );
} }
?> ?>