Migrating View and Helpers events to use the CakeEvenManager

This commit is contained in:
Jose Lorenzo Rodriguez 2011-12-25 19:00:19 -04:30
parent 5d67195bf7
commit 078a2dfd72
4 changed files with 137 additions and 32 deletions

View file

@ -584,24 +584,74 @@ class ViewTest extends CakeTestCase {
$View->Helpers = $this->getMock('HelperCollection', array('trigger'), array($View)); $View->Helpers = $this->getMock('HelperCollection', array('trigger'), array($View));
$View->Helpers->expects($this->at(0))->method('trigger') $View->Helpers->expects($this->at(0))->method('trigger')
->with('beforeRender', $this->anything()); ->with(
$this->logicalAnd(
$this->isInstanceOf('CakeEvent'),
$this->attributeEqualTo('_name', 'View.beforeRender'),
$this->attributeEqualTo('_subject', $View)
)
);
$View->Helpers->expects($this->at(1))->method('trigger') $View->Helpers->expects($this->at(1))->method('trigger')
->with('beforeRenderFile', $this->anything()); ->with(
$View->Helpers->expects($this->at(2))->method('trigger') $this->logicalAnd(
->with('afterRenderFile', $this->anything()) $this->isInstanceOf('CakeEvent'),
->will($this->returnValue('')); $this->attributeEqualTo('_name', 'View.beforeRenderFile'),
$View->Helpers->expects($this->at(3))->method('trigger') $this->attributeEqualTo('_subject', $View)
->with('afterRender', $this->anything()); )
);
$View->Helpers->expects($this->at(2))->method('trigger')
->with(
$this->logicalAnd(
$this->isInstanceOf('CakeEvent'),
$this->attributeEqualTo('_name', 'View.afterRenderFile'),
$this->attributeEqualTo('_subject', $View)
)
);
$View->Helpers->expects($this->at(3))->method('trigger')
->with(
$this->logicalAnd(
$this->isInstanceOf('CakeEvent'),
$this->attributeEqualTo('_name', 'View.afterRender'),
$this->attributeEqualTo('_subject', $View)
)
);
$View->Helpers->expects($this->at(4))->method('trigger') $View->Helpers->expects($this->at(4))->method('trigger')
->with('beforeLayout', $this->anything()); ->with(
$this->logicalAnd(
$this->isInstanceOf('CakeEvent'),
$this->attributeEqualTo('_name', 'View.beforeLayout'),
$this->attributeEqualTo('_subject', $View)
)
);
$View->Helpers->expects($this->at(5))->method('trigger') $View->Helpers->expects($this->at(5))->method('trigger')
->with('beforeRenderFile', $this->anything()); ->with(
$this->logicalAnd(
$this->isInstanceOf('CakeEvent'),
$this->attributeEqualTo('_name', 'View.beforeRenderFile'),
$this->attributeEqualTo('_subject', $View)
)
);
$View->Helpers->expects($this->at(6))->method('trigger') $View->Helpers->expects($this->at(6))->method('trigger')
->with('afterRenderFile', $this->anything()) ->with(
->will($this->returnValue('')) ; $this->logicalAnd(
$this->isInstanceOf('CakeEvent'),
$this->attributeEqualTo('_name', 'View.afterRenderFile'),
$this->attributeEqualTo('_subject', $View)
)
);
$View->Helpers->expects($this->at(7))->method('trigger') $View->Helpers->expects($this->at(7))->method('trigger')
->with('afterLayout', $this->anything()); ->with(
$this->logicalAnd(
$this->isInstanceOf('CakeEvent'),
$this->attributeEqualTo('_name', 'View.afterLayout'),
$this->attributeEqualTo('_subject', $View)
)
);
$View->render('index'); $View->render('index');
} }

View file

@ -96,11 +96,17 @@ abstract class ObjectCollection {
return true; return true;
} }
if ($callback instanceof CakeEvent) { if ($callback instanceof CakeEvent) {
if (is_array($callback->data)) { $event = $callback;
$params = $callback->data; if (is_array($event->data)) {
$params =& $event->data;
} else {
$params = array($event->subject());
} }
$params = array($callback->subject()); //TODO: Temporary BC check, while we move all the triggers system into the CakeEventManager
$callback = array_pop(explode('.', $callback->name())); if (isset($event->modParams)) {
$options['modParams'] = $event->modParams;
}
$callback = array_pop(explode('.', $event->name()));
} }
$options = array_merge( $options = array_merge(
array( array(

View file

@ -17,6 +17,7 @@
*/ */
App::uses('ObjectCollection', 'Utility'); App::uses('ObjectCollection', 'Utility');
App::uses('CakeEventListener', 'Event');
/** /**
* Helpers collection is used as a registry for loaded helpers and handles loading * Helpers collection is used as a registry for loaded helpers and handles loading
@ -24,7 +25,7 @@ App::uses('ObjectCollection', 'Utility');
* *
* @package Cake.View * @package Cake.View
*/ */
class HelperCollection extends ObjectCollection { class HelperCollection extends ObjectCollection implements CakeEventListener {
/** /**
* View object to use when making helpers. * View object to use when making helpers.
@ -97,4 +98,19 @@ class HelperCollection extends ObjectCollection {
return $this->_loaded[$alias]; return $this->_loaded[$alias];
} }
/**
* Returns a list of all events that will fire in the View during it's lifecycle.
*
* @return array
*/
public function implementedEvents() {
return array(
'View.beforeRenderFile' => 'trigger',
'View.afterRenderFile' => 'trigger',
'View.beforeRender' => 'trigger',
'View.afterRender' => 'trigger',
'View.beforeLayout' => 'trigger',
'View.afterLayout' => 'trigger'
);
}
} }

View file

@ -21,6 +21,8 @@ App::uses('HelperCollection', 'View');
App::uses('AppHelper', 'View/Helper'); App::uses('AppHelper', 'View/Helper');
App::uses('Router', 'Routing'); App::uses('Router', 'Routing');
App::uses('ViewBlock', 'View'); App::uses('ViewBlock', 'View');
App::uses('CakeEvent', 'Event');
App::uses('CakeEventManager', 'Event');
/** /**
* View, the V in the MVC triad. View interacts with Helpers and view variables passed * View, the V in the MVC triad. View interacts with Helpers and view variables passed
@ -262,6 +264,23 @@ class View extends Object {
*/ */
protected $_stack = array(); protected $_stack = array();
/**
* Instance of the CakeEventManager this View object is using
* to dispatch inner events. Usually the manager is shared with
* the controller, so it it possible to register view events in
* the controller layer.
*
* @var CakeEventManager
*/
protected $_eventManager = null;
/**
* Whether the event manager was already configured for this object
*
* @var boolean
*/
protected $_eventManagerConfigured = false;
const TYPE_VIEW = 'view'; const TYPE_VIEW = 'view';
const TYPE_ELEMENT = 'element'; const TYPE_ELEMENT = 'element';
const TYPE_LAYOUT = 'layout'; const TYPE_LAYOUT = 'layout';
@ -278,12 +297,29 @@ class View extends Object {
$var = $this->_passedVars[$j]; $var = $this->_passedVars[$j];
$this->{$var} = $controller->{$var}; $this->{$var} = $controller->{$var};
} }
$this->_eventManager = $controller->getEventManager();
} }
$this->Helpers = new HelperCollection($this); $this->Helpers = new HelperCollection($this);
$this->Blocks = new ViewBlock(); $this->Blocks = new ViewBlock();
parent::__construct(); parent::__construct();
} }
/**
* 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->_eventManagerConfigured) {
$this->_eventManager = new CakeEventManager();
$this->_eventManager->attach($this->Helpers);
$this->_eventManagerConfigured = true;
}
return $this->_eventManager;
}
/** /**
* Renders a piece of PHP with provided parameters and returns HTML, XML, or any other string. * Renders a piece of PHP with provided parameters and returns HTML, XML, or any other string.
* *
@ -347,14 +383,14 @@ class View extends Object {
$this->loadHelpers(); $this->loadHelpers();
} }
if ($callbacks) { if ($callbacks) {
$this->Helpers->trigger('beforeRender', array($file)); $this->getEventManager()->dispatch(new CakeEvent('View.beforeRender', $this, array($file)));
} }
$this->_currentType = self::TYPE_ELEMENT; $this->_currentType = self::TYPE_ELEMENT;
$element = $this->_render($file, array_merge($this->viewVars, $data)); $element = $this->_render($file, array_merge($this->viewVars, $data));
if ($callbacks) { if ($callbacks) {
$this->Helpers->trigger('afterRender', array($file, $element)); $this->getEventManager()->dispatch(new CakeEvent('View.afterRender', $this, array($file, $element)));
} }
if (isset($options['cache'])) { if (isset($options['cache'])) {
Cache::write($key, $element, $caching['config']); Cache::write($key, $element, $caching['config']);
@ -397,9 +433,9 @@ class View extends Object {
if ($view !== false && $viewFileName = $this->_getViewFileName($view)) { if ($view !== false && $viewFileName = $this->_getViewFileName($view)) {
$this->_currentType = self::TYPE_VIEW; $this->_currentType = self::TYPE_VIEW;
$this->Helpers->trigger('beforeRender', array($viewFileName)); $this->getEventManager()->dispatch(new CakeEvent('View.beforeRender', $this, array($viewFileName)));
$this->Blocks->set('content', $this->_render($viewFileName)); $this->Blocks->set('content', $this->_render($viewFileName));
$this->Helpers->trigger('afterRender', array($viewFileName)); $this->getEventManager()->dispatch(new CakeEvent('View.afterRender', $this, array($viewFileName)));
} }
if ($layout === null) { if ($layout === null) {
@ -447,7 +483,7 @@ class View extends Object {
if (empty($content)) { if (empty($content)) {
$content = $this->Blocks->get('content'); $content = $this->Blocks->get('content');
} }
$this->Helpers->trigger('beforeLayout', array($layoutFileName)); $this->getEventManager()->dispatch(new CakeEvent('View.beforeLayout', $this, array($layoutFileName)));
$scripts = implode("\n\t", $this->_scripts); $scripts = implode("\n\t", $this->_scripts);
$scripts .= $this->get('meta') . $this->get('css') . $this->get('script'); $scripts .= $this->get('meta') . $this->get('css') . $this->get('script');
@ -464,7 +500,7 @@ class View extends Object {
$this->_currentType = self::TYPE_LAYOUT; $this->_currentType = self::TYPE_LAYOUT;
$this->Blocks->set('content', $this->_render($layoutFileName)); $this->Blocks->set('content', $this->_render($layoutFileName));
$this->Helpers->trigger('afterLayout', array($layoutFileName)); $this->getEventManager()->dispatch(new CakeEvent('View.afterLayout', $this, array($layoutFileName)));
return $this->Blocks->get('content'); return $this->Blocks->get('content');
} }
@ -768,19 +804,16 @@ class View extends Object {
} }
$this->_current = $viewFile; $this->_current = $viewFile;
$this->Helpers->trigger('beforeRenderFile', array($viewFile)); $this->getEventManager()->dispatch(new CakeEvent('View.beforeRenderFile', $this, array($viewFile)));
$content = $this->_evaluate($viewFile, $data); $content = $this->_evaluate($viewFile, $data);
if ($this->Blocks->active()) { if ($this->Blocks->active()) {
throw new CakeException(__d('cake_dev', 'The "%s" block was left open.', $this->Blocks->active())); throw new CakeException(__d('cake_dev', 'The "%s" block was left open.', $this->Blocks->active()));
} }
$result = $this->Helpers->trigger( $afterEvent = new CakeEvent('View.afterRenderFile', $this, array($viewFile, $content));
'afterRenderFile', //TODO: For BC puporses, set extra info in the event object. Remove when appropriate
array($viewFile, $content), $afterEvent->modParams = 1;
array('modParams' => 1) $this->getEventManager()->dispatch($afterEvent);
); $content = $afterEvent->data[1];
if ($result !== true) {
$content = $result;
}
if (isset($this->_parents[$viewFile])) { if (isset($this->_parents[$viewFile])) {
$this->_stack[] = $this->fetch('content'); $this->_stack[] = $this->fetch('content');