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

how this is achieved?

I want to use this structure, could you please provide some help?

controllers -> modules -> index -> indexController.php

views -> modules -> index -> index.phtml

controllers -> modules -> contact -> contactController.php

views -> modules -> contact -> contact.phtml

Thanks in advance :)

Are you using namespaces? Do you want to have more than one module or is "modules" a static folder in the structure?

edited Jun '15

modules is static folder

modules ->

index (folder) -> IndexController

other (folder) -> OtherController

lo necesito así por un tema de orden en mi proyecto

edited Jun '15
├── app
│   ├── config
│   │   └── config.php
│   ├── controllers
│   │   └── modules
│   │       ├── index
│   │       │   └── IndexController.php
│   │       └── other
│   │           └── OtherController.php
│   ├── models
│   └── views
│       └── modules
│           ├── index
│           │   └── index.phtml
│           └── other
│               └── index.phtml

Regarding views you have to change the viewsDir to point to modules $view->setDirsView('app/views/modules/');

Controllers are loaded following a PSR-0/PSR-4 structure so you have to ensure that the namespaces follow that structure.

edited Jun '15

efectivamente, cambiando la ruta de las vistas si funcionan pero tengo problemas con los controladores

protected function registerAutoloaders()
{
    $loader = new Loader();
    $loader->registerNamespaces(array(
        'Single\Controller' => '../backend/controllers/modules/index/',
        // 'Single\Controller' => '../backend/controllers/modules/other/',
        'Single\Models'      => '../backend/models/'
    ));
    $loader->register();
}
protected function registerServices()
{
    $di = new DI();
    //Registering a router
    $di->set('router', function(){
        $router = new Router();
        $router->setDefaultNamespace('Single\Controller');
        return $router;
    });
    //Registering the view component
    $di->set('view', function(){
        $view = new View();
        $view->setViewsDir('../backend/views/modules/');
        return $view;
    });
    $this->setDI($di);
}

this code work only index/

controllers/modules/index/IndexController.php

namespace Single\Controller;
class IndexController extends \Phalcon\Mvc\Controller
{
    public function indexAction()
    {
    }


34.6k
Accepted
answer
edited Jun '15

You need to use PSR-0/PSR-4 namespaces:

namespace Single\Controller\Index;

class IndexController extends \Phalcon\Mvc\Controller
{
    public function indexAction()
    {
    }
$loader = new Loader();
    $loader->registerNamespaces(array(
        'Single\Controller\Index' => '../backend/controllers/modules/index/',
        'Single\Controller\Other' => '../backend/controllers/modules/other/',
        'Single\Models'      => '../backend/models/'
    ));
    $loader->register();
}

But also register the routes to match these namespaces:

$router->add('/index', array(
    'namespace' => 'Single\Controller\Index',
    'controller => 'index',
    'action' => 'index'
);

yes work :)

I also get to this solution, there is no other faster way? Thanks for your time

Actually I manage to have this configuration for modules without problems. Tell me if you are interested since I could create a github repository to show the entire code. Each Module have it's own namespace.

├── app
│   ├── config
│   │   └── config.php
│   ├── Modules
│   │   └── moduleA
│   │          └── Controllers
│   │          │     ├── admin
│   │          │     │   └── IndexController.php
│   │          │     │   └── OtherController.php
│   │          │     ├── front 
│   │          │     │   └── IndexController.php
│   │          │     │   └── OtherController.php
 │   │         │     └── other
│   │          │          └── OtherController.php
│   │          └── Models
│   │          └── Services
│   │  
│   └── views
│       └── moduleA
│            ├── admin
│            │   └── index   // for indexController in admin
│            │         └── index.volt
│            │         └── anotheractioninIndexController.volt
│            └── front
│            │   └── index   // for indexController in front
│            │         └── index.volt
│            │         └── anotheractioninIndexController.volt
edited Jun '15

Regarding to register the routes for each namespace I use this:

//on Module.php (on each module)
    public function registerServices(DiInterface $dependencyInjector = NULL)
    {
        $namespace = 'App\Base\Controllers';
        $dependencyInjector['dispatcher']->setDefaultNamespace($namespace);
    }

This way, each time you are heading for a module, the namespace is set automatically based on the module, no need to define it on each route and you can have generic routes:

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

also, instead of defining a default namespace use the defaultModule

$router->setDefaultModule('base');

please share repository github :D

edited Jun '15

Instead of a git repo I think it could be resumed here.

So, the goal is to have this app structure:

├── app
│   ├── config
│   │   └── config.php
│   ├── Modules
│   │   └── ModuleA
│   │          └── Controllers
│   │          │     ├── admin
│   │          │     │   └── IndexController.php
│   │          │     │   └── OtherController.php
│   │          │     ├── front 
│   │          │     │   └── IndexController.php
│   │          │     │   └── OtherController.php
 │   │         │     └── other
│   │          │          └── OtherController.php
│   │          └── Models
│   │          └── Services
│   │  
│   └── views
│       └── modulea
│            ├── admin
│            │   └── index   // for indexController in admin
│            │         └── index.volt
│            │         └── anotheractioninIndexController.volt
│            └── front
│            │   └── index   // for indexController in front
│            │         └── index.volt
│            │         └── anotheractioninIndexController.volt

Actually, I set each module as a git repo and I load them trought composer, outside of the app folder. But since the place in the directory only depends in terms of autoload it does not matter too much.

I set namespaces for each module. For example, moduleA have App\ModuleA as base namespace. So:


App\ModuleA\Controllers
   |_ App\ModuleA\Controllers\IndexController
   |_ admin
              |_ App\ModuleA\Controllers\Admin\IndexController
              |_,,,,
     |_ ...
App\ModuleA\Models
App\ModuleA\Services

We just need to set a router, a generic one like:

I wanted to do this kind of dispatching:

  • /admin/modulea/list ==> App\ModuleA\Controllers\Admin\IndexController::listAction
  • /admin/modulea/1/view ==> App\ModuleA\Controllers\Admin\IndexController::viewAction(element_id)
  • /admin/modulea/1/subcontroller/2/view ==> App\ModuleA\Controllers\Admin\SubcontrollerController::viewAction(element_id, subelement_id)
  • /modulea/other/1/view ==> App\ModuleA\Controllers\OtherController::view(1)

Using this I can manage to use the routes in similar way to a rest routes (still not 100% accurate but I didn't put too much time in it)

Notice that there is no namespace defined.

$router = new Router(true);

$router->setDefaultModule('modulea');

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

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

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

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

$router->add("/:module/([a-zA-Z0-9_-]+)/:action", array(
    'module' => 1,
    'controller' => 'index',
    'element' => 2,
    'action' => 3,
));

$router->add("/:module/([a-zA-Z0-9_-]+)/:controller/:action", array(
    'module' => 1,
    'element' => 2,
    'controller' => 3,
    'action' => 4,
));

$router->add("/:module/([a-zA-Z0-9_-]+)/:controller/([a-zA-Z0-9_-]+)/:action", array(
    'module' => 1,
    'element' => 2,
    'controller' => 3,
    'action' => 5,
    'subelement' => 4,
));

$router->add("/:module/([a-zA-Z0-9_-]+)/:controller/([a-zA-Z0-9_-]+)/:action", array(
    'module' => 1,
    'element' => 2,
    'controller' => 3,
    'subelement' => 4,
    'action' => 5
));

// third param is wasted, we don't need it
$router->add("/:module/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/:controller/:action", array(
    'module' => 1,
    'element' => 2,
    'subelement' => 4,
    'controller' => 5,
    'action' => 6,
));

$router->add("/:module/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/:controller/([a-zA-Z0-9_-]+)/:action", array(
    'module' => 1,
    'element' => 2,
    'subelement' => 4,
    'controller' => 5,
    'hiperelement' => 6,
    'action' => 7,
));

// since I don't know how to use the grouped routes I'm repeating the same with each resource:
// Notice the resource param added, so we can tell the dispatcher that a subfolder is used

$router->add("/admin/:module/([a-zA-Z0-9_-]+)/:controller/:action", array(
    'module' => 1,
    'element' => 2,
    'controller' => 3,
    'action' => 4,
    'resource' => 'admin'
));

//......

I still think that the phalcon router lacks many things for this way of doing things but... it works

Modules are registered as always:


$application->registerModules(array(
    'modulea' => array(
        'namespaceName'  => 'App\ModuleA\Controllers',
        'className' => 'App\ModuleA\Module',
        'path' => $this->getDi()->getShared('config')->paths->modules_path.'ModuleA'.DS.'Module.php'
    ), // ....
);

We just need to tell the system wich namespace needs to load when inside a Module. Lets do it inside the Module.php


    public function registerServices(DiInterface $dependencyInjector = NULL)
    {
        $namespace = 'App\ModuleA\Controllers';

        $resource = null;
        if (isset($dependencyInjector['router']->getParams()['resource'])) {
            $resource = $dependencyInjector['router']->getParams()['resource'];
        }

        $namespace .= '\\'.ucfirst($resource); // adding the subdir as namespace route
        $dependencyInjector['dispatcher']->setDefaultNamespace($namespace);
    }

Now we need to load the adecuate views and layouts. This is a temporal workaround:


        $di->getShared('eventsManager')->attach("dispatch:beforeDispatch", function ($event, \Phalcon\Mvc\Dispatcher $dispatcher) use ($di) {
            $layout_dir = '..' . DS;

            $di['view']->setLayout('index');
            $layout_dir .= '';

            $path_view = '';
            if ($resource = $dispatcher->getParam('resource')) {
                $path_view = $resource . DS;
                $di['view']->setLayout($resource);
                $layout_dir .= '..' . DS;
            }

            $dir = strtolower($di['router']->getModuleName()) . DS . $path_view;
            $di['view']->setViewsDir($dir);
            $di['view']->setLayoutsDir($layout_dir . 'layouts' . DS);
        });

        $di['dispatcher']->setEventsManager($di->getShared('eventsManager'));

And it's done. Tell me what you think. (It still have room for many improvements)

P.D. Of course you can call any Model or Service, etc from any model, it is just related to the autoloading. The only thing we do is to guide the dispatcher to load the good Controller based on the module namespace and mess a little with the view paths.

P.D. Creo que tenemos las mismas manías a la hora de organizar la estructura de directorios :p