Force Magento To Always Use Single Controller For

2019-07-11 19:35发布

问题:

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.

回答1:

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)



回答2:

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.



标签: magento