Modell

Ahogy az alkalmazás növekszik, hamarosan rájövünk, hogy különböző helyeken, különböző presenterekben hasonló adatbázis-műveleteket kell végrehajtanunk. Például a legújabb publikált cikkek lekérése. Ha az alkalmazást például úgy fejlesztjük tovább, hogy a cikkeknél hozzáadunk egy jelzőt, hogy vázlatban van-e, akkor végig kell mennünk az alkalmazás összes olyan helyén, ahol cikkeket kérünk le az adatbázisból, és hozzá kell adnunk a where feltételt, hogy csak a nem vázlatban lévő cikkeket válasszuk ki.

Ebben a pillanatban a közvetlen adatbázis-munka elégtelenné válik, és ügyesebb lesz egy új funkcióval segíteni magunkon, amely a publikált cikkeket fogja visszaadni nekünk. És ha később hozzáadunk egy másik feltételt, például hogy ne jelenjenek meg a jövőbeli dátummal rendelkező cikkek, a kódot csak egy helyen módosítjuk.

A funkciót például a PostFacade osztályba helyezzük, és getPublicArticles()-nak nevezzük el.

Az app/Model/ könyvtárban létrehozzuk a PostFacade modell osztályunkat, amely a cikkekkel fog foglalkozni:

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

Az osztályban a konstruktor segítségével kérjük át az adatbázis Nette\Database\Explorer-t. Kihasználjuk a DI konténer erejét.

Váltsunk a HomePresenter-re, amelyet úgy módosítunk, hogy megszabadulunk a Nette\Database\Explorer függőségtől, és lecseréljük az új osztályunktól való új függőségre.

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

A use szakaszban App\Model\PostFacade van, így a PHP kódban a jelölést lerövidíthetjük PostFacade-ra. Ezt az objektumot kérjük a konstruktorban, beírjuk a $facade property-be, és használjuk a renderDefault metódusban.

Már csak az utolsó lépés van hátra, megtanítani a DI konténert, hogy ezt az objektumot előállítsa. Ez általában úgy történik, hogy a config/services.neon fájl services szakaszába hozzáadunk egy felsorolásjelet, megadjuk az osztály teljes nevét és a konstruktor paramétereit. Ezzel regisztráljuk, és az objektumot ezután szolgáltatásnak nevezzük. Az autowiring nevű varázslatnak köszönhetően általában nem kell megadnunk a konstruktor paramétereit, mert a DI felismeri és átadja őket. Így elegendő lenne csak az osztály nevét megadni:

...

services:
	- App\Model\PostFacade

Azonban ezt a sort sem kell hozzáadnia. A services.neon elején a search szakaszban definiálva van, hogy minden -Facade vagy -Factory végződésű osztályt a DI maga keres meg, ami a PostFacade esetében is így van.

Összegzés

A PostFacade osztály a konstruktorában kéri a Nette\Database\Explorer átadását, és mivel ez az osztály regisztrálva van a DI konténerben, a konténer létrehozza ezt a példányt és átadja neki. A DI így létrehozza nekünk a PostFacade példányt, és átadja a konstruktorban a HomePresenter osztálynak, amely kérte. Ilyen matrjoska. :) Mindenki csak megmondja, mit akar, és nem érdekli, hol és hogyan jön létre valami. A létrehozásról a DI konténer gondoskodik.

Itt többet olvashat a dependency injection-ről és a konfigurációról.