Implemented feature to allow multiple counter caches per associated model.

This commit is contained in:
ADmad 2011-08-17 18:10:32 +05:30
parent 54e1de9070
commit c3884f407f
4 changed files with 121 additions and 40 deletions

View file

@ -1532,45 +1532,59 @@ class Model extends Object {
$keys['old'] = isset($keys['old']) ? $keys['old'] : array();
foreach ($this->belongsTo as $parent => $assoc) {
$foreignKey = $assoc['foreignKey'];
$fkQuoted = $this->escapeField($assoc['foreignKey']);
if (!empty($assoc['counterCache'])) {
if ($assoc['counterCache'] === true) {
$assoc['counterCache'] = Inflector::underscore($this->alias) . '_count';
}
if (!$this->{$parent}->hasField($assoc['counterCache'])) {
continue;
}
if (!array_key_exists($foreignKey, $keys)) {
$keys[$foreignKey] = $this->field($foreignKey);
}
$recursive = (isset($assoc['counterScope']) ? 0 : -1);
$conditions = ($recursive === 0) ? (array)$assoc['counterScope'] : array();
if (isset($keys['old'][$foreignKey])) {
if ($keys['old'][$foreignKey] != $keys[$foreignKey]) {
$conditions[$fkQuoted] = $keys['old'][$foreignKey];
$count = intval($this->find('count', compact('conditions', 'recursive')));
$this->{$parent}->updateAll(
array($assoc['counterCache'] => $count),
array($this->{$parent}->escapeField() => $keys['old'][$foreignKey])
);
if (!is_array($assoc['counterCache'])) {
if (isset($assoc['counterScope'])) {
$assoc['counterCache'] = array($assoc['counterCache'] => $assoc['counterScope']);
} else {
$assoc['counterCache'] = array($assoc['counterCache'] => array());
}
}
$conditions[$fkQuoted] = $keys[$foreignKey];
if ($recursive === 0) {
$conditions = array_merge($conditions, (array)$assoc['counterScope']);
$foreignKey = $assoc['foreignKey'];
$fkQuoted = $this->escapeField($assoc['foreignKey']);
foreach ($assoc['counterCache'] as $field => $conditions) {
if (!is_string($field)) {
$field = Inflector::underscore($this->alias) . '_count';
}
if (!$this->{$parent}->hasField($field)) {
continue;
}
if ($conditions === true) {
$conditions = array();
} else {
$conditions = (array)$conditions;
}
if (!array_key_exists($foreignKey, $keys)) {
$keys[$foreignKey] = $this->field($foreignKey);
}
$recursive = (empty($conditions) ? -1 : 0);
if (isset($keys['old'][$foreignKey])) {
if ($keys['old'][$foreignKey] != $keys[$foreignKey]) {
$conditions[$fkQuoted] = $keys['old'][$foreignKey];
$count = intval($this->find('count', compact('conditions', 'recursive')));
$this->{$parent}->updateAll(
array($field => $count),
array($this->{$parent}->escapeField() => $keys['old'][$foreignKey])
);
}
}
$conditions[$fkQuoted] = $keys[$foreignKey];
if ($recursive === 0) {
$conditions = array_merge($conditions, (array)$conditions);
}
$count = intval($this->find('count', compact('conditions', 'recursive')));
$this->{$parent}->updateAll(
array($field => $count),
array($this->{$parent}->escapeField() => $keys[$foreignKey])
);
}
$count = intval($this->find('count', compact('conditions', 'recursive')));
$this->{$parent}->updateAll(
array($assoc['counterCache'] => $count),
array($this->{$parent}->escapeField() => $keys[$foreignKey])
);
}
}
}

View file

@ -468,6 +468,70 @@ class ModelWriteTest extends BaseModelTest {
$this->assertEquals($result['Syfile']['item_count'], 1);
}
/**
* Tests having multiple counter caches for an associated model
*
* @access public
* @return void
*/
public function testCounterCacheMultipleCaches() {
$this->loadFixtures('CounterCacheUser', 'CounterCachePost');
$User = new CounterCacheUser();
$Post = new CounterCachePost();
$Post->unbindModel(array('belongsTo' => array('User')), false);
$Post->bindModel(array(
'belongsTo' => array(
'User' => array(
'className' => 'CounterCacheUser',
'foreignKey' => 'user_id',
'counterCache' => array(
true,
'posts_published' => array('Post.published' => true)
)
)
)
), false);
// Count Increase
$user = $User->find('first', array(
'conditions' => array('id' => 66),
'recursive' => -1
));
$data = array('Post' => array(
'id' => 22,
'title' => 'New Post',
'user_id' => 66,
'published' => true
));
$Post->save($data);
$result = $User->find('first', array(
'conditions' => array('id' => 66),
'recursive' => -1
));
$this->assertEquals(3, $result[$User->alias]['post_count']);
$this->assertEquals(2, $result[$User->alias]['posts_published']);
// Count decrease
$Post->delete(1);
$result = $User->find('first', array(
'conditions' => array('id' => 66),
'recursive' => -1
));
$this->assertEquals(2, $result[$User->alias]['post_count']);
$this->assertEquals(2, $result[$User->alias]['posts_published']);
// Count update
$data = $Post->find('first', array(
'conditions' => array('id' => 1),
'recursive' => -1
));
$data[$Post->alias]['user_id'] = 301;
$Post->save($data);
$result = $User->find('all',array('order' => 'User.id'));
$this->assertEquals(2, $result[0]['User']['post_count']);
$this->assertEquals(1, $result[1]['User']['posts_published']);
}
/**
* test that beforeValidate returning false can abort saves.
*

View file

@ -30,11 +30,13 @@ class CounterCachePostFixture extends CakeTestFixture {
'id' => array('type' => 'integer', 'key' => 'primary'),
'title' => array('type' => 'string', 'length' => 255, 'null' => false),
'user_id' => array('type' => 'integer', 'null' => true),
'user_id' => array('type' => 'integer', 'null' => true),
'published' => array('type' => 'boolean', 'null' => false)
);
public $records = array(
array('id' => 1, 'title' => 'Rock and Roll', 'user_id' => 66),
array('id' => 2, 'title' => 'Music', 'user_id' => 66),
array('id' => 3, 'title' => 'Food', 'user_id' => 301),
array('id' => 1, 'title' => 'Rock and Roll', 'user_id' => 66, 'published' => false),
array('id' => 2, 'title' => 'Music', 'user_id' => 66, 'published' => true),
array('id' => 3, 'title' => 'Food', 'user_id' => 301, 'published' => true),
);
}

View file

@ -29,11 +29,12 @@ class CounterCacheUserFixture extends CakeTestFixture {
public $fields = array(
'id' => array('type' => 'integer', 'key' => 'primary'),
'name' => array('type' => 'string', 'length' => 255, 'null' => false),
'post_count' => array('type' => 'integer', 'null' => true)
'post_count' => array('type' => 'integer', 'null' => true),
'posts_published' => array('type' => 'integer', 'null' => true)
);
public $records = array(
array('id' => 66, 'name' => 'Alexander','post_count' => 2),
array('id' => 301, 'name' => 'Steven','post_count' => 1),
array('id' => 66, 'name' => 'Alexander', 'post_count' => 2, 'posts_published' => 1),
array('id' => 301, 'name' => 'Steven', 'post_count' => 1, 'posts_published' => 1),
);
}