Magento – getUrl(‘frontname/controller’) doesn’t work with custom router

ce-1.7.0.2customgeturlrouter

So, I've written a custom router, in as much as I've extended Mage_Core_Controller_Varien_Router_Standard and created my own match() function. This part works beautifully, mapping the differently formatted URLs I'm using for a specific section of the site to the typical frontname/controller/action pattern.

The only issue is I now cannot use getUrl() in the normal way.

For example, the frontName in the modules config.xml is lookfor, and the extension name is Customsearch. Usually, using Mage::getUrl('customsearch/action') would return me a url of lookfor/action. But, with the custom router, I simply get back customsearch/action. If I switch back to the standard router in the config.xml, voila, it works.

You might ask why I want this when my URLs are different – handily some functionality through this module will still be accessed the normal way. I've got this to work as expected (by placing my custom code between the normal controller/action finding code, and the noRoute stuff), its simply the URL lookup thats failing.

I've done some digging, and noticed that in the below function, from Mage_Core_Model_Url, the $this->_getData('route_front_name') has the wrong frontName (customsearch, rather than lookfor), but I can't spot where this is set.

public function getRouteFrontName()
    {
        if (!$this->hasData('route_front_name')) {
            $routeName = $this->getRouteName();
            $route = Mage::app()->getFrontController()->getRouterByRoute($routeName);
            $frontName = $route->getFrontNameByRoute($routeName);

            $this->setRouteFrontName($frontName);
        }
        return $this->_getData('route_front_name');
    }

Here're the relevant sections from the modules config.xml…

<frontend>
   <routers>
      <customsearch>
         <use>namespace_customsearch</use>
         <args>
            <module>Namespace_Customsearch</module>
            <frontName>lookfor</frontName>
         </args>
      </customsearch>
   </routers>
   ...
</frontend>
...
<default>
   <web>
      <routers>
         <namespace_customsearch>
            <area>frontend</area>
            <class>Namespace_Customsearch_Controller_Router</class>
         </namespace_customsearch>
      </routers>
   </web>
</default>

Best Answer

General warning — while ideally it should be supported, what you're trying to do isn't really meant to be done by a non-core Magento developer. The abstractions around routing are confusing, and possibly incomplete. It's relatively safe to introduce a new router object with a match method, but (as you've seen) expecting it to behave in predicable ways with the system is asking Magento too much. Generally speaking, if you want a special URL in Magento, you use the rewrite system (either the rewrite models, or the configuration rewrites)

But that's not any fun.

So, the particular problem you're running into is a routername/frontName mismatch. Your router configuration is named customsearch, but your frontName is named lookfor. This is a common problem when customizing system behavior, because most developers don't realize there's a difference between these two. That's because Magento core code always has them match each other. You see this this a lot in the various methods of replacing a controller action, and having the layout handles generated not match the layout handles Magento expects.

Looking at your specific problem, my guess (because it's hard to tell with the information provided) is your custom router class

`Namespace_Customsearch_Controller_Router`

in inheriting from the Mage_Core_Controller_Varien_Router_Abstract class. If you look at the getFrontNameByRoute method definition there

#File: app/code/core/Mage/Core/Controller/Varien/Router/Abstract.php
public function getFrontNameByRoute($routeName)
{
    return $routeName;
}

You can see the method is essentially a placeholder. In the standard router

#File: app/code/core/Mage/Core/Controller/Varien/Router/Standard.php
public function getFrontNameByRoute($routeName)
{
    if (isset($this->_routes[$routeName])) {
        return $this->_routes[$routeName];
    }
    return false;
}

This method checks the configuration built up in the internal _routes array, and returns the value it finds (which will be the frontName).

As I said, my guess is your router object inherits from the Abstract class, and doesn't have a custom getFrontNameByRoute method. Your router object needs a similar/identical method, or needs to inherit from the standard router to have this functionality.

Related Topic