Rob Allen describe en su blog la forma en que él genera los controladores en Slim3, este framework asocia las rutas con un método, clase callable, o closure para resolver la llamada. En este ejemplo inyecta una closure al contenedor, esta funciona según el patrón Factory, se encarga de resolver las dependencias y devuelve una nueva clase “Controller” a cada llamada.

Por defecto configura el router utilizando un string, donde indica la clase y método a llamar. Con esta definición el framework intentará obtener una instancia de la clase usando el contenedor de dependencias. De manera que al llamar a la url www.midominion.com/list mediante GET, se instanciara la clase MyController para llamar al método listAction. En la documentación es más común el ejemplo en que definen directamente una closure para atender la llamada.

1
$app->get('/list', 'MyController:listAction');

Para que la ruta se resuelva correctamente, previamente debemos haber inyectado al contenedor las dependencias necesarias. En este caso genera primero una closure para obtener un objeto de conexión a base de datos, no entiendo bien porqué hacerlo Factory, quizás el código sea algo más sencillo y para este ejemplo suficiente.

1
2
3
4
5
6
7
8
9
10
11
// Retrieve container instance
$container = $app->getContainer();

$container['DatabaseService'] = function ($c) {
return new DatabaseService();
};

$container['MyController'] = function ($c) {
$dbService = $c->get('DatabaseService');
return new MyController($dbService);
};

Posteriormente ya vemos como asocia al container DI la closure que devuelve el controlador con la base de datos ya inyectada. Una vez invocada la ruta adecuada el sistema instanciará la clase y llamará al método según el estándar de Slim, con los parámetros $request, $response y $args.

Así quedaría la clase del “Controller”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
final class MyController
{
private $dbService;
public function __construct($dbService)
{
$this->dbService = $dbService;
}

public function listAction($request, $response, $args)
{
$dataArray = $this->dbService->fetchData();
return $response->withJson($dataArray);
}
}

La idea me parece extrapolable a otros muchos contextos, además ahora casi todos los frameworks incluyen alguna suerte de contenedor DI. Aunque el recomienda especificar un controller para una sola acción. Creo que para determinados desarrollos en que no haga falta tanta optimización, se puede generar un router dinámico, en breve colgaré un pequeño desarrollo de esto que tengo en Slim 2, a ver que sale mezclándolo con esta idea…