Модель
По мере роста приложения мы скоро обнаружим, что в разных местах, в разных презентерах, нам нужно выполнять похожие операции с базой данных. Например, получать последние опубликованные статьи. Если мы улучшим приложение, например, добавив к статьям флаг, указывающий, является ли она черновиком, нам придется пройти по всем местам в приложении, где статьи извлекаются из базы данных, и добавить условие 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 и конфигурации.