The end result would be that I could go to http://www.mysite.com/frontname/something/something
I will capture everything after the frontname and use that to add hooks and set data.
This would act in a similar manner to how categories work but my frontname would be prepended to the url so that i can call different blocks in the layout related to that category.
If it would help someone else after this is all figured out you would use the _call method to handle all requests: Dynamic router name for magento controller
If there is a better way to do this im all ears.
You should create a custom router that will be used additionally to the default router. To add a new router to the config, use this XML:
<default>
<web>
<routers>
<arbitrary_name>
<area>frontend</area>
<class>Your_Module_Controller_Router_Something</class>
</arbitrary_name>
</routers>
</web>
<shorturls>
</shorturls>
</default>
The router could look like this:
class Your_Module_Controller_Router_Something extends Mage_Core_Controller_Varien_Router_Abstract
{
private static $_module = 'your_module';
private static $_realModule = 'Your_Module';
private static $_controller = 'your_controller';
private static $_controllerClass = 'Your_Module_ControllerClassName';
private static $_action = 'your_action';
/**
* @var Zend_Controller_Request_Http
*/
protected $_request;
/**
* Front controller looks for collectRoutes() although it is not defined
* in abstract router class!
*
* (non-PHPdoc)
* @see Mage_Core_Controller_Varien_Router_Standard::collectRoutes()
*/
public function collectRoutes()
{
// nothing to do here
}
/* (non-PHPdoc)
* @see Mage_Core_Controller_Varien_Router_Abstract::match()
*/
public function match(Zend_Controller_Request_Http $request)
{
$this->_request = $request;
// here you will have to implement your matching:
// - detect if the request should be matched by this router
// (i.e. check for "frontname" after base url)
// - return false if not
// - otherwise:
$this->_setRequestRoute();
$this->_dispatch();
return true;
}
/**
* @return void
*/
protected function _setRequestRoute()
{
$this->_request->setModuleName(self::$_module);
$this->_request->setControllerName(self::$_controller);
$this->_request->setActionName(self::$_action);
$this->_request->setControllerModule(self::$_realModule);
}
/**
* @return void
*/
protected function _dispatch()
{
$this->_request->setDispatched(true);
$controller = Mage::getControllerInstance(
self::$_controllerClass, $this->_request, $this->_response
);
$controller->dispatch(self::$_action);
}
}
The important method is match()
which will be called for every request and has to determine if the router is responsible for the request and if yes, dispatch it. This router would always dispatch the same controller action your_controller/your_action
.
You might also want to make some parameters available based on the URL: $this->_request->setParam(key, value)
The above code works to a point and requires a bit more understanding and explanation. Here is what I came up with after tooling around with this for the last 10 hours or so:
File:config.xml
<?xml version="1.0"?>
<config>
<modules>
<AJW_Bestselling>
<version>1.6.0.0.1</version>
</AJW_Bestselling>
</modules>
<frontend>
<routers>
<bestselling>
<use>standard</use>
<args>
<module>AJW_Bestselling</module>
<frontName>bestsellers</frontName>
</args>
</bestselling>
</routers>
<layout>
<updates>
<bestselling>
<file>bestselling.xml</file>
</bestselling>
</updates>
</layout>
</frontend>
<global>
<events>
<controller_front_init_routers>
<observers>
<bestselling>
<class>AJW_Bestselling_Controller_Router</class>
<method>initControllerRouters</method>
</bestselling>
</observers>
</controller_front_init_routers>
</events>
<models>
<bestselling>
<class>AJW_Bestselling_Model</class>
</bestselling>
</models>
<blocks>
<bestselling>
<class>AJW_Bestselling_Block</class>
</bestselling>
</blocks>
<helpers>
<bestselling>
<class>AJW_Bestselling_Helper</class>
</bestselling>
</helpers>
</global>
File:Controller/Router.php
<?php
class AJW_Bestselling_Controller_Router extends Mage_Core_Controller_Varien_Router_Abstract
{
private static $_module = 'bestsellers';
private static $_realModule = 'AJW_Bestselling';
private static $_controller = 'index';
private static $_controllerClass = 'AJW_Bestselling_Controller_Index';
private static $_action = 'view';
public function initControllerRouters($observer)
{
$front = $observer->getEvent()->getFront();
$front->addRouter('bestselling', $this);
}
public function match(Zend_Controller_Request_Http $request)
{
$this->_request = $request;
$identifier = trim($request->getPathInfo(), '/');
//If Magento Is Not Installed Reroute To Installer
if (!Mage::isInstalled()) {
Mage::app()->getFrontController()->getResponse()
->setRedirect(Mage::getUrl('install'))
->sendResponse();
exit;
}
//If we dont match our router then let another router take over
if(!substr($identifier,0,strlen('bestsellers')) == 'bestsellers'){
return false;
}
//If we do match the our router then lets add some data and dispatch our
controller
else{
$route_params = str_replace ( "bestsellers/" , "" , $identifier );
$rewrite = Mage::getModel('core/url_rewrite');
$rewrite->setStoreId(1);
$rewrite->loadByRequestPath($route_params);
$category_route = $rewrite->getIdPath();
//Check to see if the route exists before we do anything else
if(!$category_route != ""){
return false;
}//Otherwise send the parameters to the request
else{
$id = str_replace ( "category/" , "" , $category_route );
$this->_request->setParam('id',$id);
}
}
$this->_setRequestRoute();
return true;
}
protected function _setRequestRoute()
{
$this->_request->setModuleName(self::$_module)
->setControllerName(self::$_controller)
->setActionName(self::$_action)
->setControllerModule(self::$_realModule);
return true;
}
}
File:controllers/IndexController.php
class AJW_Bestselling_IndexController extends Mage_Core_Controller_Front_Action{
public function indexAction(){
echo "This is the index controller";
die();
}
public function viewAction(){
$this->loadLayout();
$this->renderLayout();
}
}
What happens is if you go to just mysite.com/bestsellers/ The router will load the index controller.
However if you go to bestsellers/some/category/url.html it will fire the viewAction and the category id will be sent to be used by the controller.
The concept behind this is the ability to display content to each of your categories based on the first portion of the url after the .com - Some ideas might be best selling, most recommended, top reviewed, category questions .. anything that would form a many to one( or many) relationship with a particular category.
Also note that you could potentially use this same module to act as a router for multiple modules by conditionally setting the params of the AJW_Bestselling_Controller_Router.