Skip to content

Commit

Permalink
Implement timestamp behavior
Browse files Browse the repository at this point in the history
closes #2215
  • Loading branch information
AD7six committed Nov 4, 2013
1 parent 984d0ac commit 2e01e08
Show file tree
Hide file tree
Showing 2 changed files with 391 additions and 0 deletions.
128 changes: 128 additions & 0 deletions Cake/Model/Behavior/TimestampBehavior.php
@@ -0,0 +1,128 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* 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://cakephp.org CakePHP(tm) Project
* @since CakePHP(tm) v 3.0.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
namespace Cake\Model\Behavior;

use Cake\Event\Event;
use Cake\ORM\Behavior;
use Cake\ORM\Entity;
use Cake\ORM\Table;

class TimestampBehavior extends Behavior {

/**
* Default settings
*
* These are merged with user-provided settings when the behavior is used.
*
* refreshTimestamp - if true (the default) the timestamp used will be the current time when
* the code is executed, to set to an explicit date time value - set refreshTimetamp to false
* and call setTimestamp() on the behavior class before use.
*
* @var array
*/
protected $_defaultSettings = [
'fields' => [
'created' => 'created',
'updated' => 'modified'
],
'refreshTimestamp' => true
];

/**
* Current timestamp
*
* @var \DateTime
*/
protected $_ts;

/**
* Constructor
*
* Merge settings with the default and store in the settings property
*
* @param Table $table The table this behavior is attached to.
* @param array $settings The settings for this behavior.
*/
public function __construct(Table $table, array $settings = []) {
$this->_settings = $settings + $this->_defaultSettings;
}

/**
* beforeSave
*
* There is only one event handler, it can be configured to be called for any event
*
* @param Event $event
* @param Entity $entity
* @return true (irrespective of the behavior logic, the save will not be prevented)
*/
public function beforeSave(Event $event, Entity $entity) {
$settings = $this->settings();
$new = $entity->isNew() !== false;

if ($new) {
$this->_updateField($entity, $settings['fields']['created'], $settings['refreshTimestamp']);
}
$this->_updateField($entity, $settings['fields']['updated'], $settings['refreshTimestamp']);

return true;
}

/**
* getTimestamp
*
* Gets the current timestamp. If $refreshTimestamp is not truthy, the existing timestamp will be
* returned
*
* @return \DateTime
*/
public function getTimestamp($refreshTimestamp = null) {
if ($this->_ts === null || $refreshTimestamp) {
$this->setTimestamp();
}

return $this->_ts;
}

/**
* setTimestamp
*
* Set the timestamp to the given DateTime object, or if not passed a new DateTime object
*
* @param int $ts
* @return void
*/
public function setTimestamp(\DateTime $ts = null) {
if ($ts === null) {
$ts = new \DateTime();
}
$this->_ts = $ts;
}

/**
* Update a field, if it hasn't been updated already
*
* @param Entity $entity
* @param string $field
* @param bool $refreshTimestamp
* @return void
*/
protected function _updateField(Entity $entity, $field, $refreshTimestamp) {
if ($entity->dirty($field)) {
return;
}
$entity->set($field, $this->getTimestamp($refreshTimestamp));
}
}
263 changes: 263 additions & 0 deletions Cake/Test/TestCase/Model/Behavior/TimestampBehaviorTest.php
@@ -0,0 +1,263 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* 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://cakephp.org CakePHP(tm) Project
* @since CakePHP(tm) v 3.0.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
namespace Cake\Test\TestCase\Model\Behavior;

use Cake\Event\Event;
use Cake\Model\Behavior\TimestampBehavior;
use Cake\ORM\Entity;
use Cake\TestSuite\TestCase;

/**
* Behavior test case
*/
class TimestampBehaviorTest extends TestCase {

/**
* Sanity check Implemented events
*
* @return void
*/
public function testImplementedEvents() {
$table = $this->getMock('Cake\ORM\Table');
$this->Behavior = new TimestampBehavior($table);

$expected = [
'Model.beforeSave' => 'beforeSave'
];
$this->assertEquals($expected, $this->Behavior->implementedEvents());
}

/**
* testCreatedAbsent
*
* @return void
*/
public function testCreatedAbsent() {
$table = $this->getMock('Cake\ORM\Table');
$this->Behavior = new TimestampBehavior($table, ['refreshTimestamp' => false]);
$ts = new \DateTime('2000-01-01');
$this->Behavior->setTimestamp($ts);

$event = new Event('Model.beforeSave');
$entity = new Entity(['name' => 'Foo']);

$return = $this->Behavior->beforeSave($event, $entity);
$this->assertTrue($return, 'Handle Event is expected to always return true');
$this->assertSame($ts, $entity->created, 'Created timestamp is expected to be the mocked value');
}

/**
* testCreatedPresent
*
* @return void
*/
public function testCreatedPresent() {
$table = $this->getMock('Cake\ORM\Table');
$this->Behavior = new TimestampBehavior($table, ['refreshTimestamp' => false]);
$ts = new \DateTime('2000-01-01');
$this->Behavior->setTimestamp($ts);

$event = new Event('Model.beforeSave');
$existingValue = new \DateTime('2011-11-11');
$entity = new Entity(['name' => 'Foo', 'created' => $existingValue]);

$return = $this->Behavior->beforeSave($event, $entity);
$this->assertTrue($return, 'Handle Event is expected to always return true');
$this->assertSame($existingValue, $entity->created, 'Created timestamp is expected to be unchanged');
}

/**
* testCreatedNotNew
*
* @return void
*/
public function testCreatedNotNew() {
$table = $this->getMock('Cake\ORM\Table');
$this->Behavior = new TimestampBehavior($table, ['refreshTimestamp' => false]);
$ts = new \DateTime('2000-01-01');
$this->Behavior->setTimestamp($ts);

$event = new Event('Model.beforeSave');
$entity = new Entity(['name' => 'Foo']);
$entity->isNew(false);

$return = $this->Behavior->beforeSave($event, $entity);
$this->assertTrue($return, 'Handle Event is expected to always return true');
$this->assertNull($entity->created, 'Created timestamp is expected to be untouched if the entity is not new');
}

/**
* testModifiedAbsent
*
* @return void
*/
public function testModifiedAbsent() {
$table = $this->getMock('Cake\ORM\Table');
$this->Behavior = new TimestampBehavior($table, ['refreshTimestamp' => false]);
$ts = new \DateTime('2000-01-01');
$this->Behavior->setTimestamp($ts);

$event = new Event('Model.beforeSave');
$entity = new Entity(['name' => 'Foo']);
$entity->isNew(false);

$return = $this->Behavior->beforeSave($event, $entity);
$this->assertTrue($return, 'Handle Event is expected to always return true');
$this->assertSame($ts, $entity->modified, 'Modified timestamp is expected to be the mocked value');
}

/**
* testModifiedPresent
*
* @return void
*/
public function testModifiedPresent() {
$table = $this->getMock('Cake\ORM\Table');
$this->Behavior = new TimestampBehavior($table, ['refreshTimestamp' => false]);
$ts = new \DateTime('2000-01-01');
$this->Behavior->setTimestamp($ts);

$event = new Event('Model.beforeSave');
$existingValue = new \DateTime('2011-11-11');
$entity = new Entity(['name' => 'Foo', 'modified' => $existingValue]);
$entity->clean();
$entity->isNew(false);

$return = $this->Behavior->beforeSave($event, $entity);
$this->assertTrue($return, 'Handle Event is expected to always return true');
$this->assertSame($ts, $entity->modified, 'Modified timestamp is expected to be updated');
}

/**
* testGetTimestamp
*
* @return void
*/
public function testGetTimestamp() {
$table = $this->getMock('Cake\ORM\Table');
$this->Behavior = new TimestampBehavior($table);

$property = new \ReflectionProperty('Cake\Model\Behavior\TimestampBehavior', '_ts');
$property->setAccessible(true);

$this->assertNull($property->getValue($this->Behavior), 'Should be null be default');

$return = $this->Behavior->getTimestamp();
$this->assertInstanceOf(
'DateTime',
$return,
'After calling for the first time, should be a date time object'
);

return $this->Behavior;
}

/**
* testGetTimestampPersists
*
* @depends testGetTimestamp
* @return void
*/
public function testGetTimestampPersists($behavior) {
$this->Behavior = $behavior;

$property = new \ReflectionProperty('Cake\Model\Behavior\TimestampBehavior', '_ts');
$property->setAccessible(true);

$initialValue = $property->getValue($this->Behavior);
$this->Behavior->getTimestamp();
$postValue = $property->getValue($this->Behavior);

$this->assertSame(
$initialValue,
$postValue,
'The timestamp should be exactly the same value'
);
}

/**
* testGetTimestampRefreshes
*
* @depends testGetTimestamp
* @return void
*/
public function testGetTimestampRefreshes($behavior) {
$this->Behavior = $behavior;

$property = new \ReflectionProperty('Cake\Model\Behavior\TimestampBehavior', '_ts');
$property->setAccessible(true);

$initialValue = $property->getValue($this->Behavior);
$this->Behavior->getTimestamp(true);
$postValue = $property->getValue($this->Behavior);

$this->assertNotSame(
$initialValue,
$postValue,
'The timestamp should be a different object if refreshTimestamp is truthy'
);
}

/**
* testSetTimestampDefault
*
* @return void
*/
public function testSetTimestampDefault() {
$table = $this->getMock('Cake\ORM\Table');
$this->Behavior = new TimestampBehavior($table);

$this->Behavior->setTimestamp();

$property = new \ReflectionProperty('Cake\Model\Behavior\TimestampBehavior', '_ts');
$property->setAccessible(true);
$set = $property->getValue($this->Behavior);

$this->assertInstanceOf(
'DateTime',
$set,
'After calling for the first time, should be a date time object'
);
}

/**
* testSetTimestampExplicit
*
* @return void
*/
public function testSetTimestampExplicit() {
$table = $this->getMock('Cake\ORM\Table');
$this->Behavior = new TimestampBehavior($table);

$ts = new \DateTime();
$this->Behavior->setTimestamp($ts);

$property = new \ReflectionProperty('Cake\Model\Behavior\TimestampBehavior', '_ts');
$property->setAccessible(true);
$set = $property->getValue($this->Behavior);

$this->assertInstanceOf(
'DateTime',
$set,
'After calling for the first time, should be a date time object'
);

$this->assertSame(
$ts,
$set,
'Should have set the same object passed in'
);
}
}

0 comments on commit 2e01e08

Please sign in to comment.