Model

W miarę jak aplikacja rośnie, szybko odkryjemy, że w różnych miejscach, w różnych presenterach, potrzebujemy wykonywać podobne operacje na bazie danych. Na przykład pobierać najnowsze opublikowane artykuły. Kiedy ulepszymy aplikację, na przykład dodając do artykułów flagę oznaczającą, czy są w trakcie pisania, musimy przejrzeć wszystkie miejsca w aplikacji, gdzie artykuły są pobierane z bazy danych i dodać warunek where, aby wybierać tylko artykuły niebędące w trakcie pisania.

W tym momencie bezpośrednia praca z bazą danych staje się niewystarczająca i wygodniej będzie wspomóc się nową funkcją, która będzie nam zwracać opublikowane artykuły. A kiedy później dodamy kolejny warunek, na przykład że nie mają być wyświetlane artykuły z przyszłą datą, zmodyfikujemy kod tylko w jednym miejscu.

Funkcję umieścimy na przykład w klasie PostFacade i nazwiemy ją getPublicArticles().

W katalogu app/Model/ stworzymy naszą klasę modelową PostFacade, która będzie zajmować się artykułami:

<?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');
	}
}

W klasie za pomocą konstruktora poprosimy o przekazanie Nette\Database\Explorer. Wykorzystamy w ten sposób siłę kontenera DI.

Przełączymy się na HomePresenter, który zmodyfikujemy tak, że pozbędziemy się zależności od Nette\Database\Explorer i zastąpimy ją nową zależnością od naszej nowej klasy.

<?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);
	}
}

W sekcji use mamy App\Model\PostFacade, więc możemy skrócić zapis w kodzie PHP do PostFacade. O ten obiekt poprosimy w konstruktorze, zapiszemy go we właściwości $facade i użyjemy w metodzie renderDefault.

Pozostaje ostatni krok, czyli nauczenie kontenera DI tworzenia tego obiektu. Zwykle robi się to tak, że do pliku config/services.neon w sekcji services dodajemy myślnik, podajemy pełną nazwę klasy i parametry konstruktora. W ten sposób tak zwaną ją zarejestrujemy, a obiekt nazywa się wtedy usługą. Dzięki magii zwanej autowiring zazwyczaj nie musimy podawać parametrów konstruktora, ponieważ DI samo je rozpozna i przekaże. Wystarczyłoby więc podać tylko nazwę klasy:

...

services:
	- App\Model\PostFacade

Jednak nawet tej linii nie musisz dodawać. W sekcji search na początku services.neon zdefiniowano, że wszystkie klasy kończące się słowem -Facade lub -Factory DI wyszuka samo, co jest również przypadkiem PostFacade.

Podsumowanie

Klasa PostFacade w konstruktorze prosi o przekazanie Nette\Database\Explorer i ponieważ ta klasa jest zarejestrowana w kontenerze DI, kontener tworzy tę instancję i przekazuje ją. DI w ten sposób tworzy dla nas instancję PostFacade i przekazuje ją w konstruktorze klasie HomePresenter, która o nią poprosiła. Taka matrioszka. :) Wszyscy tylko mówią, czego chcą i nie interesują się tym, gdzie co i jak się tworzy. O tworzenie dba kontener DI.

Tutaj możesz przeczytać więcej o wstrzykiwaniu zależnościkonfiguracji.