We are moving our forum in GitHub Discussions. For questions about Phalcon v3/v4 you can visit here and for Phalcon v5 here.

Solved thread

This post is marked as solved. If you think the information contained on this thread must be part of the official documentation, please contribute submitting a pull request to its repository.

Multi Module Routing default behaviour

Hi all,

At multi-module docs I can see examples of how to route them, as well in the mvc repository in github. But I can't find an example with a generic solution. All the examples I saw define specific routers for the modules. What I really want is the router to handle it, as it does with controllers/actions but with modules.

At the moment I already have a multimodule skeleton, the loader is working ok with different namespaces across modules etc, but I can't get the router to reach other modules than the default one.

This is what I have:

$di['router'] = function () {
    $router = new \Phalcon\Mvc\Router();
    $router->setDefaultNamespace('App\Base\Controllers');
    $router->setDefaultModule("base");

    $router->add('/:module/:controller/:action',
        array(
            'module' => 1,
            'controller' => 2,
            'action' => 3,
        )
    );
    return $router;
};

with the bootstrap application as follows

$application->registerModules( array(
    'base' => array(
        'className' => 'App\Base\Module',
        'path' => $config->loader->modules_path.'Base'.DS.'Module.php'
    ),
    'user' => array(
        'className' => 'App\User\Module',
        'path' => $config->loader->modules_path.'User'.DS.'Module.php'
    )
));

if I access to route / it goes to base module, IndexController, indexAction, but it does the same whatever the route I write. What I'm doing wrong?

I'm so sorry for writing this since there is a lot of routing questions related to autoloading of multi module applications but I couln't find anything.

This is what I use

$modules = [
    'frontend' => '',
    'backend' => '/admin',
];
foreach ($modules as $module => $prefix) {
    $router->add("{$prefix}(/)?", [
        'module'     => $module,
        'controller' => 'index',
        'action'     => 'index',
    ]);
    $router->add("{$prefix}/:controller(/)?", [
        'module'     => $module,
        'controller' => 1,
        'action'     => 'index',
    ]);
    $router->add("{$prefix}/:controller/:action(/)?", [
        'module'     => $module,
        'controller' => 1,
        'action'     => 2,
    ]);
    $router->add("{$prefix}/:controller/:action/:params(/)?", [
        'module'     => $module,
        'controller' => 1,
        'action'     => 2,
        'params'     => 3,
    ]);
}

The reason it's not matching is because it treats the route as a regular expression, and the :controller etc are just shorthand for a regular expression.

Hope this helps.



16.0k
Accepted
answer

Hello,

If the modules and namespaces are loaded properly, what you did should work fine; but it will only work as long your url contains module/controller/action, otherway it will load the default module. If you want to access only by module name or module/controller, you need to add other routes.

$router->add('/:module/:controller',
    array(
        'module' => 1,
        'controller' => 2,
        'action' => 'index',
    )
);
$router->add('/:module',
    array(
        'module' => 1,
        'controller' => 'index',
        'action' => 'index',
    )
);
edited Apr '15

Yeah @Stefan solved it. I did many changes and I forget to set the default behaviour when no action, controller is present. Although I needed to set those for each of my Modules, due to namespaces. It does not work the generic way:

$di['router'] = function () use ($application) {
    $router = new \Phalcon\Mvc\Router();

    foreach ($application->getModules() as $moduleName => $module) {
        $namespaceName = str_replace('Module', 'Controllers', $module['className']).'\\';
        $router->add("$moduleName/:controller/:action/:params", array(
            'namespace'  => $namespaceName,
            'module'     => $moduleName,
            'controller' => 1,
            'action'     => 2,
            'params'     => 3
        ));
        $router->add("$moduleName/:controller", array(
            'namespace'  => $namespaceName,
            'module'     => $moduleName,
            'controller' => 1,
            'action'     => 'index',
        ));
        $router->add("$moduleName", array(
            'namespace'  => $namespaceName,
            'module'     => $moduleName,
            'controller' => 'index',
            'action'     => 'index',
        ));
    }
    $router->removeExtraSlashes(true);

    return $router;
};

Why it does not work with :module wildcard?

edited Apr '15

If the namespaces don't work the generic way, that means it does not follow the PSR-0 rules.

Make sure the folder names are written exactly as the namespace, Also the file names are the same as the class name. Everything is case sensitive.

Just defining a generic route (:module/:controller/:action, :module/:controller/, :module) will work for all modules. Define new routes only if you need something custom.

They follow the psr0. All my modules are defined on the vendor\NamespaceName\Etc... ex: NamespaceName\ModuleName\Controller\IndexController.php with namespace NamespaceName\ModuleName\Controller they are loaded with composer, all of them are accesible after autoload.

But if I set the generic routes (:module/:controller/:action, :module/:controller/, :module) or just start the router with default routes they don't work. In any case they are working with my foreach getModules since they are defined for each module.

During the weekend I will try to reproduce it on a clean skeleton so I can post it here. Don't know what I'm doing wrong.

edited Apr '15

Here is the test. I'm using phalcon 2.0 branch

<?php

//These routes simulate real URIs
$testRoutes = array(
    '/',
    '/index',
    '/index/index',
    '/base',
    '/base/index/',
    '/base/index/index',
    '/admin/user'
);

$router = new \Phalcon\Mvc\Router(true);
$router->setDefaultModule('base');
$router->setDefaultController('index');
$router->setDefaultAction('index');

// FRONT END ROUTES
$router->add("/:module/:controller/:action/:params", array(
    'module'     => 1,
    'controller' => 2,
    'action'     => 3,
    'params'     => 4
));

$router->add("/:module/:controller", array(
    'module'     => 1,
    'controller' => 2,
    'action'     => 'index',
));

$router->add("/:module", array(
    'module'     => 1,
    'controller' => 'index',
    'action'     => 'index',
));

// ADMIN ROUTES
$router->add("/admin/:module/:controller/:action/:params", array(
    'module'     => 1,
    'controller' => 2,
    'action'     => 3,
    'params'     => 4,
    'admin'      => true
));

$router->add("/admin/:module/:controller", array(
    'module'     => 1,
    'controller' => 2,
    'action'     => 'index',
    'admin'      => true
));

$router->add("/admin/:module", array(
    'module'     => 1,
    'controller' => 'index',
    'action'     => 'index',
    'admin'      => true
));

// API ROUTES
$router->add("/api/:module/:controller/:action/:params", array(
    'module'     => 1,
    'controller' => 2,
    'action'     => 3,
    'params'     => 4,
    'api'        => true
));

$router->add("/api/:module/:controller", array(
    'module'     => 1,
    'controller' => 2,
    'action'     => 'index',
    'api'        => true
));

$router->add("/api/:module", array(
    'module'     => 1,
    'controller' => 'index',
    'action'     => 'index',
    'api'        => true
));

//Testing each route
foreach ($testRoutes as $testRoute) {

    //Handle the route
    $router->handle($testRoute);

    echo 'Testing ', $testRoute, "\n";

    //Check if some route was matched
    if ($router->wasMatched()) {
        echo 'Module: ', $router->getModuleName(), "\n";
        echo 'Controller: ', $router->getControllerName(), "\n";
        echo 'Action: ', $router->getActionName(), "\n";
        if ($router->getParams()) {
            echo 'Params: ';
            print_r($router->getParams());
            echo "\n";
        }

    } else {
        echo "The route wasn\\'t matched by any route\n";
    }
    echo "\n";

}

The results are as follow:

// php routerTest.php

[BAD] Testing / The route wasn\'t matched by any route

Testing /index Module: index Controller: index Action: index

Testing /index/index Module: index Controller: index Action: index

Testing /base Module: base Controller: index Action: index

[BAD] Testing /base/index/ Module: base Controller: base Action: index

Testing /base/index/index Module: base Controller: index Action: index

[BAD] Testing /admin/user Module: user Controller: index Action: index Params: Array ( [admin] => user )

I tested your routes on my application and work fine, also the results seems to be according to the routes. Nothing wrong there. Also add

$router->removeExtraSlashes(true);

since /base/index/ is taken as another route that wasn't defined and gives you that BAD result