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.

Same routes with different Namespaces

Hey guys! I need a possibility to use the same route pattern on 2 different Namespaces and it should use the first matching route with a existing class:

---
controller:
    pattern: '/:controller'
    config:
        namespace: 'MyCustom\Controllers'
        controller: 1
corecontroller:
    pattern: '/:controller'
    config:
        namespace: 'Core\Controllers'
        controller: 1
...

It should behave like this: Called url: www.mysite.com/login

Router checks if class "MyCustom\Controllers\Login" is existing than proceed with this class else he should use "Core\Controllers\Login"

How can I get something like this to work?

Hope my explanation is clear for you and thanks in advance for your help!



1.4k

i believe what you are trying to do is modules.

http://docs.phalconphp.com/en/latest/reference/applications.html#multi-module

and place this in your bootstrap

$router = $di->get("router");
foreach ($application->getModules() as $key => $module) {
    $router->add('/'.$key.'/:params', array(
            'module' => $key,
            'controller' => 'index',
            'action' => 'index',
            'params' => 1
    ))->setName($key);
    $router->add('/'.$key.'/:controller/:params', array(
            'module' => $key,
            'controller' => 1,
            'action' => 'index',
            'params' => 2
    ));
    $router->add('/'.$key.'/:controller/:action/:params', array(
            'module' => $key,
            'controller' => 1,
            'action' => 2,
            'params' => 3
    ));
}
edited Jan '15

No not modules.

If I would have modules I would have diffrent patterns.

With modules it would be:

---
controller:
    pattern: 'fronted/:controller'
    config:
        namespace: 'MyCustom\Controllers'
        module: 'frontend'
        controller: 1
corecontroller:
    pattern: 'backend/:controller'
    config:
        namespace: 'Core\Controllers'
        module: 'backend'
        controller: 1
...

But I have one pattern in one module but two different namespaces and not different modules :)



1.4k
edited Jan '15

controllers in 2 different namespace but same module. is not good. because dispatcher will break. dispatcher only works with one namespace at a time. so if u have 2 different namespace controllers in one module i suggest u split it. or keep it to same namespace. or either that, if its the same controller but extended, i suggest u do that instead.



44.7k

I created a Router class with this method. I add the modules in the config/routes.php file.

    public function addStdModule($moduleName)
    {
        $this->add("/$moduleName", [
            'module' => $moduleName,
            'controller' => 'index'
        ]);
        $this->add("/$moduleName/:controller", [
            'module' => $moduleName,
            'controller' => 1
        ]);
        $this->add("/$moduleName/:controller/:action", [
            'module' => $moduleName,
            'controller' => 1,
            'action' => 2,
        ]);
        $this->add("/$moduleName/:controller/:action/:params", [
            'module' => $moduleName,
            'controller' => 1,
            'action' => 2,
            'params' => 3
        ]);
    }

Then since a module cannot forward to another module my security plugin has this method:

    private function stopAndForwardModuleSafe($moduleDestination, $controller, $action, $dispatcher)
    {
        $moduleCurrent = $this->router->getModuleName();
        $moduleDefault = $this->router->getDefaultModule();

        if ($moduleCurrent == $moduleDestination) {
            $dispatcher->forward([
                'controller' => $controller,
                'action' => $action
            ]);
        } else {
            $pathArr = [];
            if ($moduleDestination != $moduleDefault) {
                $pathArr[] = $moduleDestination;
            }

            if ($controller == 'index' && $action == 'index') {
                // Do nothing to prevent ugly index/index
            } else if ($action == 'index') {
                $pathArr[] = $controller;
            } else {
                $pathArr[] = $controller;
                $pathArr[] = $action;
            }
            $path = implode('/', $pathArr);

            $this->response->redirect($path);

            // Fixed in Phalcon 2.0
            // Phalcon Issue: https://github.com/phalcon/cphalcon/issues/3073
            // Phalcon Issue: https://github.com/phalcon/cphalcon/issues/3108
            $dispatcher->getActiveController()->view->disable();

            $this->response->send();
            exit();
        }

        return false;
    }

You could use a PHP Trait or inheritance to a controller to implement the same logic but to have the controller be in the different namespace.

Why should the dispatcher have any issues with that? The dispatcher just react to the matched route :) and the matched route defines which class he should use..... If you set the loader correctly it will not be an issue

controllers in 2 different namespace but same module. is not good. because dispatcher will break. dispatcher only works with one namespace at a time. so if u have 2 different namespace controllers in one module i suggest u split it. or keep it to same namespace. or either that, if its the same controller but extended, i suggest u do that instead.



1.4k
edited Jan '15

$this->dispatcher->setNameSpace("whatever") << because of this it doesnt do well because the dispatcher wont be able to intialize the controller

But why should you do this? I define my namespace per route not global :)



1.4k

Ok bro. Here's the deal.

router will match the controller in the route right? and the router will than pass the controller name to the dispatcher to initialize it. unfortunately, right now i dont think the router passes the namespace details over to the dispatcher. so for example you have a controller in A namespace and no namespace. the dispatcher can initialize and find the controller without the namespace, but will not be able to find the controller in a namspace because you need to set it. since the router does NOT pass it.

The loader loads the controllers and knows the namespace, but the router doesnt know the namespace, because it just matches URL. not even sure you understood what i just said but w/e.



2.6k
Accepted
answer
edited Jan '15

No your are wrong, the dispatcher already knows the namespace of the matched route, if it was set for the route! I did it by my self with the "beforeDispatch" Event:

PHP:

$eventsManager->attach('dispatch:beforeDispatchLoop', function($event, \Phalcon\Mvc\Dispatcher $dispatcher) {
    /** @var \Phalcon\Mvc\Router $route */
    $route = $dispatcher->getDI()->getShared('router');
    $paths = $route->getMatchedRoute()->getPaths();
    if( is_array($paths) && is_array($paths['namespace'])) {
        foreach( $paths['namespace'] AS $nsToCheck) {
            $dispatcher->setNamespaceName($nsToCheck);
            if( class_exists($dispatcher->getControllerClass())) {
                break;
            }
        }
    }
});

My route config:

---
controller:
    pattern: '/:controller'
    config:
        namespace: 
            - 'MyCustom\Controllers'
            - 'Core\Controllers'
        controller: 1
...