Model
A medida que nuestra aplicación crece, pronto descubrimos que necesitamos hacer operaciones de base de datos parecidas en
distintos lugares y en distintos presenters, por ejemplo obtener los últimos artículos publicados. Si mejoramos nuestra
aplicación, por ejemplo añadiendo a los artículos una marca que indique si son borradores, tenemos que revisar también todos
los lugares de la aplicación en los que se obtienen artículos de la base de datos y añadir una condición where
para asegurarnos de que solo se seleccionan los que no son borradores.
Llegados a este punto, trabajar directamente con la base de datos deja de bastar y será más inteligente usar un método nuevo que devuelva los artículos publicados. Y cuando más adelante añadamos otra condición (por ejemplo, no mostrar los artículos con fecha futura), editaremos nuestro código en un solo sitio.
Colocaremos el método en la clase PostFacade y lo llamaremos getPublicArticles().
Crearemos nuestra clase del modelo PostFacade en el directorio app/Model/ para que se ocupe de
nuestros 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 pedimos el Explorer de la base de datos mediante el constructor. Eso aprovecha la potencia del contenedor DI.
Pasaremos a HomePresenter, que modificaremos deshaciéndonos de la dependencia de
Nette\Database\Explorer y sustituyé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, así que en el código PHP podemos abreviar la
notación a PostFacade. Pedimos este objeto en el constructor, lo escribimos en la propiedad $facade y
lo usamos en el método renderDefault.
El último paso es enseñarle al contenedor DI a producir este objeto. Eso se hace normalmente añadiendo un elemento al
archivo config/services.neon en la sección services, indicando el nombre completo de la clase y los
parámetros del constructor. Así queda registrado y al objeto se le llama entonces servicio. Gracias a la magia del autowiring no solemos tener que indicar los parámetros del
constructor, porque DI los reconocerá y los pasará automáticamente. Así que bastaría con indicar el nombre de la clase:
...
services:
- App\Model\PostFacade
Ni siquiera hace falta, sin embargo, añadir esta línea. En la sección search de services.neon
está definido que DI encontrará automáticamente todas las clases que terminen en -Facade o -Factory,
y ese es también el caso de PostFacade.
Resumen
La clase PostFacade pide en su constructor Nette\Database\Explorer y, como esa clase está registrada
en el contenedor DI, el contenedor crea esa instancia y se la pasa. DI crea así por nosotros una instancia de
PostFacade y se la pasa en el constructor a la clase HomePresenter, que la había pedido. Es como una
muñeca matrioska. :) Cada uno se limita a decir qué quiere y no le importa dónde ni cómo se crea. De la creación se ocupa el
contenedor DI.
Aquí puede leer más sobre la inyección de dependencias y la configuración.