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