Modelo

A medida que la aplicación crece, pronto descubriremos que en diferentes lugares, en diferentes presentadores, necesitamos realizar operaciones similares con la base de datos. Por ejemplo, obtener los artículos publicados más recientes. Si mejoramos la aplicación, por ejemplo, agregando una marca a los artículos para indicar si están en borrador, debemos revisar todos los lugares en la aplicación donde se obtienen los artículos de la base de datos y agregar una condición where para seleccionar solo los artículos que no están en borrador.

En ese momento, el trabajo directo con la base de datos se vuelve insuficiente y será más conveniente ayudarnos con una nueva función que nos devuelva los artículos publicados. Y si luego agregamos otra condición, por ejemplo, que no se muestren artículos con fecha futura, modificaremos el código solo en un lugar.

Colocaremos la función, por ejemplo, en la clase PostFacade y la llamaremos getPublicArticles().

En el directorio app/Model/ crearemos nuestra clase de modelo PostFacade, que se encargará de los artículos:

<?php
namespace App\Model;

use Nette;

final class PostFacade
{
	public function __construct(
		private Nette\Database\Explorer $database,
	) {
	}

	public function getPublicArticles()
	{
		return $this->database
			->table('posts')
			->where('created_at < ', new \DateTime)
			->order('created_at DESC');
	}
}

En la clase, mediante el constructor, solicitaremos que se nos pase el Database Explorer. Aprovecharemos así la fuerza del contenedor DI.

Cambiamos a HomePresenter, que modificaremos eliminando la dependencia de Nette\Database\Explorer y reemplazándola por una nueva dependencia de nuestra nueva clase.

<?php
namespace App\Presentation\Home;

use App\Model\PostFacade;
use Nette;

final class HomePresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private PostFacade $facade,
	) {
	}

	public function renderDefault(): void
	{
		$this->template->posts = $this->facade
			->getPublicArticles()
			->limit(5);
	}
}

En la sección use tenemos App\Model\PostFacade, por lo que podemos acortar la notación en el código PHP a PostFacade. Solicitaremos este objeto en el constructor, lo escribiremos en la propiedad $facade y lo usaremos en el método renderDefault.

Queda el último paso, que es enseñar al contenedor DI a fabricar este objeto. Esto generalmente se hace agregando una viñeta a la sección services en el archivo config/services.neon, indicando el nombre completo de la clase y los parámetros del constructor. De esta manera, lo registramos y el objeto se llama entonces servicio. Gracias a la magia llamada autowiring, generalmente no necesitamos especificar los parámetros del constructor, porque DI los reconoce y los pasa automáticamente. Bastaría con indicar solo el nombre de la clase:

...

services:
	- App\Model\PostFacade

Sin embargo, ni siquiera necesita agregar esta línea. En la sección search al principio de services.neon se define que todas las clases que terminan con la palabra -Facade o -Factory serán encontradas automáticamente por DI, lo cual es también el caso de PostFacade.

Resumen

La clase PostFacade solicita en su constructor que se le pase Nette\Database\Explorer y, como esta clase está registrada en el contenedor DI, el contenedor crea esta instancia y se la pasa. DI crea así por nosotros la instancia de PostFacade y la pasa en el constructor a la clase HomePresenter, que la solicitó. Como una muñeca rusa. :) Todos simplemente dicen lo que quieren y no se preocupan por dónde y cómo se crea qué. El contenedor DI se encarga de la creación.

Aquí puedes leer más sobre inyección de dependencias y configuración.