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ści i konfiguracji.