Skip to content

Commit

Permalink
Migrating Controller events to use the CakeEventManager
Browse files Browse the repository at this point in the history
  • Loading branch information
lorenzo committed Dec 25, 2011
1 parent 0a49bd9 commit 5d67195
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 27 deletions.
20 changes: 19 additions & 1 deletion lib/Cake/Controller/ComponentCollection.php
Expand Up @@ -18,14 +18,15 @@

App::uses('ObjectCollection', 'Utility');
App::uses('Component', 'Controller');
App::uses('CakeEventListener', 'Event');

/**
* Components collection is used as a registry for loaded components and handles loading
* and constructing component class objects.
*
* @package Cake.Controller
*/
class ComponentCollection extends ObjectCollection {
class ComponentCollection extends ObjectCollection implements CakeEventListener {

/**
* The controller that this collection was initialized with.
Expand Down Expand Up @@ -108,4 +109,21 @@ public function load($component, $settings = array()) {
return $this->_loaded[$alias];
}

/**
* Returns the implemented events that will get routed to the trigger function
* in order to dispatch them separately on each component
*
* @return array
*/
public function implementedEvents() {
return array(
'Controller.startup' => array(
array('callable' => 'trigger', 'priority' => 9),
array('callable' => 'trigger')
),
'Controller.beforeRender' => array('callable' => 'trigger'),
'Controller.beforeRedirect' => array('callable' => 'trigger'),
'Controller.shutdown' => array('callable' => 'trigger', 'priority' => 9),
);
}
}
54 changes: 46 additions & 8 deletions lib/Cake/Controller/Controller.php
Expand Up @@ -24,6 +24,9 @@
App::uses('ClassRegistry', 'Utility');
App::uses('ComponentCollection', 'Controller');
App::uses('View', 'View');
App::uses('CakeEvent', 'Event');
App::uses('CakeEventListener', 'Event');
App::uses('CakeEventManager', 'Event');

/**
* Application controller class for organization of business logic.
Expand Down Expand Up @@ -57,7 +60,7 @@
* @property SessionComponent $Session
* @link http://book.cakephp.org/2.0/en/controllers.html
*/
class Controller extends Object {
class Controller extends Object implements CakeEventListener {

/**
* The name of this controller. Controller names are plural, named after the model they manipulate.
Expand Down Expand Up @@ -294,6 +297,14 @@ class Controller extends Object {
*/
protected $_mergeParent = 'AppController';

/**
* Instance of the CakeEventManager this controller is using
* to dispatch inner events.
*
* @var CakeEventManager
*/
protected $_eventManager = null;

/**
* Constructor.
*
Expand Down Expand Up @@ -570,6 +581,21 @@ protected function _mergeControllerVars() {
}
}

/**
* Returns a list of all events that will fire in the controller during it's lifecycle.
* You can override this function to add you own listener callbacks
*
* @return array
*/
public function implementedEvents() {
return array(
'Controller.startup' => 'beforeFilter',
'Controller.beforeRender' => 'beforeRender',
'Controller.beforeRedirect' => array('callable' => 'beforeRedirect', 'passParams' => true),
'Controller.shutdown' => 'afterFilter'
);
}

/**
* Loads Model classes based on the uses property
* see Controller::loadModel(); for more info.
Expand All @@ -590,6 +616,22 @@ public function constructClasses() {
return true;
}

/**
* 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);
$this->_eventManager->attach($this->Components);
}
return $this->_eventManager;
}

/**
* Perform the startup process for this controller.
* Fire the Components and Controller callbacks in the correct order.
Expand All @@ -601,9 +643,7 @@ public function constructClasses() {
* @return void
*/
public function startupProcess() {
$this->Components->trigger('initialize', array(&$this));
$this->beforeFilter();
$this->Components->trigger('startup', array(&$this));
$this->getEventManager()->dispatch(new CakeEvent('Controller.startup', $this));
}

/**
Expand All @@ -616,8 +656,7 @@ public function startupProcess() {
* @return void
*/
public function shutdownProcess() {
$this->Components->trigger('shutdown', array(&$this));
$this->afterFilter();
$this->getEventManager()->dispatch(new CakeEvent('Controller.shutdown', $this));
}

/**
Expand Down Expand Up @@ -862,8 +901,7 @@ public function validateErrors() {
* @link http://book.cakephp.org/2.0/en/controllers.html#Controller::render
*/
public function render($view = null, $layout = null) {
$this->beforeRender();
$this->Components->trigger('beforeRender', array(&$this));
$this->getEventManager()->dispatch(new CakeEvent('Controller.beforeRender', $this));

$viewClass = $this->viewClass;
if ($this->viewClass != 'View') {
Expand Down
71 changes: 55 additions & 16 deletions lib/Cake/Test/Case/Controller/ControllerTest.php
Expand Up @@ -313,31 +313,31 @@ public function beforeRedirect() {
*
* @return void
*/
public function initialize(&$controller) {
public function initialize($controller) {
}

/**
* startup method
*
* @return void
*/
public function startup(&$controller) {
public function startup($controller) {
}

/**
* shutdown method
*
* @return void
*/
public function shutdown(&$controller) {
public function shutdown($controller) {
}

/**
* beforeRender callback
*
* @return void
*/
public function beforeRender(&$controller) {
public function beforeRender($controller) {
if ($this->viewclass) {
$controller->viewClass = $this->viewclass;
}
Expand Down Expand Up @@ -1111,17 +1111,35 @@ public function testControllerHttpCodes() {
* @return void
*/
public function testStartupProcess() {
$Controller = $this->getMock('Controller', array('beforeFilter', 'afterFilter'));
$Controller = $this->getMock('Controller', array('getEventManager'));

$eventManager = $this->getMock('CakeEventManager');
$eventManager->expects($this->once())->method('dispatch')
->with(
$this->logicalAnd(
$this->isInstanceOf('CakeEvent'),
$this->attributeEqualTo('_name', 'Controller.startup'),
$this->attributeEqualTo('_subject', $Controller)
)
);
$Controller->expects($this->once())->method('getEventManager')
->will($this->returnValue($eventManager));
$Controller->startupProcess();
}

$Controller->components = array('MockStartup');
$Controller->Components = $this->getMock('ComponentCollection');
/**
* Tests that the shutdown process calls the correct functions
*
* @return void
*/
public function testStartupProcessIndirect() {
$Controller = $this->getMock('Controller', array('beforeFilter'));

$Controller->expects($this->once())->method('beforeFilter');
$Controller->Components->expects($this->at(0))->method('trigger')
->with('initialize', array(&$Controller));
$Controller->components = array('MockShutdown');
$Controller->Components = $this->getMock('ComponentCollection', array('trigger'));

$Controller->Components->expects($this->at(1))->method('trigger')
->with('startup', array(&$Controller));
$Controller->expects($this->once())->method('beforeFilter');
$Controller->Components->expects($this->exactly(2))->method('trigger')->with($this->isInstanceOf('CakeEvent'));

$Controller->startupProcess();
}
Expand All @@ -1132,14 +1150,35 @@ public function testStartupProcess() {
* @return void
*/
public function testShutdownProcess() {
$Controller = $this->getMock('Controller', array('beforeFilter', 'afterFilter'));
$Controller = $this->getMock('Controller', array('getEventManager'));

$eventManager = $this->getMock('CakeEventManager');
$eventManager->expects($this->once())->method('dispatch')
->with(
$this->logicalAnd(
$this->isInstanceOf('CakeEvent'),
$this->attributeEqualTo('_name', 'Controller.shutdown'),
$this->attributeEqualTo('_subject', $Controller)
)
);
$Controller->expects($this->once())->method('getEventManager')
->will($this->returnValue($eventManager));
$Controller->shutdownProcess();
}

/**
* Tests that the shutdown process calls the correct functions
*
* @return void
*/
public function testShutdownProcessIndirect() {
$Controller = $this->getMock('Controller', array('afterFilter'));

$Controller->components = array('MockShutdown');
$Controller->Components = $this->getMock('ComponentCollection');
$Controller->Components = $this->getMock('ComponentCollection', array('trigger'));

$Controller->expects($this->once())->method('afterFilter');
$Controller->Components->expects($this->once())->method('trigger')
->with('shutdown', array(&$Controller));
$Controller->Components->expects($this->exactly(1))->method('trigger')->with($this->isInstanceOf('CakeEvent'));

$Controller->shutdownProcess();
}
Expand Down
13 changes: 11 additions & 2 deletions lib/Cake/Utility/ObjectCollection.php
Expand Up @@ -82,8 +82,10 @@ abstract public function load($name, $options = array());
* Defaults to false.
*
*
* @param string $callback Method to fire on all the objects. Its assumed all the objects implement
* the method you are calling.
* @param string $callback|CakeEvent Method to fire on all the objects. Its assumed all the objects implement
* the method you are calling. If an instance of CakeEvent is provided, then then Event name will parsed to
* get the callback name. This is done by getting the last word after any dot in the event name
* (eg. `Model.afterSave` event will trigger the `afterSave` callback)
* @param array $params Array of parameters for the triggered callback.
* @param array $options Array of options.
* @return mixed Either the last result or all results if collectReturn is on.
Expand All @@ -93,6 +95,13 @@ public function trigger($callback, $params = array(), $options = array()) {
if (empty($this->_enabled)) {
return true;
}
if ($callback instanceof CakeEvent) {
if (is_array($callback->data)) {
$params = $callback->data;
}
$params = array($callback->subject());

This comment has been minimized.

Copy link
@majna

majna Dec 29, 2011

Contributor

Missing else {} here?

This comment has been minimized.

Copy link
@lorenzo

lorenzo Dec 29, 2011

Author Member

The code changed quite a bit after this commit :)

$callback = array_pop(explode('.', $callback->name()));
}
$options = array_merge(
array(
'break' => false,
Expand Down

0 comments on commit 5d67195

Please sign in to comment.