Browse Source

Implemented feature to allow multiple counter caches per associated model.

ADmad 14 years ago
parent
commit
c3884f407f

+ 47 - 33
lib/Cake/Model/Model.php

@@ -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 (!is_array($assoc['counterCache'])) {
+					if (isset($assoc['counterScope'])) {
+						$assoc['counterCache'] = array($assoc['counterCache'] => $assoc['counterScope']);
+					} else {
+						$assoc['counterCache'] = array($assoc['counterCache'] => array());
+					}
 				}
 
-				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])
-						);
+				$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;
 					}
-				}
-				$conditions[$fkQuoted] = $keys[$foreignKey];
 
-				if ($recursive === 0) {
-					$conditions = array_merge($conditions, (array)$assoc['counterScope']);
-				}
-				$count = intval($this->find('count', compact('conditions', 'recursive')));
+					if (!array_key_exists($foreignKey, $keys)) {
+						$keys[$foreignKey] = $this->field($foreignKey);
+					}
+					$recursive = (empty($conditions) ? -1 : 0);
 
-				$this->{$parent}->updateAll(
-					array($assoc['counterCache'] => $count),
-					array($this->{$parent}->escapeField() => $keys[$foreignKey])
-				);
+					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])
+					);
+				}
 			}
 		}
 	}

+ 64 - 0
lib/Cake/Test/Case/Model/ModelWriteTest.php

@@ -469,6 +469,70 @@ class ModelWriteTest extends BaseModelTest {
 	}
 
 /**
+ * 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.
  *
  * @return void

+ 5 - 3
lib/Cake/Test/Fixture/CounterCachePostFixture.php

@@ -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),
     );
 }

+ 4 - 3
lib/Cake/Test/Fixture/CounterCacheUserFixture.php

@@ -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),
 	);
 }