Debugging Magento Controller 404 Errors

This article will describe the steps to take when debugging Magento controller 404 errors, specifically those that occur when attempting to override an existing Magento controller within a custom module.

If you are looking to override a frontend controller, let’s say AccountController.php of the Mage_Customer module, the configuration within your custom module’s config.xml might look like the following.

// app/code/local/[Vendor]/[Module]/etc/config.xml

<?xml version="1.0"?>
<config>
    <frontend>
        <routers>
            <customer>
                <args>
                    <modules>
                        <[vendor]_[module] before="Mage_Customer">[Vendor]_[Module]</[vendor]_[module]>
                    </modules>
                </args>
            </customer>
        </routers>
    </frontend>
</config>

And the controller class created to override the loginAction() method of Mage_Customer might contain the initial code below.

// app/code/local/[Vendor]/[Module]/controllers/AccountController.php

<?php
require_once Mage::getModuleDir('controllers', 'Mage_Customer') . DS . 'AccountController.php';

class [Vendor]_[Module]_AccountController extends Mage_Customer_AccountController
{
    public function loginAction()
    {
        echo "Add some code here to override loginAction()";
    }
}

This will successfully override loginAction(), but there are occasions overrides do not work, and developers are not sure if their configuration is incorrect, the controller file hasn’t been named correctly or for other reasons.

One of the first areas to check if your controller overrides are not working is by adding temporary debugging code to Magento’s core code, specifically in the file where Magento decides what controller can handle the route specified.

If you’ve read the Magento Dispatch Process post, you’ll know that within the Mage_Core_Controller_Varien_Router_Standard class, there is a match() method responsible for working out the module, controller class and action method to use to handle a route.

In the match() method, search for this line:

foreach ($modules as $realModule) {

And add some debugging code just above it.

print_r($modules); exit();
foreach ($modules as $realModule) {

This will print out the list of modules eligible to handle the route.

So for the customer/account/login route, Magento will check the frontend/routers/customer node configuration within the modules’ config.xml.

If your custom module’s config.xml has the correct router configuration, and there are no other modules that look to extend the Mage_Customer controllers, there should be two modules within the contents of the $modules array. One is the original configuration from Mage_Customer, and the other is from your custom module.

Array ( [0] => [Vendor]_[Module] [1] => Mage_Customer )

This indicates that the configuration for the override of the ‘modules’ part of the route is correct. Also take note that the custom module positioned first in the array, before Mage_Customer. This is important as within the foreach loop, your custom module will get analysed first, checking for a matching controller and action. The position in the above array is a result of the before attribute specified in config.xml.

Within the foreach method, locate the following line.

$controllerClassName = $this->_validateControllerClassName($realModule, $controller);

And add the debugging code just underneath.

$controllerClassName = $this->_validateControllerClassName($realModule, $controller);
print_r($controllerClassName); exit();

This will print out the valid controller class name that should be used for your override. In the above example, it might look like this.

[Vendor]_[Module]_AccountController

Note that as the Mage_Customer_AccountController class is being extended, the custom module’s controller file name should also be AccountController.

Swap out the $controllerClassName within print_r() with $action to determine the action method.

As the loginAction() method is the one that’s being overridden, $action should contain login.

The adminhtml router configuration has changed since the introduction of the SUPEE-6788 patch. All adminhtml routers now should route through the adminhtml module, and therefore contain /admin as the first part of the URL.

The configuration needed to override the Mage_Adminhtml_Sales_OrderController should look like the following.

// app/code/local/[Vendor]/[Module]/etc/config.xml

<?xml version="1.0"?>
<config>
    <admin>
        <routers>
            <adminhtml>
                <args>
                    <modules>
                        <[vendor]_[module] before="Mage_Adminhtml">[Vendor]_[Module]_Adminhtml</[vendor]_[module]>
                    </modules>
                </args>
            </adminhtml>
        </routers>
    </admin>
</config>
// app/code/local/[Vendor]/[Module]/controllers/Adminhtml/Sales/OrderController.php

<?php
require_once Mage::getModuleDir('controllers', 'Mage_Adminhtml') . DS .
    'Sales' . DS . 'OrderController.php';

class [Vendor]_[Module]_Sales_OrderController extends Mage_Adminhtml_Sales_OrderController
{
    public function indexAction()
    {
        echo "The code to override the indexAction()";
    }
}

Bear in mind that within config.xml, your custom module will need to specify the adminhtml node after the routers node.

Don’t forget to remove the debugging lines from the core Magento code when you are finished. If you have the correct module configuration, controller class name and controller method correctly added in your custom module, you should be able to override Magento controllers with ease.

Note: This article is based on Magento Open Source version 1.9.