Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Zend Framework URL based translation routes

I am trying to implement URL based translation in Zend Framework so that my site is SEO friendly. This means that I want URLs like the below in addition to the default routes.

zend.local/en/module
zend.local/en/controller
zend.local/en/module/controller
zend.local/en/controller/action

The above are the ones I have problems with right now; the rest should be OK. I have added a controller plugin that fetches a lang parameter so that I can set the locale and translation object in the preDispatch method. Here are some of my routes (stored in a .ini file):

; Language + module
; Language + controller
resources.router.routes.lang1.type = "Zend_Controller_Router_Route_Regex"
resources.router.routes.lang1.route = "(^[a-zA-Z]{2})/(\w+$)"
resources.router.routes.lang1.defaults.controller = index
resources.router.routes.lang1.defaults.action = index
resources.router.routes.lang1.map.1 = "lang"
resources.router.routes.lang1.map.2 = "module"


; Language + module + controller
; Language + controller + action
resources.router.routes.lang2.type = "Zend_Controller_Router_Route_Regex"
resources.router.routes.lang2.route = "(^[a-zA-Z]{2})/(\w+)/(\w+$)"
resources.router.routes.lang2.defaults.module = default
resources.router.routes.lang2.defaults.action = index
resources.router.routes.lang2.map.1 = "lang"
resources.router.routes.lang2.map.2 = "controller"
resources.router.routes.lang2.map.3 = "action"

As the comments indicate, several URL structures will match the same route, which makes my application interpret the format incorrectly. For instance, the following two URLs will be matched by the lang1 route:

zend.local/en/mymodule
zend.local/en/mycontroller

In the first URL, "mymodule" is used as module name, which is correct. However, in the second URL, "mycontroller" is used as module name, which is not what I want. Here I want it to use the "default" module and "mycontroller" as controller. The same applies for the previous lang2 route. So I don't know how to distinguish between if the URL is of the structure /en/module or /en/controller.

To fix this, I experimented with the code below in my controller plugin.

// Get module names as array
$dirs = Zend_Controller_Front::getInstance()->getControllerDirectory();
$modules = array_keys($dirs);

// Module variable contains a module that does not exist
if (!in_array($request->getModuleName(), $modules)) {
   // Try to use it as controller name instead
   $request->setControllerName($request->getModuleName());
   $request->setModuleName('default');
}

This works fine in the scenarios I tested, but then I would have to do something similar to make the lang2 route work (which possibly involves scanning directories to get the list of controllers). This just seems like a poor solution, so if it is possible, I would love to accomplish all of this with routes only (or simple code that is not so "hacky"). I could also make routes for every time I want /en/controller, for instance, but that is a compromise that I would rather not go with. So, if anyone knows how to solve this, or know of another approach to accomplish the same thing, I am all ears!

like image 876
ba0708 Avatar asked Aug 29 '12 13:08

ba0708


People also ask

What is routing in Zend Framework?

Routing is the act of matching a request to a given controller. Typically, routing will examine the request URI, and attempt to match the URI path segment against provided constraints. If the constraints match, a set of “matches” are returned, one of which should be the controller name to execute.

Is Zend Framework Good?

Of all the projects in the market, Zend is one of the most commonly used framework, and various medium to big corporations have trusted it to build their websites and robots applications. The framework has a lot of professional PHP packages and incorporates object-oriented coding.

Is Zend Framework free?

Zend Server has a free community version and a commercial version.

Is Zend a PHP framework?

Zend is an open source PHP framework. It is pure object-oriented and built around the MVC design pattern. Zend framework contains collection of PHP packages which can be used to develop web applications and services. Zend was started by Andi Gutmans and Zeev Suraski.


1 Answers

I've reproduced your problem here and come out with the following (not using config files though):

Router

/**
 * Initializes the router
 * @return Zend_Controller_Router_Interface
 */
protected function _initRouter() {
    $locale = Zend_Registry::get('Zend_Locale');

    $routeLang = new Zend_Controller_Router_Route(
        ':lang',
        array(
        'lang' => $locale->getLanguage()
        ),
        array('lang' => '[a-z]{2}_?([a-z]{2})?')
    );

    $frontController  = Zend_Controller_Front::getInstance();
    $router = $frontController->getRouter();

    // Instantiate default module route
    $routeDefault = new Zend_Controller_Router_Route_Module(
        array(),
        $frontController->getDispatcher(),
        $frontController->getRequest()
    );

    // Chain it with language route
    $routeLangDefault = $routeLang->chain($routeDefault);

    // Add both language route chained with default route and
    // plain language route
    $router->addRoute('default', $routeLangDefault);

    // Register plugin to handle language changes
    $frontController->registerPlugin(new Plugin_Language());

    return $router;
}

Plug-in

/**
 * Language controller plugin
 */
class Plugin_Language extends Zend_Controller_Plugin_Abstract
{
    /**
     * @var array The available languages
     */
    private $languages = array('en', 'pt');

    /**
     * Check the URI before starting the route process
     * @param Zend_Controller_Request_Abstract $request
     */
    public function routeStartup(Zend_Controller_Request_Abstract $request)
    {
        $translate = Zend_Registry::get('Zend_Translate');
        $lang = $translate->getLocale();

        // Extracts the URI (part of the URL after the project public folder)
        $uri = str_replace($request->getBaseUrl() . '/', '', $request->getRequestUri());
        $langParam = substr($uri, 0, 3);

        // Fix: Checks if the language was specified (if not, set it on the URI)
        if((isset($langParam[2]) && $langParam[2] !== '/') || !in_array(substr($langParam, 0, 2), $this->languages)) { {
            $request->setRequestUri($request->getBaseUrl() . '/' . $lang . "/" . $uri);
            $request->setParam('lang', $lang);
        }
    }
}

Basically, there's the route chain for applying the language settings within the module default route. As a fix, we ensure that the URI will contain the language if the user left it out.

You'll need to adapt it, as I'm using the Zend_Registry::get('Zend_Locale') and Zend_Registry::get('Zend_Translate'). Change it to the actual keys on your app.

As for the lang route: [a-z]{2}_?([a-z]{2})? it will allow languages like mine: pt_BR

Let me know if it worked for you.

like image 50
Keyne Viana Avatar answered Oct 16 '22 20:10

Keyne Viana