diff --git a/Cake/ORM/Entity.php b/Cake/ORM/Entity.php index 15915a45fda..2ad7d0815d0 100644 --- a/Cake/ORM/Entity.php +++ b/Cake/ORM/Entity.php @@ -39,6 +39,14 @@ class Entity implements \ArrayAccess, \JsonSerializable { */ protected $_className; +/** + * Holds a list of the properties that where modified after that where added or + * modified after this object was originally created. + * + * @var array + */ + protected $_dirty = []; + /** * Holds a cached list of methods that exist in the instanced class * @@ -151,12 +159,21 @@ public function set($property, $value = true, $useSetters = true) { $useSetters = $value; } - if (!$useSetters) { - $this->_properties = $property + $this->_properties; - return $this; - } - foreach ($property as $p => $value) { + $markDirty = true; + if (isset($this->_properties[$p])) { + $markDirty = $value !== $this->_properties[$p]; + } + + if ($markDirty) { + $this->dirty($p, true); + } + + if (!$useSetters) { + $this->_properties[$p] = $value; + continue; + } + $setter = 'set' . Inflector::camelize($p); if ($this->_methodExists($setter)) { $value = $this->{$setter}($value); @@ -320,4 +337,29 @@ public function extract(array $properties) { 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 + * after the object creation. + * + * @param string $property the field to set or check status for + * @param null|boolean true means the property was changed, false means + * it was not changed and null will make the function return current state + * for that property + * @return boolean whether the property was changed or not + */ + public function dirty($property, $isDirty = null) { + if ($isDirty === null) { + return isset($this->_dirty[$property]); + } + + if (!$isDirty) { + unset($this->_dirty[$property]); + return false; + } + + $this->_dirty[$property] = true; + return true; + } + } diff --git a/Cake/Test/TestCase/ORM/EntityTest.php b/Cake/Test/TestCase/ORM/EntityTest.php index dfd5b6beaaf..c9708b76360 100644 --- a/Cake/Test/TestCase/ORM/EntityTest.php +++ b/Cake/Test/TestCase/ORM/EntityTest.php @@ -447,4 +447,49 @@ public function testExtract() { $expected = ['id' => 1, 'crazyness' => null]; $this->assertEquals($expected, $entity->extract(['id', 'crazyness'])); } + +/** + * Tests dirty() method on a newly created object + * + * @return void + */ + public function testDirty() { + $entity = new \Cake\ORM\Entity([ + 'id' => 1, + 'title' => 'Foo', + 'author_id' => 3 + ]); + $this->assertTrue($entity->dirty('id')); + $this->assertTrue($entity->dirty('title')); + $this->assertTrue($entity->dirty('author_id')); + + $entity->dirty('id', false); + $this->assertFalse($entity->dirty('id')); + $this->assertTrue($entity->dirty('title')); + $entity->dirty('title', false); + $this->assertFalse($entity->dirty('title')); + } + +/** + * Tests dirty() when altering properties values and adding new ones + * + * @return void + */ + public function testDirtyChangingProperties() { + $entity = new \Cake\ORM\Entity([ + 'title' => 'Foo', + ]); + $entity->dirty('title', false); + $this->assertFalse($entity->dirty('title')); + $entity->set('title', 'Foo'); + $this->assertFalse($entity->dirty('title')); + $entity->set('title', 'Foo'); + $this->assertFalse($entity->dirty('title')); + $entity->set('title', 'Something Else'); + $this->assertTrue($entity->dirty('title')); + + $entity->set('something', 'else'); + $this->assertTrue($entity->dirty('something')); + } + } diff --git a/Cake/Test/TestCase/ORM/TableTest.php b/Cake/Test/TestCase/ORM/TableTest.php index 801dd185f2b..f0ae419c9fd 100644 --- a/Cake/Test/TestCase/ORM/TableTest.php +++ b/Cake/Test/TestCase/ORM/TableTest.php @@ -1235,7 +1235,8 @@ public function testAtomicSaveRollbackOnFailure() { ->will($this->returnValue($query)); $statement = $this->getMock('\Cake\Database\Statement\StatementDecorator'); - $statement->expects($this->once())->method('rowCount')->will($this->returnValue(0)); + $statement->expects($this->once())->method('rowCount') + ->will($this->returnValue(0)); $connection->expects($this->once())->method('begin'); $connection->expects($this->once())->method('rollback'); $query->expects($this->once())->method('executeStatement')