Todas las clases controladoras que pertenecen a un módulo deben ser registradas en el archivo de configuración module.config.php. Si nuestra clase controladora no necesita usar servicios (no tiene dependencias) podemos registrarla de la siguiente manera:
<?php
use Zend\ServiceManager\Factory\InvokableFactory;
return [
// ...
'controllers' => [
'factories' => [
Controller\IndexController::class => InvokableFactory::class
// Put other controllers registration here
],
],
// ...
];
En la línea 7 tenemos la llave controllers que contiene la subllave factories. Para
registrar una clase controladora agregamos una línea que tenga la forma de un par: llave => valor.
La llave será el nombre completo (fully qualified name) de la clase controladora, como
\Application\Controller\IndexController
(usamos la palabra clave de PHP ::class
para
la resolución de nombre de clase), y el valor será el nombre de una clase fábrica que creará
la clase controladora que necesitamos usar. En nuestro caso usamos la InvokableFactory
estándar pero podemos crear una propia si es necesario.
Al usar la
InvokableFactory
decimos a Zend Framework que puede invocar el controlador instanciandolo con el operadornew
. Esta es la manera más simple de instaciar el controlador. Como una alternativa, podemos registrar nuestra propia fábrica para crear la instancia del controlador e inyectar las dependencias dentro del él.
Si nuestra clase controladora necesita llamar a algún servicio (lo que sucede muy a menudo), necesitamos pedirlo al administrador de servicios (discutimos sobre el administrador de servicios en el capítulo Website Operation) y luego pasarlo al constructor del controlador para que el controlador guarde el servicio que pasamos en una propiedad privada para su uso interno (a esto se llama inyección de dependencia).
Este procedimiento se implementa típicamente dentro de la clase fábrica. Por ejemplo, asumiendo
que nuestra clase controladora necesita usar el servicio CurrencyConverter
que convierte
dinero de USD a EUR. La clase fábrica de nuestro controlador tendrá el siguiente aspecto:
<?php
namespace Application\Controller\Factory;
use Zend\ServiceManager\Factory\FactoryInterface;
use Application\Service\CurrencyConverter;
use Application\Controller\IndexController;
// Factory class
class IndexControllerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container,
$requestedName, array $options = null)
{
// Get the instance of CurrencyConverter service from the service manager.
$currencyConverter = $container->get(CurrencyConverter::class);
// Create an instance of the controller and pass the dependency
// to controller's constructor.
return new IndexController($currencyConverter);
}
}
Luego registramos el controlador de la misma manera que antes pero especificando la clase fábrica que hemos escrito antes:
<?php
return [
// ...
'controllers' => [
'factories' => [
Controller\IndexController::class => Controller\Factory\IndexControllerFactory::class
],
],
// ...
];
Si tenemos alguna experiencia con Zend Framework 2 podemos notar que las cosas ahora son un poco diferentes que antes. En ZF2 había un método
getServiceLocator()
en la clase baseAbstractActionController
que permitía traer las dependencias al controlador incluso sin la clase fábrica. En ZF3 tenemos que pasar las dependencias explícitamente. Esto es un poco más aburrido pero elimina las dependencias "ocultas" y hace a nuestro código más claro y fácil de entender.
Escribir una fábrica para cada controlador puede parecer aburrido en un primer
momento. Si somos muy flojos para querer hacerlo podemos usar la clase fábrica
por defecto LazyControllerAbstractFactory
.
La fábrica
LazyControllerAbstractFactory
usa reflexión para determinar que servicios necesita usar nuestro controlador. Solo necesitamos obligar las dependencias (typehint) colocando los servicios como argumentos del constructor del controlador y la fábrica recuperará por si misma los servicios necesarios y los pasará al constructor.
Por ejemplo, para inyectar el servicio CurrencyConverter
en nuestro controlador nos aseguramos
de que el constructor se vea de esta manera:
namespace Application\Controller;
use Application\Service\CurrencyConverter;
class IndexController extends AbstractActionController
{
// Here we will save the service for internal use.
private $currencyConverter;
// Typehint the arguments of constructor to get the dependencies.
public function __construct(CurrencyConverter $currencyConverter)
{
$this->currencyConverter = $currencyConverter;
}
}
Luego registramos el controlador de la misma manera pero especificando la fábrica
LazyControllerAbstractFactory
:
<?php
use Zend\Mvc\Controller\LazyControllerAbstractFactory;
return [
// ...
'controllers' => [
'factories' => [
Controller\IndexController::class => LazyControllerAbstractFactory::class
],
],
// ...
];