Question

How to redirect to module/controller/action when I'm inside an observer method? I need to redirect to myrouter/adminhtml_test/validate.

Why I need this: I try to fire my controller's action which has to perform some additional calculations after "Save Config" was clicked in System -> Configuration -> Mysection. I registered observer for the event admin_system_config_changed_section_{$section}.

In my method handle_adminSystemConfigChangedSection() in Model/Observer.php I try to fire my action but it doesn't redirect to that action:

class My_Module_Model_Observer
{
    public function handle_adminSystemConfigChangedSection()
    {
        //I tried this code but it doesn't trigger the action:
        $url = Mage::helper('adminhtml')->getUrl("myrouter/adminhtml_test/validate");
        Mage::log($url); //To check if URL is correct (and it is correct)
        Mage::app()->getResponse()->setRedirect($url);
    }
}

Observer method and controller work fine (below are configuration details). I replaced the observer method with this code (from Magento forum) using header() and it works:

public function handle_adminSystemConfigChangedSection()
{
    $url = Mage::helper('adminhtml')->getUrl("myrouter/adminhtml_test/validate");
    Mage::log($url);
    header( 'Location: ' . $url );
    exit();
}

But this doesn't seem to be a correct way of redirecting to an action. How it should be done?

Configuration details:

Observer and router configuration in config.xml:

<config>
...
    <global>
        ...
        <events>
            <admin_system_config_changed_section_mysection>
                <observers>
                    <mymodule>
                        <type>singleton</type>
                        <class>mymodule/observer</class>
                        <method>handle_adminSystemConfigChangedSection</method>
                    </mymodule>
                </observers>
            </admin_system_config_changed_section_mysection>
        </events>
    </global>
    <admin>
        <routers>
            <mymodule>
                <use>admin</use>
                <args>
                    <module>My_Module</module>
                    <frontName>myrouter</frontName>
                </args>
            </mymodule>
        </routers>
    </admin>
...
</config>

Controller in controllers/Adminhtml/TestController.php:

class My_Module_Adminhtml_TestController extends Mage_Adminhtml_Controller_Action
{   
    public function validateAction()
    {
        Mage::log('Test: action is working!'); //To check if action is working
        //do something here...
    }
}
Was it helpful?

Solution

Again, what are you trying to do? Redirecting someone from sysconfig to your controller is odd UX at best, even if you redirect back to sysconfig after your code executes. If that's all you're doing, you should be consuming the POST and letting sysconfig behave as normal.

The reason your (first) redirect isn't working is that after this event fires, execution flows back to the controller action, which then sets its own redirect per usual:

class Mage_Adminhtml_System_ConfigController extends Mage_Adminhtml_Controller_Action
{
    public function saveAction()
    {
        // snip ...
        try {
            // snip ...
            Mage::dispatchEvent("admin_system_config_changed_section_{$section}",
                array('website' => $website, 'store' => $store)
            );
            // snip ...            }
        catch (Mage_Core_Exception $e) {
            // snip ...
        }
        catch (Exception $e) {
            // snip ...
        }
            // snip ...

        $this->_redirect('*/*/edit', array('_current' => array('section', 'website', 'store')));
    }

You can get around this in a couple ways:

  1. (not ideal) Flush the response after setting the redirect in your browser; while this obeys your current intent, know that you are preventing subsequent native code from executing both in the action and in postDispatch().
  2. (better, but still pretty ugly) Instantiate your observer as a singleton, set a flag, and then set that observer class to also consume the controller_action_postdispatch_adminhtml_system_config_save event using a different method which will set your redirect.

E.g.

public function checkShouldRedirect()
{
    $response = Mage::app()->getResponse();

    //use 'mysection' to check we are in correct section
    if (self::$flag==true && $this->getRequest()->getParam('section') == 'mysection') {
        $response->setRedirect(
            Mage::helper('adminhtml')->getUrl('myrouter/adminhtml_test/validate')
        );
        //below is only necessary for option 1
        //$response->sendResponse();
    }
}

If you are going to engage in the second option - and provided that you aren't opposed to the action saving the open panel state - you might as well just set your observer to consume only the postdispatch event, since you can test that you are saving your section by testing the section param.

Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top