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.