Модел

С разрастването на нашето приложение скоро ще установим, че трябва да извършваме подобни операции с базата данни на различни места и в различни презентатори. Например за извличане на най-новите публикувани статии. Ако подобрим приложението си, като добавим флаг, указващ състоянието на готовност на статиите, трябва също така да преминем през всички места в приложението си и да добавим условие 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-контейнера.

Можете да прочетете повече за разгръщането на зависимостите тук и за конфигурацията тук.