diff --git a/lib/Cake/Controller/Controller.php b/lib/Cake/Controller/Controller.php index a5b912feb85..01f2c052dd6 100644 --- a/lib/Cake/Controller/Controller.php +++ b/lib/Cake/Controller/Controller.php @@ -732,9 +732,8 @@ public function redirect($url, $status = null, $exit = true) { extract($status, EXTR_OVERWRITE); } $event = new CakeEvent('Controller.beforeRedirect', $this, array($url, $status, $exit)); - //TODO: Remove the following two lines when the events are fully migrated to the CakeEventManager - $event->breakOn = false; - $event->collectReturn = true; + //TODO: Remove the following line when the events are fully migrated to the CakeEventManager + list($event->break, $event->breakOn, $event->collectReturn) = array(true, false, true); $this->getEventManager()->dispatch($event); if ($event->isStopped()) { diff --git a/lib/Cake/Model/BehaviorCollection.php b/lib/Cake/Model/BehaviorCollection.php index 1b0e5d8ed0a..3b8bcbd6ef7 100644 --- a/lib/Cake/Model/BehaviorCollection.php +++ b/lib/Cake/Model/BehaviorCollection.php @@ -20,6 +20,7 @@ */ App::uses('ObjectCollection', 'Utility'); +App::uses('CakeEventListener', 'Event'); /** * Model behavior collection class. @@ -28,7 +29,7 @@ * * @package Cake.Model */ -class BehaviorCollection extends ObjectCollection { +class BehaviorCollection extends ObjectCollection implements CakeEventListener { /** * Stores a reference to the attached name @@ -271,4 +272,21 @@ public function hasMethod($method, $callback = false) { return false; } +/** + * Returns the implemented events that will get routed to the trigger function + * in order to dispatch them separately on each behavior + * + * @return array + */ + public function implementedEvents() { + return array( + 'Model.beforeFind' => 'trigger', + 'Model.afterFind' => 'trigger', + 'Model.beforeValidate' => 'trigger', + 'Model.beforeSave' => 'trigger', + 'Model.afterSave' => 'trigger', + 'Model.beforeDelete' => 'trigger', + 'Model.afterDelete' => 'trigger' + ); + } } diff --git a/lib/Cake/Model/Model.php b/lib/Cake/Model/Model.php index 7f466a8a607..1bbf103fcfc 100644 --- a/lib/Cake/Model/Model.php +++ b/lib/Cake/Model/Model.php @@ -27,6 +27,9 @@ App::uses('ModelBehavior', 'Model'); App::uses('ConnectionManager', 'Model'); App::uses('Xml', 'Utility'); +App::uses('CakeEvent', 'Event'); +App::uses('CakeEventListener', 'Event'); +App::uses('CakeEventManager', 'Event'); /** * Object-relational mapper. @@ -39,7 +42,7 @@ * @package Cake.Model * @link http://book.cakephp.org/2.0/en/models.html */ -class Model extends Object { +class Model extends Object implements CakeEventListener { /** * The name of the DataSource connection that this Model uses @@ -607,6 +610,14 @@ class Model extends Object { 'neighbors' => true, 'list' => true, 'threaded' => true ); +/** + * Instance of the CakeEventManager this model is using + * to dispatch inner events. + * + * @var CakeEventManager + */ + protected $_eventManager = null; + /** * Constructor. Binds the model's database table to the object. * @@ -712,6 +723,40 @@ public function __construct($id = false, $table = null, $ds = null) { $this->Behaviors->init($this->alias, $this->actsAs); } +/** + * Returns a list of all events that will fire in the model during it's lifecycle. + * You can override this function to add you own listener callbacks + * + * @return array + */ + public function implementedEvents() { + return array( + 'Model.beforeFind' => array('callable' => 'beforeFind', 'passParams' => true), + 'Model.afterFind' => array('callable' => 'afterFind', 'passParams' => true), + 'Model.beforeValidate' => array('callable' => 'beforeValidate', 'passParams' => true), + 'Model.beforeSave' => array('callable' => 'beforeSave', 'passParams' => true), + 'Model.afterSave' => array('callable' => 'afterSave', 'passParams' => true), + 'Model.beforeDelete' => array('callable' => 'beforeDelete', 'passParams' => true, 'priority' => 9), + 'Model.afterDelete' => array('callable' => 'afterDelete'), + ); + } + +/** + * Returns the CakeEventManager manager instance that is handling any callbacks. + * You can use this instance to register any new listeners or callbacks to the + * controller events, or create your own events and trigger them at will. + * + * @return CakeEventManager + */ + public function getEventManager() { + if (empty($this->_eventManager)) { + $this->_eventManager = new CakeEventManager(); + $this->_eventManager->attach($this->Behaviors); + $this->_eventManager->attach($this); + } + return $this->_eventManager; + } + /** * Handles custom method calls, like findBy for DB models, * and custom RPC calls for remote data sources. @@ -1585,10 +1630,10 @@ public function save($data = null, $validate = true, $fieldList = array()) { } if ($options['callbacks'] === true || $options['callbacks'] === 'before') { - $result = $this->Behaviors->trigger('beforeSave', array(&$this, $options), array( - 'break' => true, 'breakOn' => array(false, null) - )); - if (!$result || !$this->beforeSave($options)) { + $event = new CakeEvent('Model.beforeSave', $this, array($options)); + list($event->break, $event->breakOn) = array(true, array(false, null)); + $this->getEventManager()->dispatch($event); + if (!$event->result) { $this->whitelist = $_whitelist; return false; } @@ -1672,8 +1717,8 @@ public function save($data = null, $validate = true, $fieldList = array()) { } } if ($options['callbacks'] === true || $options['callbacks'] === 'after') { - $this->Behaviors->trigger('afterSave', array(&$this, $created, $options)); - $this->afterSave($created); + $event = new CakeEvent('Model.afterSave', $this, array($created, $options)); + $this->getEventManager()->dispatch($event); } if (!empty($this->data)) { $success = Set::merge($success, $this->data); @@ -2236,13 +2281,11 @@ public function delete($id = null, $cascade = true) { } $id = $this->id; - if ($this->beforeDelete($cascade)) { - $filters = $this->Behaviors->trigger( - 'beforeDelete', - array(&$this, $cascade), - array('break' => true, 'breakOn' => array(false, null)) - ); - if (!$filters || !$this->exists()) { + $event = new CakeEvent('Model.beforeDelete', $this, array($cascade)); + list($event->break, $event->breakOn) = array(true, array(false, null)); + $this->getEventManager()->dispatch($event); + if (!$event->isStopped()) { + if (!$this->exists()) { return false; } $db = $this->getDataSource(); @@ -2272,8 +2315,7 @@ public function delete($id = null, $cascade = true) { if ($updateCounterCache) { $this->updateCounterCache($keys[$this->alias]); } - $this->Behaviors->trigger('afterDelete', array(&$this)); - $this->afterDelete(); + $this->getEventManager()->dispatch(new CakeEvent('Model.afterDelete', $this)); $this->_clearCache(); $this->id = false; return true; @@ -2566,24 +2608,13 @@ public function buildQuery($type = 'first', $query = array()) { $query['order'] = array($query['order']); if ($query['callbacks'] === true || $query['callbacks'] === 'before') { - $return = $this->Behaviors->trigger( - 'beforeFind', - array(&$this, $query), - array('break' => true, 'breakOn' => array(false, null), 'modParams' => 1) - ); - - $query = (is_array($return)) ? $return : $query; - - if ($return === false) { - return null; - } - - $return = $this->beforeFind($query); - $query = (is_array($return)) ? $return : $query; - - if ($return === false) { + $event = new CakeEvent('Model.beforeFind', $this, array($query)); + list($event->break, $event->breakOn, $event->modParams) = array(true, array(false, null), 0); + $this->getEventManager()->dispatch($event); + if ($event->isStopped()) { return null; } + $query = $event->data[0]; } return $query; @@ -2814,15 +2845,10 @@ protected function _findThreaded($state, $query, $results = array()) { * @return array Set of filtered results */ protected function _filterResults($results, $primary = true) { - $return = $this->Behaviors->trigger( - 'afterFind', - array(&$this, $results, $primary), - array('modParams' => 1) - ); - if ($return !== true) { - $results = $return; - } - return $this->afterFind($results, $primary); + $event = new CakeEvent('Model.afterFind', $this, array($results, $primary)); + $event->modParams = 0; + $this->getEventManager()->dispatch($event); + return $event->result; } /** @@ -2936,14 +2962,10 @@ public function validates($options = array()) { * @see Model::validates() */ public function invalidFields($options = array()) { - if ( - !$this->Behaviors->trigger( - 'beforeValidate', - array(&$this, $options), - array('break' => true, 'breakOn' => false) - ) || - $this->beforeValidate($options) === false - ) { + $event = new CakeEvent('Model.beforeValidate', $this, array($options)); + list($event->break, $event->breakOn) = array(true, false); + $this->getEventManager()->dispatch($event); + if ($event->isStopped()) { return false; } diff --git a/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php b/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php index f2cea74f895..6c23daf4c8e 100644 --- a/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php +++ b/lib/Cake/Test/Case/Model/BehaviorCollectionTest.php @@ -859,31 +859,31 @@ public function testBehaviorSaveCallbacks() { $Sample->Behaviors->attach('Test', array('beforeSave' => 'on')); $Sample->create(); - $this->assertSame($Sample->save($record), false); + $this->assertSame(false, $Sample->save($record)); $Sample->Behaviors->attach('Test', array('beforeSave' => 'off')); $Sample->create(); $result = $Sample->save($record); $expected = $record; $expected['Sample']['id'] = $Sample->id; - $this->assertSame($result, $expected); + $this->assertSame($expected, $result); $Sample->Behaviors->attach('Test', array('beforeSave' => 'test')); $Sample->create(); $result = $Sample->save($record); $expected = $record; $expected['Sample']['id'] = $Sample->id; - $this->assertSame($result, $expected); + $this->assertSame($expected, $result); $Sample->Behaviors->attach('Test', array('beforeSave' => 'modify')); $expected = Set::insert($record, 'Sample.name', 'sample99 modified before'); $Sample->create(); $result = $Sample->save($record); $expected['Sample']['id'] = $Sample->id; - $this->assertSame($result, $expected); + $this->assertSame($expected, $result); $Sample->Behaviors->disable('Test'); - $this->assertSame($Sample->save($record), $record); + $this->assertSame($record, $Sample->save($record)); $Sample->Behaviors->attach('Test', array('beforeSave' => 'off', 'afterSave' => 'on')); $expected = Set::merge($record, array('Sample' => array('aftersave' => 'modified after on create'))); @@ -897,21 +897,21 @@ public function testBehaviorSaveCallbacks() { $Sample->create(); $result = $Sample->save($record); $expected['Sample']['id'] = $Sample->id; - $this->assertSame($result, $expected); + $this->assertSame($expected, $result); $Sample->Behaviors->attach('Test', array('beforeSave' => 'off', 'afterSave' => 'test')); $Sample->create(); $expected = $record; $result = $Sample->save($record); $expected['Sample']['id'] = $Sample->id; - $this->assertSame($result, $expected); + $this->assertSame($expected, $result); $Sample->Behaviors->attach('Test', array('afterSave' => 'test2')); $Sample->create(); $expected = $record; $result = $Sample->save($record); $expected['Sample']['id'] = $Sample->id; - $this->assertSame($result, $expected); + $this->assertSame($expected, $result); $Sample->Behaviors->attach('Test', array('beforeFind' => 'off', 'afterFind' => 'off')); $Sample->recursive = -1; @@ -920,12 +920,12 @@ public function testBehaviorSaveCallbacks() { $Sample->Behaviors->attach('Test', array('afterSave' => 'on')); $expected = Set::merge($record2, array('Sample' => array('aftersave' => 'modified after'))); $Sample->create(); - $this->assertSame($Sample->save($record2), $expected); + $this->assertSame($expected, $Sample->save($record2)); $Sample->Behaviors->attach('Test', array('afterSave' => 'modify')); $expected = Set::merge($record2, array('Sample' => array('name' => 'sample1 modified after'))); $Sample->create(); - $this->assertSame($Sample->save($record2), $expected); + $this->assertSame($expected, $Sample->save($record2)); } /** diff --git a/lib/Cake/Test/Case/Model/ModelReadTest.php b/lib/Cake/Test/Case/Model/ModelReadTest.php index 9ecaec5da62..51ed48ce0c4 100644 --- a/lib/Cake/Test/Case/Model/ModelReadTest.php +++ b/lib/Cake/Test/Case/Model/ModelReadTest.php @@ -5041,7 +5041,7 @@ public function testCallbackSourceChange() { * @return void */ public function testCallbackSourceChangeUnknownDatasource() { - $this->loadFixtures('Post'); + $this->loadFixtures('Post', 'Author'); $TestModel = new Post(); $this->assertFalse($TestModel->find('all', array('connection' => 'foo'))); } diff --git a/lib/Cake/Utility/ObjectCollection.php b/lib/Cake/Utility/ObjectCollection.php index 9a829c88371..539de19289e 100644 --- a/lib/Cake/Utility/ObjectCollection.php +++ b/lib/Cake/Utility/ObjectCollection.php @@ -104,7 +104,7 @@ public function trigger($callback, $params = array(), $options = array()) { $subject = $event->subject(); } //TODO: Temporary BC check, while we move all the triggers system into the CakeEventManager - foreach (array('breakOn', 'collectReturn', 'modParams') as $opt) { + foreach (array('break', 'breakOn', 'collectReturn', 'modParams') as $opt) { if (isset($event->{$opt})) { $options[$opt] = $event->{$opt}; }