Model
*****
Man mano che la nostra applicazione cresce, scopriamo presto di dover eseguire operazioni simili sul database in punti diversi e in presenter diversi, per esempio ottenere gli ultimi articoli pubblicati. Se miglioriamo l'applicazione, per esempio aggiungendo agli articoli un flag che indica se sono bozze, dobbiamo anche rivedere tutti i punti in cui gli articoli vengono presi dal database e aggiungere una condizione `where` perché vengano selezionati solo gli articoli che non sono bozze.
A questo punto lavorare direttamente con il database non basta più e sarà più intelligente usare un nuovo metodo che restituisce gli articoli pubblicati. E quando più avanti aggiungeremo un'altra condizione (per esempio non mostrare gli articoli con data futura), modificheremo il nostro codice in un solo punto.
Metteremo il metodo nella classe `PostFacade` e lo chiameremo `getPublicArticles()`.
La nostra classe di modello `PostFacade` la creeremo nella directory `app/Model/`, perché si occupi dei nostri articoli:
```php .{file:app/Model/PostFacade.php}
<?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');
}
}
```
Nella classe ci facciamo passare l'[Explorer |api:Nette\Database\Explorer] del database tramite il costruttore. Sfruttiamo così la potenza del [container DI |dependency-injection:passing-dependencies].
Passiamo a `HomePresenter`, che modificheremo eliminando la dipendenza da `Nette\Database\Explorer` e sostituendola con una nuova dipendenza dalla nostra nuova classe.
```php .{file:app/Presentation/Home/HomePresenter.php}
<?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);
}
}
```
Nella sezione `use` abbiamo `App\Model\PostFacade`, quindi nel codice PHP possiamo abbreviare la notazione in `PostFacade`. Ci facciamo passare questo oggetto nel costruttore, lo scriviamo nella proprietà `$facade` e lo usiamo nel metodo `renderDefault`.
L'ultimo passo è insegnare al container DI a produrre questo oggetto. Di solito lo si fa aggiungendo una voce al file `config/services.neon` nella sezione `services`, indicando il nome completo della classe e i parametri del costruttore. Così viene registrato e l'oggetto si chiama poi **servizio**. Grazie alla magia dell'[autowiring |dependency-injection:autowiring] di solito non dobbiamo indicare i parametri del costruttore, perché la DI li riconosce e li passa da sé. Basterebbe quindi indicare solo il nome della classe:
```neon .{file:config/services.neon}
...
services:
- App\Model\PostFacade
```
Neanche questa riga però dovete aggiungerla. Nella sezione `search` di `services.neon` è definito che tutte le classi che finiscono con `-Facade` oppure `-Factory` vengano trovate automaticamente dalla DI, ed è il caso anche di `PostFacade`.
Riepilogo
=========
La classe `PostFacade` chiede nel proprio costruttore `Nette\Database\Explorer` e, poiché questa classe è registrata nel container DI, il container crea questa istanza e la passa. La DI crea così per noi un'istanza di `PostFacade` e la passa nel costruttore alla classe `HomePresenter`, che l'ha richiesta. È come una matrioska. :) Ognuno dice solo che cosa vuole e non gli importa dove e come venga creato. Della creazione si occupa il container DI.
.[note]
Qui potete leggere di più sulla [dependency injection |dependency-injection:introduction] e sulla [configurazione |nette:configuring].
{{priority: -1}}
Model
Man mano che la nostra applicazione cresce, scopriamo presto di dover eseguire operazioni simili sul database in punti diversi
e in presenter diversi, per esempio ottenere gli ultimi articoli pubblicati. Se miglioriamo l'applicazione, per esempio
aggiungendo agli articoli un flag che indica se sono bozze, dobbiamo anche rivedere tutti i punti in cui gli articoli vengono
presi dal database e aggiungere una condizione where perché vengano selezionati solo gli articoli che non
sono bozze.
A questo punto lavorare direttamente con il database non basta più e sarà più intelligente usare un nuovo metodo che
restituisce gli articoli pubblicati. E quando più avanti aggiungeremo un'altra condizione (per esempio non mostrare gli articoli
con data futura), modificheremo il nostro codice in un solo punto.
Metteremo il metodo nella classe PostFacade e lo chiameremo getPublicArticles().
La nostra classe di modello PostFacade la creeremo nella directory app/Model/, perché si occupi dei
nostri articoli:
<?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');
}
}
Nella classe ci facciamo passare l'Explorer
del database tramite il costruttore. Sfruttiamo così la potenza del container DI.
Passiamo a HomePresenter, che modificheremo eliminando la dipendenza da Nette\Database\Explorer e
sostituendola con una nuova dipendenza dalla nostra nuova classe.
<?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);
}
}
Nella sezione use abbiamo App\Model\PostFacade, quindi nel codice PHP possiamo abbreviare la
notazione in PostFacade. Ci facciamo passare questo oggetto nel costruttore, lo scriviamo nella proprietà
$facade e lo usiamo nel metodo renderDefault.
L'ultimo passo è insegnare al container DI a produrre questo oggetto. Di solito lo si fa aggiungendo una voce al file
config/services.neon nella sezione services, indicando il nome completo della classe e i parametri del
costruttore. Così viene registrato e l'oggetto si chiama poi servizio. Grazie alla magia dell'autowiring di solito non dobbiamo indicare i parametri del
costruttore, perché la DI li riconosce e li passa da sé. Basterebbe quindi indicare solo il nome della classe:
...
services:
- App\Model\PostFacade
Neanche questa riga però dovete aggiungerla. Nella sezione search di services.neon è definito che
tutte le classi che finiscono con -Facade oppure -Factory vengano trovate automaticamente dalla DI, ed
è il caso anche di PostFacade.
Riepilogo
La classe PostFacade chiede nel proprio costruttore Nette\Database\Explorer e, poiché questa classe
è registrata nel container DI, il container crea questa istanza e la passa. La DI crea così per noi un'istanza di
PostFacade e la passa nel costruttore alla classe HomePresenter, che l'ha richiesta. È come una
matrioska. :) Ognuno dice solo che cosa vuole e non gli importa dove e come venga creato. Della creazione si occupa il
container DI.
Qui potete leggere di più sulla dependency
injection e sulla configurazione.