We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

multi-module wildcard annotation router

Ok where to start...

I want a structure like this:

/public
/app
/app/cache
/app/config
/src
/src/Vendor
/src/Vendor/Module

So I want my modules in /src and I want the annotation router to parse all my controllers in that module (and add those to the global routing table) I do NOT want to add every controller manually when I create a new controller file.

So far my bootstrap looks like this:

<?php

use Phalcon\Mvc\Router,
    Phalcon\Mvc\Application,
    Phalcon\DI\FactoryDefault,
    Phalcon\Exception;

$di = new FactoryDefault();
$di->set('router', function()
{
    $router = new \Phalcon\Mvc\Router\Annotations(false);
    $router->setDefaultModule('ServerManager');
    $router->setDefaultNamespace('Dalu\ServerManager');
    $router->addModuleResource('ServerManager','Dalu\ServerManager\Index');
    //var_dump($router);
    return $router;

});

try
{
    //Create an application
    $application = new Application();
    $application->setDI($di);

    $application->registerModules(
        array(
            'ServerManager' => array(
                'className' => 'Dalu\ServerManager\Module',
                'path' => dirname(__DIR__) . '/src/Dalu/ServerManager/Module.php'
            )
        )
    );

    //Handle the request
    echo $application->handle()->getContent();

}
catch(Exception $e)
{
    echo $e->getMessage();
}

Module.php

<?php
namespace Dalu\ServerManager;

use Phalcon\Loader,
    Phalcon\Mvc\Dispatcher,
    Phalcon\Mvc\View,
    Phalcon\Mvc\ModuleDefinitionInterface;

class Module implements ModuleDefinitionInterface
{

    /**
     * Register a specific autoloader for the module
     */
    public function registerAutoloaders()
    {
        echo "RegisterAutoloaders:" . __DIR__ . "\n";
        $loader = new Loader();

        $loader->registerNamespaces(
            array(
                'Dalu\ServerManager\Controller' => __DIR__ . 'Controller/',
                'Dalu\ServerManager\Model'      => __DIR__ . 'Model/',
            )
        );

        $loader->register();
    }

    /**
     * Register specific services for the module
     */
    public function registerServices($di)
    {
        //Registering a dispatcher
        $di->set('dispatcher', function()
        {
            $dispatcher = new Dispatcher();
            $dispatcher->setDefaultNamespace('Dalu\ServerManager\Controller');
            return $dispatcher;
        });

        //Registering the view component
        $di->set('view', function()
        {
            $view = new View();
            $view->setViewsDir('Views/');
            return $view;
        });
    }

}

Error:

Fatal error: Uncaught exception 'ReflectionException' with message 'Class Dalu\ServerManager\IndexController does not exist' in /home/darko/NetBeansProjects/phal/app/bootstrap.php:51 
Stack trace: 
#0 [internal function]: ReflectionClass->__construct('Dalu\ServerMana...') 
#1 [internal function]: Phalcon\Annotations\Reader->parse('Dalu\ServerMana...') 
#2 [internal function]: Phalcon\Annotations\Adapter->get('Dalu\ServerMana...') 
#3 [internal function]: Phalcon\Mvc\Router\Annotations->handle(NULL) 
#4 /home/darko/NetBeansProjects/phal/app/bootstrap.php(51): Phalcon\Mvc\Application->handle() 
#5 /home/darko/NetBeansProjects/phal/public/index.php(4): require('/home/darko/Net...') 
#6 {main} thrown in /home/darko/NetBeansProjects/phal/app/bootstrap.php on line 51

line 51:

    echo $application->handle()->getContent();

Can this be done and if so, how?



98.9k

The autoloader is being registered in the module definition:

$loader->registerNamespaces(
            array(
                'Dalu\ServerManager\Controller' => __DIR__ . 'Controller/',
                'Dalu\ServerManager\Model'      => __DIR__ . 'Model/',
            )
);

But the router needs to know all the routes to tell the application what module must be started, know all routes means read the annotations from controllers, to read annotations from controllers, the router needs to be able to access the controller classes.

Solution: you need to use a global autoloader instead of a per-module autoloader.



3.8k
edited Oct '14

New error message: The pattern must be string

Probably thrown in Router::add

<?php

use Phalcon\Mvc\Router,
    Phalcon\Mvc\Application,
    Phalcon\DI\FactoryDefault,
    Phalcon\Exception,
    Phalcon\Session\Adapter\Files as SessionAdapter,
    Phalcon\Loader;

$loader = new Loader();

$loader->registerNamespaces(
    array(
        'Dalu\ServerManager' => dirname(__DIR__) . '/src/Dalu/ServerManager',
        'Dalu\ServerManager\Controller' => dirname(__DIR__) . '/src/Dalu/ServerManager/Controller', // probably overkill
    )
);

$loader->register();

$di = new FactoryDefault();

$di->set('router', function()
{
    $router = new \Phalcon\Mvc\Router\Annotations(false);
    $router->addModuleResource('ServerManager','Dalu\ServerManager\Controller\Index');
    return $router;

});

try
{
    //Create an application
    $application = new Application();

    $application->registerModules(
        array(
            'ServerManager' => array(
                'className' => 'Dalu\ServerManager\Module',
                'path' => dirname(__DIR__) . '/src/Dalu/ServerManager/Module.php'
            )
        )
    );

    $application->setDI($di);

    //Handle the request
    echo $application->handle()->getContent();

}
catch(Exception $e)
{
    echo $e->getMessage();
}

Module is same as before

IndexController

<?php
namespace Dalu\ServerManager\Controller;

class IndexController extends ControllerBase
{
    /**
     * @Get("/")
     */
    public function indexAction()
    {
        echo "Hello";

    }

}


98.9k
Accepted
answer

It seems the route is being created with pattern = null, you can avoid that exception this way:

/**
 * @RoutePrefix("")
 */
class IndexController extends ControllerBase
{
    /**
     * @Get("/")
     */
    public function indexAction()
    {
        echo "Hello";
    }

}


3.8k

oh my god, thank you so much :) finally some progress. Ok do I understand this right that $router->addModuleResource is sufficient for every module, that it defines the entry point of a module?



3.8k

The answer is no Router::setDefaultModule does this and addModuleResource isn't necessary (so far)



98.9k

If the application have a single-module, addResource is enough, but addModuleResource is necessary if you have more than one module in your application.