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