diff --git a/lib/Cake/Test/Case/Controller/ControllerTest.php b/lib/Cake/Test/Case/Controller/ControllerTest.php index dc73e86aa..45793b8cc 100644 --- a/lib/Cake/Test/Case/Controller/ControllerTest.php +++ b/lib/Cake/Test/Case/Controller/ControllerTest.php @@ -768,7 +768,7 @@ class ControllerTest extends CakeTestCase { } /** - * test that beforeRedirect callback returnning null doesn't affect things. + * test that beforeRedirect callback returning null doesn't affect things. * * @return void */ @@ -790,7 +790,7 @@ class ControllerTest extends CakeTestCase { } /** - * test that beforeRedirect callback returnning null doesn't affect things. + * test that beforeRedirect callback returning null doesn't affect things. * * @return void */ @@ -823,7 +823,7 @@ class ControllerTest extends CakeTestCase { } /** - * test that beforeRedirect callback returnning false in controller + * test that beforeRedirect callback returning false in controller * * @return void */ @@ -833,6 +833,7 @@ class ControllerTest extends CakeTestCase { $Controller->Components = $this->getMock('ComponentCollection', array('trigger')); $Controller->expects($this->once())->method('beforeRedirect') + ->with('http://cakephp.org') ->will($this->returnValue(false)); $Controller->response->expects($this->never())->method('header'); $Controller->expects($this->never())->method('_stop'); diff --git a/lib/Cake/Test/Case/Utility/ObjectCollectionTest.php b/lib/Cake/Test/Case/Utility/ObjectCollectionTest.php index b71abe322..d00fb383b 100644 --- a/lib/Cake/Test/Case/Utility/ObjectCollectionTest.php +++ b/lib/Cake/Test/Case/Utility/ObjectCollectionTest.php @@ -18,6 +18,7 @@ */ App::uses('ObjectCollection', 'Utility'); +App::uses('CakeEvent', 'Event'); /** * A generic object class @@ -524,5 +525,60 @@ class ObjectCollectionTest extends CakeTestCase { $result = ObjectCollection::normalizeObjectArray($components); $this->assertEquals($expected, $result); } + +/** + * tests that passing an instance of CakeEvent to trigger will prepend the subject to the list of arguments + * + * @return void + */ + public function testDispatchEventWithSubject() { + $this->_makeMockClasses(); + $this->Objects->load('TriggerMockFirst'); + $this->Objects->load('TriggerMockSecond'); + $this->mockObjects[] = $this->Objects->TriggerMockFirst; + $this->mockObjects[] = $this->Objects->TriggerMockSecond; + + $subjectClass = new Object(); + $this->Objects->TriggerMockFirst->expects($this->once()) + ->method('callback') + ->with($subjectClass, 'first argument') + ->will($this->returnValue(true)); + $this->Objects->TriggerMockSecond->expects($this->once()) + ->method('callback') + ->with($subjectClass, 'first argument') + ->will($this->returnValue(true)); + + $event = new CakeEvent('callback', $subjectClass, array('first argument')); + $this->assertTrue($this->Objects->trigger($event)); + } + +/** + * tests that passing an instance of CakeEvent to trigger with omitSubject property + * will NOT prepend the subject to the list of arguments + * + * @return void + */ + public function testDispatchEventNoSubject() { + $this->_makeMockClasses(); + $this->Objects->load('TriggerMockFirst'); + $this->Objects->load('TriggerMockSecond'); + + $this->mockObjects[] = $this->Objects->TriggerMockFirst; + $this->mockObjects[] = $this->Objects->TriggerMockSecond; + + $subjectClass = new Object(); + $this->Objects->TriggerMockFirst->expects($this->once()) + ->method('callback') + ->with('first argument') + ->will($this->returnValue(true)); + $this->Objects->TriggerMockSecond->expects($this->once()) + ->method('callback') + ->with('first argument') + ->will($this->returnValue(true)); + + $event = new CakeEvent('callback', $subjectClass, array('first argument')); + $event->omitSubject = true; + $this->assertTrue($this->Objects->trigger($event)); + } } \ No newline at end of file diff --git a/lib/Cake/Utility/ObjectCollection.php b/lib/Cake/Utility/ObjectCollection.php index 4c575592d..9a829c883 100644 --- a/lib/Cake/Utility/ObjectCollection.php +++ b/lib/Cake/Utility/ObjectCollection.php @@ -99,8 +99,9 @@ abstract class ObjectCollection { $event = $callback; if (is_array($event->data)) { $params =& $event->data; - } else { - $params = array($event->subject()); + } + if (empty($event->omitSubject)) { + $subject = $event->subject(); } //TODO: Temporary BC check, while we move all the triggers system into the CakeEventManager foreach (array('breakOn', 'collectReturn', 'modParams') as $opt) { @@ -125,7 +126,7 @@ abstract class ObjectCollection { throw new CakeException(__d('cake_dev', 'Cannot use modParams with indexes that do not exist.')); } foreach ($list as $name) { - $result = call_user_func_array(array($this->_loaded[$name], $callback), $params); + $result = call_user_func_array(array($this->_loaded[$name], $callback), compact('subject') + $params); if ($options['collectReturn'] === true) { $collected[] = $result; } diff --git a/lib/Cake/View/HelperCollection.php b/lib/Cake/View/HelperCollection.php index 64ef72667..5e593f51d 100644 --- a/lib/Cake/View/HelperCollection.php +++ b/lib/Cake/View/HelperCollection.php @@ -113,4 +113,43 @@ class HelperCollection extends ObjectCollection implements CakeEventListener { 'View.afterLayout' => 'trigger' ); } + +/** + * Trigger a callback method on every object in the collection. + * Used to trigger methods on objects in the collection. Will fire the methods in the + * order they were attached. + * + * ### Options + * + * - `breakOn` Set to the value or values you want the callback propagation to stop on. + * Can either be a scalar value, or an array of values to break on. Defaults to `false`. + * + * - `break` Set to true to enabled breaking. When a trigger is broken, the last returned value + * will be returned. If used in combination with `collectReturn` the collected results will be returned. + * Defaults to `false`. + * + * - `collectReturn` Set to true to collect the return of each object into an array. + * This array of return values will be returned from the trigger() call. Defaults to `false`. + * + * - `modParams` Allows each object the callback gets called on to modify the parameters to the next object. + * Setting modParams to an integer value will allow you to modify the parameter with that index. + * Any non-null value will modify the parameter index indicated. + * Defaults to false. + * + * + * @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. + * @throws CakeException when modParams is used with an index that does not exist. + */ + public function trigger($callback, $params = array(), $options = array()) { + if ($callback instanceof CakeEvent) { + $callback->omitSubject = true; + } + return parent::trigger($callback, $params, $options); + } }