The purpose is to have sections of controllers automatically recognized/loaded byt the autoloader, and having uris and actions automatically created/recognized

File structure

Controllers has several subdirs, one for each subsection of the site. Here "Subdir" is an example.

app/controllers/Subdir/SomeController.php

view file structure, this needs special treatment in the controllers (see below). "view/subdir" is paired to the controllers/Subdir controllers, the "some" folders corresponds to the SomeController and the "show.volt" file corresponds to the SomeController::someAction().

app/view/subdir/some/show.volt

Loader, tells phalcon the correlation between folders and namespaces. Phalcon now translates everything into namespaces as a pseudo file structure.

$loader = new \Phalcon\Loader();

$loader->registerNamespaces(
    array(
        'MyApp\Controllers' => dirname(__DIR__) ."/controllers",
        'MyApp\Models'      => dirname(__DIR__) ."/models",
        'MyApp\Library'     => dirname(__DIR__) ."/library",
        'MyApp\Forms'       => dirname(__DIR__) ."/forms",
    )
);

$loader->register();

Router, each subdir route needs three versions of the route:

# router

$router = new Phalcon\Mvc\Router();
$router->removeExtraSlashes(true);
$router->setDefaultNamespace('MyApp\Controllers');

#-----------------------------------------------------------
# catch all/index routes (effectively the default route)
#-----------------------------------------------------------

/**
 * effectively the default namespace regardless of other settings
 * at least for routes
 */
$router->add(
    "(.*)",
    array(
        'namespace' => 'MyApp\Controllers',
        "controller" => "index",
        "action" => "index",
    )
);
# index with action, params
$router->add(
    "/index/:action/:params",
    array(
        "namespace" => 'MyApp\Controllers',
        "controller" => "index",
        "action" => 1,
        "params" => 2,
    )
);

# index with action
$router->add(
    "/index/:action",
    array(
        "namespace" => 'MyApp\Controllers',
        "controller" => "index",
        "action" => 1,
    )
);


#-----------------------------------------------------------
# mysite/subdir/ routes
#-----------------------------------------------------------

# controller with action, params
$router->add(
    "/subdir/:controller/:action/:params",
    array(
        "namespace" => 'MyApp\Controllers\Subdir',
        "controller" => 1,
        "action" => 2,
        "params" => 3,
    )
);
#  subdir/:controller/index
$router->add(
    "/subdir/:controller",
    array(
        "namespace" => 'MyApp\Controllers\Subdir',
        "controller" => 1,
    )
);
# subdir/index/index
$router->add(
    "/subdir",
    array(
        "namespace" => 'MyApp\Controllers\Subdir',
        "controller" => "index",
    )
);

The controller.

Note how a view is picked based on the matched route, this was the only way I could find to make this automatic. This plays together with the way controllers are automatically loaded.

<?php
#-----------------------------------------------------------
# base controller for all controllers in Subdir
#-----------------------------------------------------------
namespace MyApp\Controllers\Subdir;

use Phalcon\Mvc\Controller;

class SubdirControllerBase extends Controller
{
  public function initialize()
  {
      # trick to emulate selection of view according to class structure
      $cname = $this->router->getControllerName();
      $aname = $this->router->getActionName()?:'index';
      $this->view->pick("subdir/$cname/$aname");
  }
}

#---------------
<?php
#--------------- SomeController.php -----------------
namespace MyApp\Controllers\Subdir;

class SomeController extends SubdirControllerBase
{
  public function initialize()
  {
      parent::initialize();
  }

  public function showAction()
  {
    $x = "hello";
    $this->view->x = $x;
  }
}

#---------------

The volt template

{#--------------- show.volt -----------------#}
{{ content() }}

{% if x is defined %}
  {{ x }}
{% endif %}
{#---------------#}