Model
Wraz z rozwojem aplikacji szybko okazuje się, że musimy wykonywać podobne operacje na bazie danych w różnych miejscach, w różnych prezenterach. Na przykład, aby pobrać najnowsze opublikowane artykuły. Jeśli wzbogacimy aplikację, dodając na przykład flagę dla artykułów, aby wskazać, czy są one niepublikowane, musimy wtedy przejść przez wszystkie miejsca w aplikacji, gdzie artykuły są pobierane z bazy danych i dodać warunek where, aby wybrać tylko niepublikowane artykuły.
W tym momencie praca bezpośrednio z bazą danych staje się niewystarczająca i przydatne będzie użycie nowej funkcji do zwrócenia opublikowanych artykułów. A jeśli później dodamy kolejny warunek, np. że artykuły z przyszłą datą nie powinny być wyświetlane, zmodyfikujemy kod tylko w jednym miejscu.
Na przykład umieść funkcję w klasie PostFacade
i nazwij ją getPublicArticles()
.
W katalogu app/Model/
stworzymy naszą klasę modelową PostFacade
, która zajmie 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 będziemy mieli konstruktor przekazujący eksplorator bazy danych. Wykorzystamy moc kontenera DI.
Przechodzimy na stronę HomePresenter
, którą modyfikujemy pozbywając się zależności od
Nette\Database\Explorer
i zastępując ją zależnością od naszej nowej klasy.
<?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);
}
}
W sekcji use mamy App\Model\PostFacade
, więc możemy skrócić zapis kodu PHP do PostFacade
.
Będziemy żądać tego obiektu w konstruktorze, zapisywać go do właściwości $facade
i używać w metodzie
renderDefault.
Pozostał ostatni krok – nauczenie kontenera DI produkowania tego obiektu. Zwykle robi się to poprzez dodanie punktu
wypunktowania do pliku config/services.neon
w sekcji services
, podając pełną nazwę klasy
i parametry konstruktora. To rejestruje go, że tak powiem, a obiekt jest następnie nazywany serwisem. Dzięki magicznej
sztuczce zwanej autowiring, zazwyczaj nie musimy podawać parametrów
konstruktora, ponieważ DI sam je rozpozna i przekaże. Tak więc wystarczyłoby po prostu podać nazwę klasy:
...
services:
- App\Model\PostFacade
Nie musisz jednak dodawać również tej linii. Sekcja search
na początku services.neon
definiuje,
że wszystkie klasy kończące się na -Facade
lub -Factory
będą wyszukiwane przez DI, co dotyczy
również PostFacade
.
Streszczenie.
Klasa PostFacade
prosi konstruktora o przekazanie Nette\Database\Explorer
, a ponieważ ta klasa jest
zarejestrowana w kontenerze DI, kontener tworzy i przekazuje tę instancję. DI tworzy więc dla nas instancję
PostFacade
i przekazuje ją w konstruktorze do klasy HomePresenter, która jej zażądała. To jest matryca. :)
Każdy mówi tylko to, co chce i nie przejmuje się tym, gdzie co powstaje i jak. Kontener DI zajmuje się tworzeniem.
Możesz przeczytać więcej o wtrysku zależności i konfiguracji tutaj.