Модел
С разрастването на нашето приложение скоро ще установим, че трябва да извършваме подобни операции с базата данни на различни места и в различни презентатори. Например за извличане на най-новите публикувани статии. Ако подобрим приложението си, като добавим флаг, указващ състоянието на готовност на статиите, трябва също така да преминем през всички места в приложението си и да добавим условие where, за да сме сигурни, че се избират само готови статии.
В този момент директната работа с базата данни става недостатъчна и е по-разумно да си помогнем с нова функция, която връща публикувани статии. А когато по-късно добавим друга клауза (например да не се показват статии с бъдеща дата), ще редактираме кода само на едно място.
Ще поставим функцията в класа PostFacade
и ще я наречем
getPublicArticles()
.
Нека да създадем нашия моделен клас PostFacade
в директорията
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');
}
}
В този клас предаваме базата данни Explorer. Това ще ни позволи да използваме функциите на DI-контейнера.
Нека отидем във файла HomePresenter.php
, който ще редактираме, за да се
отървем от зависимостта от Nette\Database\Explorer
, като я заменим с новата
зависимост на създадения от нас клас.
<?php
namespace App\UI\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 (не се страхувайте, той работи дори в
коментари и вашият интелигентен IDE би трябвало да може да се справи
с него).
Последната оставаща стъпка е да научим 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-контейнера.
Можете да прочетете повече за разгръщането на зависимостите тук и за конфигурацията тук.