Hello,
Firt of all this is a long post and it's something that I sould be contributing (make it myself and share) but well, maybe someone else did it, it's pending, or Im doing things the way I'm not suposed to.
Long short story, I need to use Phalcon\Cli\Console with namespaces on a nice orderly module integrated fassion.
So, here it goes:
I'm developing a multi module, multi mode (MVC/Console) Application.
I'm currently having two modules named "base" and "billing".
The base module is for the MVC Application only and the billing module is to be used by both MVC and Console Applications.
To get the two of them working "together" as you don't have a unified instance of Application that handles both things, I've implemented a builder pattern to “build” one or another as follows:
ApplicationBuilder: parent class, has three methods that implements and to be implemented as well for it's children; one to register services, another to register the loaders and the last one is an initialization method. Here is the definition of all the common modules, services and loaders required for both applications. It registers i.e. the logger, database, billing module, etc. There is another method called “build" (the only public) that returns the specialized instance of Phalcon\Application (by it’s children).
MVCBuilder: child of ApplicationBuilder, registers i.e. "view" service, Phalcon\Mvc\Router service, Phalcon\Mvc\Dispatcher, cookie support (and crypt for cookie), registers the "base" module, etc.
ConsoleBuilder: child of ApplicationBuilder, implementing the things only required by this Application, the Phalcon\Cli\Router, the Phalcon\Cli\Dispatcher, etc.
MicroBuilder: hey, if I want to add a Micro Application, I can do it by implementing this.
The idea of implementing the builder pattern here is to delegate the complex building (setting services, loaders, modules and configurations) to a model that you have all the applications together and sharing their components; i.e. if I want to change the Phalcon\Logger\Adapter service to all the applications I only need to change it on the registerServices() method of ApplicationBuilder. You get the idea…
So everything is "nice" and "mostly happy" tied together until I get the following setback:
The framework is not properly adjusted to be working with Console Applications and Namespaces.
Phalcon\Cli\Console->handle requires an array and basically it sends the arguments to Phalcon\Cli\Router->handle phalcon/cli/console.zep#L63 and then sets the dispatcher, etc.
Phalcon\Cli\Router->handle works with both arrays and strings:
- phalcon/cli/router.zep#L190 if it’s a string (that the Phalcon\Cli\Console->handle don’t permit), it matches the route as seen phalcon/cli/router.zep#L217
- If it’s and array, it doesn’t take into account the namespace: phalcon/cli/router.zep#L376
Phalcon\Cli\Router\Route->reConfigure executed by it’s constructor it does correctly support namespaces router/route.zep#L89
So if we call Phalcon\Cli\Console->handle with an array it doesn’t work with namespaces.
If we declare a specific route like “billing/:controller/:action” we can match it to i.e.[“module” => “billing”, “namespace” => “My\Billing\Module\TaskNamespace”, “controller” => 1, “action” => 2]
it will no be processed by the router as this will require to call Phalcon\Cli\Console->handle(“billing/:controller/:action”)
that we cannot do.
The only thing to do that works out of the box without getting much into trouble is to specify the namespace on the task argument i.e.
<?php
$arguments = [“module” => “billing”, “task” => “My\Billing\Module\TaskNamespace\Main”, “action” => “test”];
$console->handle($arguments); /* var $console Phalcon\Cli\Console */
I don’t like that since I would have to specify the namespace of all modules to be added in the future by hand with each action execution.
Another fix is to tell the dispatcher where is the namespace of the controller to execute (the thing that the Phalcon\Cli\Console->handle is not doing)
<?php
// ...
$builder = new ConsoleBuilder();
$app = $builder->build();
$app->getDI()->get(‘dispatcher’)->setNamespace(“My\Billing\Module\TaskNamespace”); //To be set according to which task i’m executing
$arguments = [“module” => “billing”, “task” => “main”, “action” => “test”];
$app->handle($arguments);
As for what to do to really use namespaces….
The Phalcon\Cli\Router should add namespace support by adding it to it’s default routes, adding a _defaultNamespace property, _namespace property, it’s access methods, and make it work with arrays (with strings it already works as Phalcon\Cli\Router\Route does support them properly)
The Phalcon\Cli\Console->handle:
- Should set the namespace on the dispatcher as Phalcon\Cli\Dispatcher already supports namespaces as they are implemented on it’s parent.
- Should allow arrays and strings as parameters to be consistent with Phalcon\Cli\Router.
Can yo add support for Namespaces to Console Applications?
Am I missing something?
Thanks for your time :)
EDIT: Added some styling.