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