Modell

Ahogy alkalmazásunk növekszik, hamarosan rájövünk, hogy hasonló adatbázisműveleteket kell végrehajtanunk különböző helyeken és különböző prezenterekben, például a legújabb megjelent cikkek megszerzéséhez. Ha úgy fejlesztjük az alkalmazásunkat, hogy a cikkekhez hozzáadunk egy flaget, amely a folyamatban lévő cikkek állapotát jelzi, akkor is végig kell mennünk az alkalmazásunk összes helyszínén, és hozzá kell adnunk egy where záradékot, hogy csak a kész cikkek legyenek kiválasztva.

Ezen a ponton az adatbázissal való közvetlen munka elégtelenné válik, és okosabb lesz egy új függvénnyel segíteni magunkat, amely visszaadja a megjelent cikkeket. Ha pedig később újabb záradékot adunk hozzá (például, hogy ne jelenítsünk meg jövőbeni dátumú cikkeket), akkor csak egy helyen szerkesztjük a kódunkat.

A függvényt a PostFacade osztályba helyezzük, és getPublicArticles() névre kereszteljük.

Létrehozzuk a PostFacade modellosztályunkat a app/Model/ könyvtárban, hogy gondoskodjon a cikkeinkről:

<?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 átadjuk az adatbázis Explorer-t. Ezzel kihasználjuk a DI konténer erejét.

Átváltunk a HomePresenter -ra, amelyet úgy szerkesztünk, hogy megszabadulunk a Nette\Database\Explorer függőségtől, és azt az új osztályunk függőségével helyettesítjük.

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

A használati szakaszban a App\Model\PostFacade címet használjuk, így a PHP-kódot lerövidíthetjük a PostFacade címre. Ezt az objektumot a konstruktorban kérjük, a $facade tulajdonságba írjuk, és a renderDefault metódusban használjuk.

Az utolsó hátralévő lépés az, hogy megtanítjuk a DI konténert, hogy ezt az objektumot előállítsa. Ez általában úgy történik, hogy a config/services.neon fájlban a services szakaszban egy bullet pointot adunk hozzá a fájlhoz, megadva a teljes osztálynevet és a konstruktor paramétereit. Ezzel úgymond regisztráljuk, és az objektumot ezután service-nek hívjuk. Az autowiring nevű varázslatnak köszönhetően általában nem kell megadnunk a konstruktor paramétereit, mert a DI felismeri és automatikusan átadja őket. Így elegendő lenne csak az osztály nevét megadni:

...

services:
	- App\Model\PostFacade

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

Összefoglaló

A PostFacade osztály egy konstruktorban kéri a Nette\Database\Explorer címet, é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. A DI így létrehoz nekünk egy PostFacade példányt, és egy konstruktorban átadja azt a HomePresenter osztálynak, amelyik ezt kérte. Afféle Matrjoska baba kód :) Minden komponens csak azt kéri, amire szüksége van, és nem érdekli őket, hogy hol és hogyan jön létre. A létrehozást a DI konténer kezeli.

Itt olvashatsz többet a függőségi injektálásról, és a konfigurációról.