Modell

Mit zunehmender Größe der Anwendung stellen wir bald fest, dass wir an verschiedenen Stellen, in verschiedenen Presentern, ähnliche Datenbankoperationen durchführen müssen. Zum Beispiel die neuesten veröffentlichten Artikel abrufen. Wenn wir die Anwendung verbessern, indem wir beispielsweise bei Artikeln ein Flag hinzufügen, ob sie sich in Bearbeitung befinden, müssen wir anschließend alle Stellen in der Anwendung durchgehen, an denen Artikel aus der Datenbank abgerufen werden, und eine where-Bedingung hinzufügen, um nur nicht in Bearbeitung befindliche Artikel auszuwählen.

In diesem Moment wird die direkte Arbeit mit der Datenbank unzureichend, und es wäre geschickter, sich mit einer neuen Funktion zu behelfen, die uns die veröffentlichten Artikel zurückgibt. Und wenn wir später eine weitere Bedingung hinzufügen, zum Beispiel dass Artikel mit zukünftigem Datum nicht angezeigt werden sollen, passen wir den Code nur an einer Stelle an.

Die Funktion platzieren wir beispielsweise in der Klasse PostFacade und nennen sie getPublicArticles().

Im Verzeichnis app/Model/ erstellen wir unsere Modellklasse PostFacade, die sich um die Artikel kümmert:

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

In der Klasse lassen wir uns über den Konstruktor den Datenbank-Explorer Nette\Database\Explorer übergeben. Wir nutzen so die Stärke des DI-Containers.

Wir wechseln zum HomePresenter, den wir so anpassen, dass wir die Abhängigkeit von Nette\Database\Explorer entfernen und durch eine neue Abhängigkeit von unserer neuen Klasse ersetzen.

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

Im use-Abschnitt haben wir App\Model\PostFacade, sodass wir die Schreibweise im PHP-Code auf PostFacade verkürzen können. Dieses Objekt fordern wir im Konstruktor an, schreiben es in die Eigenschaft $facade und verwenden es in der Methode renderDefault.

Der letzte Schritt ist, dem DI-Container beizubringen, dieses Objekt zu erstellen. Dies geschieht normalerweise, indem wir zur Datei config/services.neon im Abschnitt services einen Bindestrich hinzufügen, den vollständigen Klassennamen und die Konstruktorparameter angeben. Damit registrieren wir sie sozusagen, und das Objekt wird dann Dienst genannt. Dank des Zaubers namens Autowiring müssen wir die Konstruktorparameter meistens nicht angeben, da DI sie selbst erkennt und übergibt. Es würde also ausreichen, nur den Klassennamen anzugeben:

...

services:
	- App\Model\PostFacade

Allerdings müssen Sie nicht einmal diese Zeile hinzufügen. Im Abschnitt search am Anfang von services.neon ist definiert, dass alle Klassen, die auf -Facade oder -Factory enden, von DI selbst gefunden werden, was auch bei PostFacade der Fall ist.

Zusammenfassung

Die Klasse PostFacade fordert im Konstruktor die Übergabe von Nette\Database\Explorer an, und da diese Klasse im DI-Container registriert ist, erstellt der Container diese Instanz und übergibt sie. DI erstellt für uns auf diese Weise eine Instanz von PostFacade und übergibt sie im Konstruktor an die Klasse HomePresenter, die sie angefordert hat. Eine Art Matrjoschka. :) Jeder sagt nur, was er will, und kümmert sich nicht darum, wo und wie etwas erstellt wird. Um die Erstellung kümmert sich der DI-Container.

Hier können Sie mehr über Dependency Injection und Konfiguration lesen.