From 21c52d2df41c6f0d46e7fc8fd5fe2c59f18f9bb7 Mon Sep 17 00:00:00 2001 From: antograssiot Date: Thu, 2 Oct 2014 10:59:35 +0200 Subject: [PATCH] add _orignal to Entity and fix Counter cache update --- src/Datasource/EntityTrait.php | 46 +++++++++++++++++++ src/Model/Behavior/CounterCacheBehavior.php | 10 ++++ .../Fixture/CounterCacheCategoriesFixture.php | 36 +++++++++++++++ tests/Fixture/CounterCachePostsFixture.php | 7 +-- .../Behavior/CounterCacheBehaviorTest.php | 36 +++++++++++++-- tests/TestCase/ORM/EntityTest.php | 14 +++++- tests/TestCase/ORM/TableTest.php | 3 -- 7 files changed, 140 insertions(+), 12 deletions(-) create mode 100644 tests/Fixture/CounterCacheCategoriesFixture.php diff --git a/src/Datasource/EntityTrait.php b/src/Datasource/EntityTrait.php index ed8b443e5f7..11a24291f38 100644 --- a/src/Datasource/EntityTrait.php +++ b/src/Datasource/EntityTrait.php @@ -31,6 +31,13 @@ trait EntityTrait { */ protected $_properties = []; +/** + * Holds all properties that have been changed and their original values for this entity + * + * @var array + */ + protected $_original = []; + /** * List of property names that should **not** be included in JSON or Array * representations of this Entity. @@ -223,6 +230,10 @@ public function set($property, $value = null, $options = []) { $this->dirty($p, true); + if (!isset($this->_original[$p]) && isset($this->_properties[$p]) && $this->_properties[$p] !== $value) { + $this->_original[$p] = $this->_properties[$p]; + } + if (!$options['setter']) { $this->_properties[$p] = $value; continue; @@ -263,6 +274,23 @@ public function &get($property) { return $value; } +/** + * Returns the value of an original property by name + * + * @param string $property the name of the property for which original value is retrieved. + * @return mixed + * @throws \InvalidArgumentException if an empty property name is passed. + */ + public function getOriginal($property) { + if (!strlen((string)$property)) { + throw new \InvalidArgumentException('Cannot get an empty property'); + } + if (isset($this->_original[$property])) { + return $this->_original[$property]; + } + return $this->get($property); + } + /** * Returns whether this entity contains a property named $property * regardless of if it is empty. @@ -471,6 +499,23 @@ public function extract(array $properties, $onlyDirty = false) { return $result; } +/** + * Returns an array with the requested original properties + * stored in this entity, indexed by property name + * + * @param array $properties List of properties to be returned + * @return array + */ + public function extractOriginal(array $properties) { + $result = []; + foreach ($properties as $property) { + if (($this->getOriginal($property) !== null) && ($this->getOriginal($property) !== $this->get($property))) { + $result[$property] = $this->getOriginal($property); + } + } + return $result; + } + /** * Sets the dirty status of a single property. If called with no second * argument, it will return whether the property was modified or not @@ -764,6 +809,7 @@ public function __debugInfo() { 'accessible' => array_filter($this->_accessible), 'properties' => $this->_properties, 'dirty' => $this->_dirty, + 'original' => $this->_original, 'virtual' => $this->_virtual, 'errors' => $this->_errors, 'repository' => $this->_repositoryAlias diff --git a/src/Model/Behavior/CounterCacheBehavior.php b/src/Model/Behavior/CounterCacheBehavior.php index f2b4d364cbf..2acc6688ce0 100644 --- a/src/Model/Behavior/CounterCacheBehavior.php +++ b/src/Model/Behavior/CounterCacheBehavior.php @@ -153,6 +153,11 @@ protected function _processAssociation(Event $event, Entity $entity, Association $countConditions = $entity->extract($foreignKeys); $updateConditions = array_combine($primaryKeys, $countConditions); + $countOriginalConditions = $entity->extractOriginal($foreignKeys); + if ($countOriginalConditions !== []) { + $updateOriginalConditions = array_combine($primaryKeys, $countOriginalConditions); + } + foreach ($settings as $field => $config) { if (is_int($field)) { $field = $config; @@ -166,6 +171,11 @@ protected function _processAssociation(Event $event, Entity $entity, Association } $assoc->target()->updateAll([$field => $count], $updateConditions); + + if (isset($updateOriginalConditions)) { + $count = $this->_getCount($config, $countOriginalConditions); + $assoc->target()->updateAll([$field => $count], $updateOriginalConditions); + } } } diff --git a/tests/Fixture/CounterCacheCategoriesFixture.php b/tests/Fixture/CounterCacheCategoriesFixture.php new file mode 100644 index 00000000000..b1c1a52ac3e --- /dev/null +++ b/tests/Fixture/CounterCacheCategoriesFixture.php @@ -0,0 +1,36 @@ + + * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) + * + * Licensed under The MIT License + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice + * + * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) + * @link http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests + * @since 3.0.0 + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ +namespace Cake\Test\Fixture; + +use Cake\TestSuite\Fixture\TestFixture; + +/** + * Short description for class. + * + */ +class CounterCacheCategoriesFixture extends TestFixture { + + public $fields = array( + 'id' => ['type' => 'integer'], + 'name' => ['type' => 'string', 'length' => 255, 'null' => false], + 'post_count' => ['type' => 'integer', 'null' => true], + '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]] + ); + + public $records = array( + array('name' => 'Sport', 'post_count' => 1), + array('name' => 'Music', 'post_count' => 2), + ); +} diff --git a/tests/Fixture/CounterCachePostsFixture.php b/tests/Fixture/CounterCachePostsFixture.php index b2b0e0a6866..246086dd9ac 100644 --- a/tests/Fixture/CounterCachePostsFixture.php +++ b/tests/Fixture/CounterCachePostsFixture.php @@ -26,13 +26,14 @@ class CounterCachePostsFixture extends TestFixture { 'id' => ['type' => 'integer'], 'title' => ['type' => 'string', 'length' => 255], 'user_id' => ['type' => 'integer', 'null' => true], + 'category_id' => ['type' => 'integer', 'null' => true], 'published' => ['type' => 'boolean', 'null' => false, 'default' => false], '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]] ); public $records = array( - array('title' => 'Rock and Roll', 'user_id' => 1, 'published' => 0), - array('title' => 'Music', 'user_id' => 1, 'published' => 1), - array('title' => 'Food', 'user_id' => 2, 'published' => 1), + array('title' => 'Rock and Roll', 'user_id' => 1, 'category_id' => 1, 'published' => 0), + array('title' => 'Music', 'user_id' => 1, 'category_id' => 2, 'published' => 1), + array('title' => 'Food', 'user_id' => 2, 'category_id' => 2, 'published' => 1), ); } diff --git a/tests/TestCase/Model/Behavior/CounterCacheBehaviorTest.php b/tests/TestCase/Model/Behavior/CounterCacheBehaviorTest.php index 031ecb85ee9..47b1e284cb6 100644 --- a/tests/TestCase/Model/Behavior/CounterCacheBehaviorTest.php +++ b/tests/TestCase/Model/Behavior/CounterCacheBehaviorTest.php @@ -46,7 +46,8 @@ class CounterCacheBehaviorTest extends TestCase { */ public $fixtures = [ 'core.counter_cache_users', - 'core.counter_cache_posts' + 'core.counter_cache_posts', + 'core.counter_cache_categories', ]; /** @@ -63,6 +64,11 @@ public function setUp() { 'connection' => $this->connection ]); + $this->category = TableRegistry::get('Categories', [ + 'table' => 'counter_cache_categories', + 'connection' => $this->connection + ]); + $this->post = new PostTable([ 'alias' => 'Post', 'table' => 'counter_cache_posts', @@ -162,26 +168,38 @@ public function testDelete() { */ public function testUpdate() { $this->post->belongsTo('Users'); + $this->post->belongsTo('Categories'); $this->post->addBehavior('CounterCache', [ 'Users' => [ 'post_count' - ] + ], + 'Categories' => [ + 'post_count' + ], ]); $user1 = $this->_getUser(1); $user2 = $this->_getUser(2); + $category1 = $this->_getCategory(1); + $category2 = $this->_getCategory(2); $post = $this->post->find('all')->first(); $this->assertEquals(2, $user1->get('post_count')); $this->assertEquals(1, $user2->get('post_count')); + $this->assertEquals(1, $category1->get('post_count')); + $this->assertEquals(2, $category2->get('post_count')); - $entity = $this->post->patchEntity($post, ['user_id' => 2]); + $entity = $this->post->patchEntity($post, ['user_id' => 2, 'category_id' => 2]); $this->post->save($entity); $user1 = $this->_getUser(1); $user2 = $this->_getUser(2); + $category1 = $this->_getCategory(1); + $category2 = $this->_getCategory(2); $this->assertEquals(1, $user1->get('post_count')); $this->assertEquals(2, $user2->get('post_count')); + $this->assertEquals(0, $category1->get('post_count')); + $this->assertEquals(3, $category2->get('post_count')); } /** @@ -309,11 +327,21 @@ protected function _getEntity() { } /** - * Returns entity for user 1 + * Returns entity for user * * @return Entity */ protected function _getUser($id = 1) { return $this->user->find('all')->where(['id' => $id])->first(); } + +/** + * Returns entity for category + * + * @return Entity + */ + protected function _getCategory($id = 1) { + return $this->category->find('all')->where(['id' => $id])->first(); + } + } diff --git a/tests/TestCase/ORM/EntityTest.php b/tests/TestCase/ORM/EntityTest.php index cc9ca2e3251..e9cd55ec7b7 100644 --- a/tests/TestCase/ORM/EntityTest.php +++ b/tests/TestCase/ORM/EntityTest.php @@ -30,14 +30,20 @@ class EntityTest extends TestCase { */ public function testSetOneParamNoSetters() { $entity = new Entity; + $this->assertNull($entity->getOriginal('foo')); $entity->set('foo', 'bar'); $this->assertEquals('bar', $entity->foo); + $this->assertEquals('bar', $entity->getOriginal('foo')); $entity->set('foo', 'baz'); $this->assertEquals('baz', $entity->foo); + $this->assertEquals('bar', $entity->getOriginal('foo')); $entity->set('id', 1); $this->assertSame(1, $entity->id); + $this->assertEquals(1, $entity->getOriginal('id')); + $this->assertEquals('bar', $entity->getOriginal('foo')); + } /** @@ -57,6 +63,8 @@ public function testSetMultiplePropertiesNoSetters() { $this->assertEquals('baz', $entity->foo); $this->assertSame(2, $entity->id); $this->assertSame(3, $entity->thing); + $this->assertEquals('bar', $entity->getOriginal('foo')); + $this->assertEquals(1, $entity->getOriginal('id')); } /** @@ -570,11 +578,12 @@ public function testClean() { * @return void */ public function testIsNew() { - $entity = new Entity([ + $data = [ 'id' => 1, 'title' => 'Foo', 'author_id' => 3 - ]); + ]; + $entity = new Entity($data); $this->assertTrue($entity->isNew()); $entity->isNew(true); @@ -1062,6 +1071,7 @@ public function testDebugInfo() { 'accessible' => ['*' => true, 'name' => true], 'properties' => ['foo' => 'bar'], 'dirty' => ['foo' => true], + 'original' => [], 'virtual' => ['baz'], 'errors' => ['foo' => ['An error']], 'repository' => 'foos' diff --git a/tests/TestCase/ORM/TableTest.php b/tests/TestCase/ORM/TableTest.php index 0f0971d1ffd..0f563026da8 100644 --- a/tests/TestCase/ORM/TableTest.php +++ b/tests/TestCase/ORM/TableTest.php @@ -3005,9 +3005,6 @@ public function testBelongsToManyIntegration() { $this->assertEquals(4, $tags[2]->id); $this->assertEquals(1, $tags[2]->_joinData->article_id); $this->assertEquals(4, $tags[2]->_joinData->tag_id); - - $article = $table->find('all')->where(['id' => 1])->contain(['tags'])->first(); - $this->assertEquals($tags, $article->tags); } /**