4

In Zend Framework, we can forward to another controller's action using _forward()

E.g.

// Inside controller1
$this->_forward('foo', "controller2");
echo $this->getResponse(); // Echo the response of foo action at this point

But the _forward is taken at the end of request cycle, what if I want to forward happen immediately and echo out the response immediately?

Charles
  • 50,943
  • 13
  • 104
  • 142
Howard
  • 19,215
  • 35
  • 112
  • 184
  • 2
    For a very detailed explanation of how `_forward` works, see [this question](http://stackoverflow.com/questions/1647594/zend-framework-what-this-forward-is-doing). – Bryan M. Nov 04 '10 at 13:47
  • What I want to is to get the response of _forward action, i.e. sub-request. – Howard Nov 04 '10 at 15:42
  • Maybe we can help if you post more details about what you're really trying to achieve ... What kind of manipulation you need and which is the purpose? – Keyne Viana Nov 08 '10 at 21:02

8 Answers8

2

You should do

$this->_forward('foo', "controller2");
return;
Tomáš Fejfar
  • 11,129
  • 8
  • 54
  • 82
  • Yes, but I want to capture the response of "$this->_forward('foo', "controller2");" and do some manipulations on the output in the current action. – Howard Nov 07 '10 at 03:14
  • Then you're doing it wrong. What is this construction used for? It seems to me like mixing model logic into controller ;) – Tomáš Fejfar Nov 08 '10 at 09:36
  • what you can do is to register the response manipulation code as action helper (dispatchLoopShutdown) and set the forwarded action to render to different output segment. Then pick the response segment in helper, alter it and inject it into the default response segment. – Tomáš Fejfar Nov 08 '10 at 09:38
  • In ZF there is this saying: "If anything you're trying to do seems impossible, then you probably have some flaws in design." – Tomáš Fejfar Nov 08 '10 at 09:39
1

I would personally suggest an Action plugin, or View Helper to solve your problem. I have made many, many of these in the past, and they can be extremely helpful and powerful.

Or if you are just trying to get a module of your site, like a login/categories section, might wanna consider the view helper "partial".

View Helpers : http:// framework.zend.com/manual/en/zend.view.helpers.html

KSolo
  • 477
  • 3
  • 11
0

The _forward function forwards after the current action has run, just in case you did know.

Alex Pliutau
  • 21,392
  • 27
  • 113
  • 143
0

It sounds like you're repeating code, in which case this logic should be put onto a model. This way you can call the functionality whenever you need to

Ashley
  • 5,939
  • 9
  • 39
  • 82
0

It think you are looking for the HMVC pattern. I've asked a question similar to this before, but it seems it is just not possible with Zend Framework. There is a framework called DotKernel which is an extension to Zend Framework. They say it implements (a simplified version of) HMVC, but I never used it.

Is switched to Kohana 3. This framework has native support for HMVC and can do what you are looking for.

Community
  • 1
  • 1
Rene Terstegen
  • 7,911
  • 18
  • 52
  • 74
  • Does this have something with Zend Action Stack? – Keyne Viana Nov 09 '10 at 15:38
  • @Keyne: I don't understand what you mean... If you mean it should be possible with Zend Action Stack. Check http://www.rmauger.co.uk/2009/03/why-the-zend-framework-actionstack-is-evil/. The fact is that what he wants to achieve is exactly what the HMVC pattern offers. – Rene Terstegen Nov 09 '10 at 15:47
  • Actually this is a question... I've never used ActionStack, I just read about that... and was wondering if this has something with this pattern. – Keyne Viana Nov 09 '10 at 17:04
  • @Kenye: Ok, no problem. Sorry if I sounded offensive :). – Rene Terstegen Nov 10 '10 at 07:40
0

As many of the answers here state, there is a good chance that your design could use some review and some of the logic should be refactored to the model classes or action helpers. The two biggest warning signs of this are:

  • You are trying to manipulate the response from that other "inner" action at the point in which your "outer" action is happening
  • You are trying to manipulate a shared object/model

Both of these cases will create a bad interdependence in one or both directions and will result in code reuse that is very difficult to maintain in the long run.

However, there are some common scenarios where I've found "inner" actions very useful for reusing controller actions cleanly. The most common of these is when you really only need the inner action's response within the outer action's view. From within your view, you can easily use the Zend_View_Helper_Action:

print $this->action('foo', 'controller2');

For example, this can be useful when including some javascript created by another view. For example, MenuController::viewAction might have a response like this:

(function() { 
  var menuComponent = new MenuObject({dynamicOptions: ...}); 
  return menuComponent;
})()

Now in DefaultController::indexAction's view script you can do something like this:

var mainmenu = <?php print $this->action('view', 'menu'); ?>;
navigationBar.add(mainmenu);

Additionally, in SpecificModule_DefaultController::indexAction's view script you can do something like this:

var modulemenu = <?php 
  print $this->action('view', 'menu', 'default', array(
    'Module' => 'SpecificModule'
  ));
?>;
moduleWidgetMenu.add(modulemenu);

This can be a powerful technique for reusing actions and essentially creates a nested MVC request (see the other answer here about HMVC). In the above example, we have the rendering of a menu centralized to one action and we are able to use that in a variety of ways. More over, we have more flexibility in the execution of that action since we get the full action lifecycle (dispatch/plugins/etc.) in a separate execution context rather than overloading logic in to large view and/or action helpers.

Two very very crucial things to make sure of that will help your design stay safe and sane while using this approach:

  • The "inner" request (both the action and its associated view) should have no concept of the "outer" request, or even know about its existence at all.
  • The "outer" request (both the action and its associated view) should have no control of the inner request except as passed through as parameters to the $this->action($actionName, $controllerName, $moduleName, $paramsArray) call.

This approach generally has worked best with the least maintenance necessary when not modifying the standard MVC lifecycle that ZF implements, including using the two-step view approach with the ViewRenderer action helper.

Shog9
  • 156,901
  • 35
  • 231
  • 235
Kevin Vaughan
  • 14,320
  • 4
  • 29
  • 22
0

Here is what I use:

class MyController extends Zend_Controller_Action {

    public function indexAction() {
       $response = $this->_redispatch('world');
       echo 'hello '.$response->getBody() // outputs 'hello world'
    }

    public function worldAction() {
       echo 'world';
    }

    protected function _redispatch($action, $controller = null, $module = null, array $params = null) {
        $request = new Zend_Controller_Request_Simple;
        $response = new Zend_Controller_Response_Http;

        $request->setActionName($action);

        if (isset($controller)) {
            $request->setControllerName($controller);   
        } else {
            $request->setControllerName($this->_request->getControllerName());
        }

        if (isset($module)) {
            $request->setModuleName($module);   
        } else {
            $request->setModuleName($this->_request->getModuleName());
        }

        if (!empty($params)) {
            foreach ($params as $key => $value) {
                $request->setParam($key, $value);   
            }
        }

        $dispatcher = $this->getFrontController()->getDispatcher();
        $dispatcher->dispatch($request, $response);

        return $response;
    }

}
netcoder
  • 66,435
  • 19
  • 125
  • 142
0

Zend_Controller_Dispatcher_Standard::dispatch() would probably work better for what you need. Pass in the request (Zend_Controller_Front::getRequest()) and a Zend_Controller_Response object. When the method returns, you can catch the response via Zend_Controller_Front::getResponse().

Here is an example from Zend/Test/PHPUnit/ControllerTestCaseTest.php:

$this->testCase->getFrontController()->setControllerDirectory(dirname(__FILE__) . '/_files/application/controllers');
$this->testCase->dispatch('/zend-test-php-unit-foo/bar');
$request  = $this->testCase->getRequest();
$response = $this->testCase->getResponse();
$content  = $response->getBody();
$this->assertEquals('zend-test-php-unit-foo', $request->getControllerName(), $content);
$this->assertEquals('bar', $request->getActionName());
$this->assertContains('FooController::barAction', $content, $content);
Kris Hardy
  • 548
  • 4
  • 12