Модел
С нарастването на приложението скоро ще открием, че на различни места, в различни презентери, трябва да извършваме подобни операции с базата данни. Например, да получаваме най-новите публикувани статии. Когато подобрим приложението, например като добавим флаг към статиите, дали са в процес на писане, трябва след това да преминем през всички места в приложението, където се извличат статии от базата данни и да добавим условие where, за да се избират само статии, които не са в процес на писане.
В този момент директната работа с базата данни става недостатъчна и ще бъде по-удобно да си помогнем с нова функция, която ще ни връща публикуваните статии. И когато по-късно добавим друго условие, например да не се показват статии с бъдеща дата, ще променим кода само на едно място.
Функцията ще поставим например в клас PostFacade
и ще я наречем
getPublicArticles()
.
В директорията app/Model/
ще създадем нашия моделен клас
PostFacade
, който ще се грижи за статиите:
<?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');
}
}
В класа с помощта на конструктора ще си поискаме да ни бъде предаден Database Explorer. Ще използваме силата на DI контейнера.
Ще преминем към HomePresenter
, който ще променим така, че да се
отървем от зависимостта от Nette\Database\Explorer
и да я заменим с нова
зависимост към нашия нов клас.
<?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);
}
}
В секцията use имаме App\Model\PostFacade
, така че можем да съкратим
записа в PHP кода до PostFacade
. Ще поискаме този обект в конструктора,
ще го запишем в свойството $facade
и ще го използваме в метода
renderDefault.
Остава последната стъпка, а именно да научим DI контейнера да
произвежда този обект. Това обикновено се прави, като във файла
config/services.neon
в секцията services
добавим точка, посочим
пълното име на класа и параметрите на конструктора. Така го
регистрираме и обектът след това се нарича сървис. Благодарение на
магията, наречена autowiring,
обикновено не е необходимо да посочваме параметрите на конструктора,
защото DI ги разпознава и предава сам. Би било достатъчно да посочим
само името на класа:
...
services:
- App\Model\PostFacade
Въпреки това, дори този ред не е необходимо да добавяте. В секцията
search
в началото на services.neon
е дефинирано, че всички класове,
завършващи с думата -Facade
или -Factory
, DI ще намери сам, което е
и случаят с PostFacade
.
Резюме
Класът PostFacade
си иска в конструктора предаването на
Nette\Database\Explorer
и тъй като този клас е регистриран в DI контейнера,
контейнерът създава този екземпляр и го предава. DI така създава за нас
екземпляр на PostFacade
и го предава в конструктора на класа HomePresenter,
който го е поискал. Като матрьошка. :) Всички просто казват какво искат и
не се интересуват къде и как се създава нещо. За създаването се грижи DI
контейнерът.
Тук можете да прочетете повече за dependency injection и конфигурацията.