Skip to content

Commit

Permalink
Allow for use with any event
Browse files Browse the repository at this point in the history
By allowing the behavior to act on any event, it's possible to easily
cater for soft delete (deleted_date) or any kind of custom event
(approval_date, rejection_date, published_date)
  • Loading branch information
AD7six committed Nov 4, 2013
1 parent 2e01e08 commit dc2238c
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 15 deletions.
47 changes: 39 additions & 8 deletions Cake/Model/Behavior/TimestampBehavior.php
Expand Up @@ -26,16 +26,22 @@ class TimestampBehavior extends Behavior {
*
* These are merged with user-provided settings when the behavior is used.
*
* events - an event-name keyed array of which fields to update, and when, for a given event
* possible values for when a field will be updated are true, "new" or "existing" to set the
* field value always, only when a new record or only when an existing record.
*
* 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'
'events' => [
'Model.beforeSave' => [
'created' => 'new',
'modified' => true,
]
],
'refreshTimestamp' => true
];
Expand All @@ -60,26 +66,51 @@ public function __construct(Table $table, array $settings = []) {
}

/**
* beforeSave
* handleEvent
*
* 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) {
public function handleEvent(Event $event, Entity $entity) {
$eventName = $event->name();
$settings = $this->settings();
if (!isset($settings['events'][$eventName])) {
return true;
}

$new = $entity->isNew() !== false;

if ($new) {
$this->_updateField($entity, $settings['fields']['created'], $settings['refreshTimestamp']);
foreach($settings['events'][$eventName] as $field => $when) {
if (
$when === true ||
($when === 'new' && $new) ||
($when === 'existing' && !$new)
) {
$this->_updateField($entity, $field, $settings['refreshTimestamp']);
}
}
$this->_updateField($entity, $settings['fields']['updated'], $settings['refreshTimestamp']);

return true;
}

/**
* implementedEvents
*
* The implemented events of this behavior depend on configuration
*
* @return array
*/
public function implementedEvents() {
$events = array_flip(array_keys($this->_settings['events']));
foreach($events as &$method) {
$method = 'handleEvent';
}
return $events;
}

/**
* getTimestamp
*
Expand Down
32 changes: 25 additions & 7 deletions Cake/Test/TestCase/Model/Behavior/TimestampBehaviorTest.php
Expand Up @@ -29,12 +29,30 @@ class TimestampBehaviorTest extends TestCase {
*
* @return void
*/
public function testImplementedEvents() {
public function testImplementedEventsDefault() {
$table = $this->getMock('Cake\ORM\Table');
$this->Behavior = new TimestampBehavior($table);

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

/**
* testImplementedEventsCustom
*
* The behavior allows for handling any event - test an example
*
* @return void
*/
public function testImplementedEventsCustom() {
$table = $this->getMock('Cake\ORM\Table');
$settings = ['events' => ['Something.special' => ['date_specialed' => true]]];
$this->Behavior = new TimestampBehavior($table, $settings);

$expected = [
'Something.special' => 'handleEvent'
];
$this->assertEquals($expected, $this->Behavior->implementedEvents());
}
Expand All @@ -53,7 +71,7 @@ public function testCreatedAbsent() {
$event = new Event('Model.beforeSave');
$entity = new Entity(['name' => 'Foo']);

$return = $this->Behavior->beforeSave($event, $entity);
$return = $this->Behavior->handleEvent($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');
}
Expand All @@ -73,7 +91,7 @@ public function testCreatedPresent() {
$existingValue = new \DateTime('2011-11-11');
$entity = new Entity(['name' => 'Foo', 'created' => $existingValue]);

$return = $this->Behavior->beforeSave($event, $entity);
$return = $this->Behavior->handleEvent($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');
}
Expand All @@ -93,7 +111,7 @@ public function testCreatedNotNew() {
$entity = new Entity(['name' => 'Foo']);
$entity->isNew(false);

$return = $this->Behavior->beforeSave($event, $entity);
$return = $this->Behavior->handleEvent($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');
}
Expand All @@ -113,7 +131,7 @@ public function testModifiedAbsent() {
$entity = new Entity(['name' => 'Foo']);
$entity->isNew(false);

$return = $this->Behavior->beforeSave($event, $entity);
$return = $this->Behavior->handleEvent($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');
}
Expand All @@ -135,7 +153,7 @@ public function testModifiedPresent() {
$entity->clean();
$entity->isNew(false);

$return = $this->Behavior->beforeSave($event, $entity);
$return = $this->Behavior->handleEvent($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');
}
Expand Down

0 comments on commit dc2238c

Please sign in to comment.