Model
Während unsere Anwendung wächst, stellen wir bald fest, dass wir an verschiedenen Stellen und in verschiedenen Presentern
ähnliche Datenbankoperationen brauchen, zum Beispiel das Laden der zuletzt veröffentlichten Beiträge. Wenn wir unsere Anwendung
verbessern, etwa indem wir den Beiträgen ein Flag hinzufügen, das angibt, ob es sich um Entwürfe handelt, müssen wir außerdem
alle Stellen in unserer Anwendung durchsehen, an denen Beiträge aus der Datenbank geholt werden, und eine
where-Bedingung ergänzen, damit nur Beiträge ausgewählt werden, die keine Entwürfe sind.
An diesem Punkt reicht die direkte Arbeit mit der Datenbank nicht mehr aus, und es ist klüger, eine neue Methode zu verwenden, die die veröffentlichten Beiträge zurückgibt. Und wenn wir später eine weitere Bedingung ergänzen (zum Beispiel Beiträge mit einem Datum in der Zukunft nicht anzuzeigen), ändern wir unseren Code nur an einer Stelle.
Wir legen die Methode in die Klasse PostFacade und nennen sie getPublicArticles().
Unsere Modellklasse PostFacade, die sich um unsere Beiträge kümmert, erstellen wir im Verzeichnis
app/Model/:
<?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 fordern wir den Datenbank-Explorer über den Konstruktor an. Damit nutzen wir die Kraft des DI-Containers.
Wechseln wir zum HomePresenter, den wir so anpassen, dass wir die Abhängigkeit von
Nette\Database\Explorer loswerden 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 Abschnitt use steht App\Model\PostFacade, sodass wir die Schreibweise im PHP-Code auf
PostFacade verkürzen können. Wir fordern dieses Objekt im Konstruktor an, schreiben es in die Property
$facade und verwenden es in der Methode renderDefault.
Der letzte Schritt ist, dem DI-Container beizubringen, dieses Objekt zu erzeugen. Das geschieht üblicherweise, indem man in
der Datei config/services.neon im Abschnitt services einen Eintrag mit dem vollständigen Klassennamen
und den Parametern des Konstruktors ergänzt. Damit wird es registriert, und das Objekt heißt dann Service. Dank der Magie
des Autowirings müssen wir die Parameter des Konstruktors
meist nicht angeben, denn DI erkennt und übergibt sie automatisch. Es würde also genügen, nur den Klassennamen anzugeben:
...
services:
- App\Model\PostFacade
Sie müssen diese Zeile allerdings auch nicht ergänzen. Im Abschnitt search von services.neon ist
festgelegt, dass DI alle Klassen, die auf -Facade oder -Factory enden, automatisch findet, was auch für
PostFacade gilt.
Zusammenfassung
Die Klasse PostFacade fordert in ihrem Konstruktor Nette\Database\Explorer an, und da diese Klasse im
DI-Container registriert ist, erzeugt der Container diese Instanz und übergibt sie. DI erzeugt also für uns eine Instanz von
PostFacade und übergibt sie im Konstruktor an die Klasse HomePresenter, die sie angefordert hat. Es ist
wie eine Matrjoschka. :) Jeder sagt nur, was er will, und kümmert sich nicht darum, wo und wie es erzeugt wird. Um das Erzeugen
kümmert sich der DI-Container.
Hier können Sie mehr über Dependency Injection und die Konfiguration lesen.