Nette Documentation Preview

syntax
Interaktive Komponenten
***********************

<div class=perex>

Komponenten sind eigenständige, wiederverwendbare Objekte, die wir in Seiten einbetten. Das können Formulare, Datagrids, Umfragen sein - kurz alles, was sich sinnvoll wiederholt verwenden lässt. Wir zeigen:

- wie man Komponenten verwendet?
- wie man sie schreibt?
- was Signale sind?

</div>

Nette hat ein eingebautes Komponentensystem. Etwas Ähnliches kennen Veteranen vielleicht aus Delphi oder ASP.NET Web Forms; React oder Vue.js bauen auf entfernt Ähnlichem auf. In der Welt der PHP-Frameworks ist das jedoch eine einzigartige Eigenschaft.

Zugleich beeinflussen Komponenten die Herangehensweise an die Anwendungsentwicklung grundlegend. Sie können Seiten aus vorbereiteten Einheiten zusammensetzen. Brauchen Sie in Ihrer Administration ein Datagrid? Finden Sie es auf [Componette |https://componette.org/search/component], einem Repository von Open-Source-Erweiterungen (nicht nur Komponenten) für Nette, und fügen Sie es einfach in den Presenter ein.

Sie können beliebig viele Komponenten in den Presenter einbinden. Und in manche Komponenten können Sie weitere Komponenten einbetten. So entsteht ein Komponentenbaum, dessen Wurzel der Presenter ist.


Factory-Methoden
================

Wie werden Komponenten in den Presenter eingefügt und anschließend verwendet? Üblicherweise über Factory-Methoden.

Eine Factory für Komponenten ist ein eleganter Weg, Komponenten erst dann zu erzeugen, wenn sie tatsächlich gebraucht werden (lazy / on demand). Die ganze Magie steckt in der Implementierung einer Methode namens `createComponent<Name>()`, wobei `<Name>` der Name der erzeugten Komponente ist und die die Komponente erzeugt und zurückgibt.

```php .{file:DefaultPresenter.php}
class DefaultPresenter extends Nette\Application\UI\Presenter
{
	protected function createComponentPoll(): PollControl
	{
		$poll = new PollControl;
		$poll->items = $this->items;
		return $poll;
	}
}
```

Weil alle Komponenten in eigenen Methoden erzeugt werden, wird der Code übersichtlicher.

.[note]
Namen von Komponenten beginnen immer mit einem Kleinbuchstaben, obwohl sie im Methodennamen großgeschrieben werden.

Wir rufen Factories nie direkt auf; sie werden automatisch beim ersten Verwenden der Komponente aufgerufen. Dadurch wird die Komponente im richtigen Moment erzeugt und nur dann, wenn sie tatsächlich gebraucht wird. Verwenden wir die Komponente nicht (z. B. bei einem AJAX-Request, bei dem nur ein Teil der Seite übertragen wird, oder beim Cachen des Templates), wird sie überhaupt nicht erzeugt, was Serverleistung spart.

```php .{file:DefaultPresenter.php}
// wir greifen auf die Komponente zu, und wenn es das erste Mal war,
// wird createComponentPoll() aufgerufen, die sie erzeugt
$poll = $this->getComponent('poll');
// alternative Schreibweise: $poll = $this['poll'];
```

Im Template lässt sich eine Komponente mit dem Tag [{control} |#Rendering] rendern. Komponenten müssen daher nicht von Hand an das Template übergeben werden.

```latte
<h2>Bitte stimmen Sie ab</h2>

{control poll}
```

.[tip]
Um dynamisch eine variable Anzahl von Komponenten zu erzeugen, verwenden Sie den [Multiplier |multiplier].

Die Factory-Methoden `createComponent<Name>()` funktionieren nicht nur in Presentern. Auf dieselbe Weise können Sie eine Komponente in eine andere Komponente verschachteln und sie zu einem Baum zusammensetzen - praktisch zum Beispiel für ein separat gerendertes Formular innerhalb einer Komponente.


Hollywood Style
===============

Komponenten nutzen gewöhnlich eine erfrischende Technik, die wir gerne Hollywood Style nennen. Sie kennen sicher das Klischee, das Teilnehmer von Filmcastings oft hören: "Rufen Sie uns nicht an, wir rufen Sie an." Und genau darum geht es.

In Nette müssen Sie nicht ständig Fragen stellen ("wurde das Formular abgeschickt?", "war es gültig?", oder "hat der Benutzer diese Schaltfläche gedrückt?"), sondern Sie sagen dem Framework "wenn das passiert, rufe diese Methode auf" und überlassen ihm die weitere Arbeit. Wenn Sie in JavaScript programmieren, ist Ihnen dieser Programmierstil bestens vertraut. Sie schreiben Funktionen, die aufgerufen werden, wenn ein bestimmtes Ereignis eintritt. Und die Sprache übergibt ihnen die passenden Parameter.

Das ändert die Sicht auf das Schreiben von Anwendungen vollständig. Je mehr Aufgaben Sie dem Framework überlassen können, desto weniger Arbeit haben Sie. Und desto weniger können Sie übersehen.


Eine Komponente schreiben
=========================

Mit dem Begriff Komponente meinen wir üblicherweise einen Nachfahren der Klasse [api:Nette\Application\UI\Control]. (Genauer wäre der Begriff "Controls", aber der hat in manchen Sprachen eine andere Bedeutung, und "Komponenten" hat sich stärker eingebürgert.) Auch der Presenter [api:Nette\Application\UI\Presenter] selbst ist ein Nachfahre der Klasse `Control`.

```php .{file:PollControl.php}
use Nette\Application\UI\Control;

class PollControl extends Control
{
}
```


Rendering
=========

Wir wissen bereits, dass zum Rendern einer Komponente der Tag `{control componentName}` dient. Er ruft in Wirklichkeit die Methode `render()` der Komponente auf, in der wir uns um das Rendering kümmern. Wir haben, genau wie im Presenter, in der Variablen `$this->template` ein [Latte-Template|templates] zur Verfügung, dem wir Parameter übergeben. Anders als im Presenter müssen wir die Template-Datei angeben und sie rendern lassen:

```php .{file:PollControl.php}
public function render(): void
{
	// einige Parameter in das Template einfügen
	$this->template->param = $value;
	// und es rendern
	$this->template->render(__DIR__ . '/poll.latte');
}
```

Der Tag `{control}` erlaubt es, der Methode `render()` Parameter zu übergeben:

```latte
{control poll $id, $message}
```

```php .{file:PollControl.php}
public function render(int $id, string $message): void
{
	// ...
}
```

Manchmal kann eine Komponente aus mehreren Teilen bestehen, die wir getrennt rendern wollen. Für jeden davon legen wir eine eigene Rendering-Methode an, im Beispiel hier `renderPaginator()`:

```php .{file:PollControl.php}
public function renderPaginator(): void
{
	// ...
}
```

Und im Template rufen wir sie dann so auf:

```latte
{control poll:paginator}
```

Zum besseren Verständnis ist es gut zu wissen, wie sich dieser Tag in PHP-Code übersetzt.

```latte
{control poll}
{control poll:paginator 123, 'hello'}
```

übersetzt sich zu:

```php
$control->getComponent('poll')->render();
$control->getComponent('poll')->renderPaginator(123, 'hello');
```

Die Methode `getComponent()` gibt die Komponente `poll` zurück, und auf dieser Komponente wird die Methode `render()` aufgerufen, bzw. `renderPaginator()`, wenn im Tag nach dem Doppelpunkt eine andere Rendering-Methode angegeben ist.

.[caution]
Achtung: Erscheint in den Parametern außerhalb eckiger Klammern ein **`=>`**, werden alle Parameter in ein Array verpackt und als erstes Argument übergeben:

```latte
{control poll, id: 123, message: 'hello'}
```

übersetzt sich zu:

```php
$control->getComponent('poll')->render(['id' => 123, 'message' => 'hello']);
```

Rendering einer Unterkomponente:

```latte
{control cartControl-someForm}
```

übersetzt sich zu:

```php
$control->getComponent("cartControl-someForm")->render();
```

Komponenten übergeben Templates, ebenso wie Presenter, automatisch mehrere nützliche Variablen:

- `$basePath` ist der absolute URL-Pfad zum Wurzelverzeichnis (z. B. `/eshop`)
- `$baseUrl` ist die absolute URL zum Wurzelverzeichnis (z. B. `http://localhost/eshop`)
- `$user` ist ein Objekt, das [den Benutzer repräsentiert |security:authentication]
- `$presenter` ist der aktuelle Presenter
- `$control` ist die aktuelle Komponente
- `$flashes` ist ein Array von [Meldungen |#Flash-Meldungen], die mit der Funktion `flashMessage()` gesendet wurden


Signal
======

Wir wissen bereits, dass die Navigation in einer Nette-Anwendung im Verlinken oder Weiterleiten auf Paare `Presenter:action` besteht. Was aber, wenn wir nur eine Aktion auf der **aktuellen Seite** ausführen wollen? Zum Beispiel die Sortierung von Spalten einer Tabelle ändern; ein Element löschen; zwischen hellem und dunklem Modus umschalten; ein Formular absenden; in einer Umfrage abstimmen usw.

Diese Art von Request nennt man Signal. Und so, wie Aktionen die Methoden `action<Aktion>()` oder `render<Aktion>()` aufrufen, rufen Signale die Methoden `handle<Signal>()` auf. Während sich der Begriff Aktion (oder View) rein auf Presenter bezieht, betreffen Signale alle Komponenten. Und damit auch Presenter, denn `UI\Presenter` ist ein Nachfahre von `UI\Control`.

```php
public function handleClick(int $x, int $y): void
{
	// ... Verarbeitung des Signals ...
}
```

Einen Link, der ein Signal aufruft, erstellt man auf die übliche Weise, also im Template mit dem Attribut `n:href` oder dem Tag `{link}`, im Code mit der Methode `link()`. Mehr im Kapitel [Erstellen von URL-Links |creating-links#Links auf Signale].

```latte
<a n:href="click! $x, $y">hier klicken</a>
```

Ein Signal wird immer auf dem aktuellen Presenter und der aktuellen Aktion aufgerufen; es lässt sich nicht auf einem anderen Presenter oder einer anderen Aktion auslösen.

Ein Signal bewirkt also, dass die Seite genau wie beim ursprünglichen Request neu geladen wird, ruft zusätzlich aber die Methode zur Verarbeitung des Signals mit den passenden Parametern auf. Existiert die Methode nicht, wird eine Exception [api:Nette\Application\UI\BadSignalException] geworfen, die dem Benutzer als Fehlerseite 403 Forbidden angezeigt wird.


Snippets und AJAX
=================

Signale erinnern Sie vielleicht ein wenig an AJAX: Handler, die auf der aktuellen Seite aufgerufen werden. Und Sie haben recht, Signale werden tatsächlich oft mit AJAX aufgerufen, und anschließend werden nur die geänderten Teile der Seite in den Browser übertragen. Diese nennt man Snippets. Mehr dazu finden Sie auf der [Seite über AJAX |ajax].


Flash-Meldungen
===============

Eine Komponente hat einen eigenen, vom Presenter unabhängigen Speicher für Flash-Meldungen. Das sind Meldungen, die zum Beispiel über das Ergebnis einer Operation informieren. Eine wichtige Eigenschaft von Flash-Meldungen ist, dass sie im Template auch nach einer Weiterleitung verfügbar sind. Auch nach der Anzeige bleiben sie noch 30 Sekunden aktiv - zum Beispiel für den Fall, dass der Benutzer die Seite wegen eines Übertragungsfehlers neu lädt, verschwindet die Meldung nicht sofort.

Das Senden übernimmt die Methode [flashMessage |api:Nette\Application\UI\Control::flashMessage()]. Der erste Parameter ist der Text der Meldung (`string`, `Stringable`) oder ein Objekt `stdClass`, das die Meldung repräsentiert. Der optionale zweite Parameter ist ihr Typ (error, warning, info usw.). Die Methode `flashMessage()` gibt eine Instanz der Flash-Meldung als Objekt `stdClass` zurück, dem sich weitere Informationen hinzufügen lassen.

```php
$this->flashMessage('Das Element wurde gelöscht.');
$this->redirect(/* ... */); // und weiterleiten
```

Diese Meldungen stehen dem Template in der Variablen `$flashes` als Objekte `stdClass` zur Verfügung, die die Properties `message` (Text der Meldung) und `type` (Typ der Meldung) enthalten und die erwähnten eigenen Informationen enthalten können. Wir rendern sie zum Beispiel so:

```latte
{foreach $flashes as $flash}
	<div class="flash {$flash->type}">{$flash->message}</div>
{/foreach}
```


Weiterleitung nach Verarbeitung eines Signals
=============================================

Auf die Verarbeitung eines Signals einer Komponente folgt oft eine Weiterleitung. Das ist ähnlich wie bei Formularen - nach dem Absenden leiten wir ebenfalls weiter, damit die Daten beim Neuladen der Seite im Browser nicht erneut abgeschickt werden.

```php
$this->redirect('this'); // leitet auf den aktuellen Presenter und die aktuelle Aktion weiter
```

Da eine Komponente ein wiederverwendbares Element ist und üblicherweise keine direkte Bindung an konkrete Presenter haben sollte, interpretieren die Methoden `redirect()` und `link()` den Parameter automatisch als Signal der Komponente:

```php
$this->redirect('click'); // leitet auf das Signal 'click' derselben Komponente weiter
```

Müssen Sie auf einen anderen Presenter oder eine andere Aktion weiterleiten, geht das über den Presenter:

```php
$this->getPresenter()->redirect('Product:show'); // leitet auf einen anderen Presenter/eine andere Aktion weiter
```


Persistente Parameter
=====================

Persistente Parameter dienen dazu, den Zustand von Komponenten über verschiedene Requests hinweg zu erhalten. Ihr Wert bleibt auch nach dem Klick auf einen Link derselbe. Anders als Daten in der Session werden sie in der URL übertragen. Und das geschieht vollautomatisch, einschließlich der Links, die in anderen Komponenten auf derselben Seite erstellt werden.

Sie haben zum Beispiel eine Komponente zur Paginierung von Inhalten. Auf einer Seite können mehrere solcher Komponenten sein. Und wir wollen, dass alle Komponenten nach dem Klick auf einen Link auf ihrer aktuellen Seite bleiben. Deshalb machen wir die Seitennummer (`page`) zu einem persistenten Parameter.

Einen persistenten Parameter in Nette zu erstellen ist ausgesprochen einfach. Legen Sie einfach eine public Property an und kennzeichnen Sie sie mit dem Attribut: (früher wurde `/** @persistent */` verwendet)

```php
use Nette\Application\Attributes\Persistent;  // diese Zeile ist wichtig

class PaginatingControl extends Control
{
	#[Persistent]
	public int $page = 1; // muss public sein
}
```

Wir empfehlen, für die Property den Datentyp anzugeben (z. B. `int`), und Sie können auch einen Standardwert angeben. Die Werte der Parameter lassen sich [validieren |#Validierung persistenter Parameter].

Beim Erstellen eines Links lässt sich der Wert eines persistenten Parameters ändern:

```latte
<a n:href="this page: $page + 1">weiter</a>
```

Oder er lässt sich *zurücksetzen*, also aus der URL entfernen. Er nimmt dann seinen Standardwert an:

```latte
<a n:href="this page: null">zurücksetzen</a>
```


Persistente Komponenten
=======================

Nicht nur Parameter, sondern auch Komponenten können persistent sein. Ihre persistenten Parameter werden dann auch zwischen verschiedenen Aktionen des Presenters oder zwischen mehreren Presentern übertragen. Persistente Komponenten kennzeichnen wir mit einem Attribut an der Klasse des Presenters. So kennzeichnen wir zum Beispiel die Komponenten `calendar` und `poll`:

```php
use Nette\Application\Attributes\Persistent;

#[Persistent('calendar', 'poll')]
class DefaultPresenter extends Nette\Application\UI\Presenter
{
}
```

Unterkomponenten innerhalb dieser Komponenten müssen nicht gekennzeichnet werden; auch sie werden persistent.

Die ältere Annotation `@persistent` funktioniert weiterhin, ist aber veraltet und löst eine Warnung aus:

```php
/**
 * @persistent(calendar, poll)
 */
class DefaultPresenter extends Nette\Application\UI\Presenter
{
}
```


Komponenten mit Abhängigkeiten
==============================

Wie erstellt man Komponenten mit Abhängigkeiten, ohne die Presenter, die sie verwenden, zu "verstopfen"? Dank der klugen Fähigkeiten des DI-Containers in Nette lässt sich, ähnlich wie bei klassischen Services, der Großteil der Arbeit dem Framework überlassen.

Nehmen wir als Beispiel eine Komponente, die eine Abhängigkeit vom Service `PollFacade` hat:

```php
class PollControl extends Control
{
	public function __construct(
		private int $id, // ID der Umfrage, für die wir die Komponente erstellen
		private PollFacade $facade,
	) {
	}

	public function handleVote(int $voteId): void
	{
		$this->facade->vote($this->id, $voteId);
		// ...
	}
}
```

Würden wir einen klassischen Service schreiben, gäbe es nichts zu besprechen. Der DI-Container würde die Übergabe aller Abhängigkeiten unsichtbar erledigen. Bei Komponenten lösen wir das jedoch üblicherweise so, dass wir in den [#Factory-Methoden] `createComponent…()` direkt im Presenter eine neue Instanz erzeugen. Aber alle Abhängigkeiten aller Komponenten in den Presenter zu übergeben, nur um sie an die Komponenten weiterzureichen, ist umständlich. Und die Menge des geschriebenen Codes …

Die logische Frage lautet: Warum registrieren wir die Komponente nicht einfach als klassischen Service, übergeben sie dem Presenter und geben sie dann in der Methode `createComponent…()` zurück? Dieser Ansatz ist jedoch ungeeignet, weil wir die Möglichkeit haben wollen, die Komponente bei Bedarf mehrfach zu erzeugen.

Die richtige Lösung ist, für die Komponente eine Factory zu schreiben, also eine Klasse, die die Komponente für uns erzeugt:

```php
class PollControlFactory
{
	public function __construct(
		private PollFacade $facade,
	) {
	}

	public function create(int $id): PollControl
	{
		return new PollControl($id, $this->facade);
	}
}
```

Diese Factory registrieren wir in der Konfiguration in unserem Container:

```neon
services:
	- PollControlFactory
```

und schließlich verwenden wir sie in unserem Presenter:

```php
class PollPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private PollControlFactory $pollControlFactory,
	) {
	}

	protected function createComponentPollControl(): PollControl
	{
		$pollId = 1; // wir können unseren Parameter übergeben
		return $this->pollControlFactory->create($pollId);
	}
}
```

Das Großartige daran ist, dass Nette DI solche einfachen Factories [generieren |dependency-injection:factory] kann, sodass Sie statt des gesamten Codes nur ihr Interface schreiben müssen:

```php
interface PollControlFactory
{
	public function create(int $id): PollControl;
}
```

Und das ist alles. Nette implementiert dieses Interface intern und injiziert es in den Presenter, wo wir es verwenden können. Es ergänzt unsere Komponente auf magische Weise um den Parameter `$id` und eine Instanz der Klasse `PollFacade`.


Komponenten im Detail
=====================

Komponenten stellen in Nette Application wiederverwendbare Teile einer Webanwendung dar, die wir in Seiten einbetten und denen dieses ganze Kapitel gewidmet ist. Was genau kann eine solche Komponente?

1) Sie ist in einem Template renderbar
2) Sie weiß, [welchen Teil von sich |ajax#Snippets] sie bei einem AJAX-Request rendern soll (Snippets)
3) Sie kann ihren Zustand in der URL speichern (persistente Parameter)
4) Sie kann auf Benutzeraktionen reagieren (Signale)
5) Sie bildet eine hierarchische Struktur (deren Wurzel der Presenter ist)

Jede dieser Funktionen übernimmt eine der Klassen in der Vererbungslinie. Um das Rendering (1 + 2) kümmert sich [api:Nette\Application\UI\Control], um die Einbindung in den [Lebenszyklus |presenters#Lebenszyklus des Presenters] (3, 4) die Klasse [api:Nette\Application\UI\Component], und um den Aufbau der hierarchischen Struktur (5) die Klassen [Container und Component |component-model:].

```
Nette\ComponentModel\Component  { IComponent }
|
+- Nette\ComponentModel\Container  { IContainer }
	|
	+- Nette\Application\UI\Component  { SignalReceiver, StatePersistent }
		|
		+- Nette\Application\UI\Control  { Renderable }
			|
			+- Nette\Application\UI\Presenter  { IPresenter }
```


Lebenszyklus einer Komponente
-----------------------------

[* lifecycle-component.svg *] *** *Lebenszyklus einer Komponente* .<>


Validierung persistenter Parameter
----------------------------------

Die aus der URL empfangenen Werte der [#Persistente Parameter] werden von der Methode `loadState()` in die Properties geschrieben. Sie prüft außerdem, ob der für die Property angegebene Datentyp passt; andernfalls antwortet sie mit dem Fehler 404 und die Seite wird nicht angezeigt.

Vertrauen Sie persistenten Parametern niemals blind, denn sie lassen sich vom Benutzer in der URL leicht überschreiben. So prüfen wir zum Beispiel, ob die Seitennummer `$this->page` größer als 0 ist. Ein geeigneter Weg ist, die erwähnte Methode `loadState()` zu überschreiben:

```php
class PaginatingControl extends Control
{
	#[Persistent]
	public int $page = 1;

	public function loadState(array $params): void
	{
		parent::loadState($params); // hier wird $this->page gesetzt
		// es folgt die eigene Wertprüfung:
		if ($this->page < 1) {
			$this->error();
		}
	}
}
```

Den umgekehrten Vorgang, also das Einsammeln der Werte aus den persistenten Properties, übernimmt die Methode `saveState()`.


Verbindung mit dem Presenter
----------------------------

In dem Moment, in dem eine Komponente Teil der Hierarchie des Presenters wird, werden ihre im Array `$onAnchor` gespeicherten Callbacks aufgerufen. Ab diesem Zeitpunkt hat die Komponente den Presenter zur Verfügung, kann gefahrlos Links erstellen, persistente Parameter lesen und so weiter.

```php
$control->onAnchor[] = function ($control): void {
	// die Komponente hat nun den Presenter zur Verfügung
};
```


Signale im Detail
-----------------

Ein Signal bewirkt, dass die Seite genau wie beim ursprünglichen Request neu geladen wird (außer beim Aufruf über AJAX), und ruft die Methode `signalReceived($signal)` auf, deren Standardimplementierung in der Klasse `Nette\Application\UI\Component` versucht, eine aus den Wörtern `handle<Signal>` zusammengesetzte Methode aufzurufen. Die weitere Verarbeitung liegt beim jeweiligen Objekt. Objekte, die von `Component` erben (also `Control` und `Presenter`), reagieren so, dass sie versuchen, die Methode `handle<Signal>` mit den passenden Parametern aufzurufen.

Mit anderen Worten: Es wird die Definition der Funktion `handle<Signal>` genommen, samt allen Parametern, die mit dem Request kamen, und die Parameter aus der URL werden den Argumenten anhand des Namens zugeordnet, dann wird versucht, die Methode aufzurufen. Zum Beispiel wird der Wert des Parameters `id` aus der URL als Argument `$id` übergeben, `something` aus der URL als `$something` usw. Und existiert die Methode nicht, wirft die Methode `signalReceived` eine [Exception |api:Nette\Application\UI\BadSignalException].

Neben den Parametern aus der URL liest ein Signal auch die im **POST-Body des Requests** gesendeten Parameter. Das ist praktisch, weil Signale oft über JavaScript aufgerufen werden, wo es natürlich ist, Daten mit der Methode POST zu senden. Kommt ein Parameter gleichen Namens jedoch sowohl aus der URL als auch aus dem POST-Body, hat der Wert **aus der URL Vorrang**. Vermeiden Sie es daher, einem POST-Feld denselben Namen wie einem URL- oder Route-Parameter zu geben, sonst würde der Wert aus der URL ihn stillschweigend überschreiben. Die Signalparameter teilen sich einen gemeinsamen Raum mit den Parametern der Aktion und den persistenten Parametern, siehe [Gemeinsamer Parameterraum |presenters#Gemeinsamer Parameterraum].

Ein Signal kann jede Komponente, jeder Presenter oder jedes Objekt empfangen, das das Interface `SignalReceiver` implementiert und mit dem Komponentenbaum verbunden ist.

Die Hauptempfänger von Signalen werden `Presenter` und visuelle Komponenten sein, die von `Control` erben. Ein Signal soll einem Objekt als Zeichen dienen, dass es etwas tun soll - eine Umfrage soll eine Stimme des Benutzers zählen, ein Nachrichtenblock soll sich ausklappen und doppelt so viele Nachrichten anzeigen, ein Formular wurde abgeschickt und soll Daten verarbeiten und so weiter.

Die URL für ein Signal wird mit der Methode [Component::link() |api:Nette\Application\UI\Component::link()] erstellt. Als Parameter `$destination` übergeben wir den String `{signal}!` und als `$args` ein Array von Argumenten, die wir dem Signal übergeben wollen. Das Signal wird immer auf dem aktuellen Presenter und der aktuellen Aktion mit den aktuellen Parametern aufgerufen; die Parameter des Signals kommen lediglich hinzu. Zusätzlich wird der **Parameter `?do`, der das Signal angibt**, ergänzt.

Sein Format lautet entweder `{signal}` oder `{signalReceiver}-{signal}`. `{signalReceiver}` ist der Name der Komponente im Presenter. Deshalb darf im Namen der Komponente kein Bindestrich vorkommen - er dient der Trennung von Komponentenname und Signal, wobei sich auf diese Weise mehrere Komponenten verschachteln lassen.

Die Methode [isSignalReceiver()|api:Nette\Application\UI\Presenter::isSignalReceiver()] prüft, ob die Komponente (erstes Argument) Empfänger des Signals (zweites Argument) ist. Das zweite Argument kann weggelassen werden - dann wird geprüft, ob die Komponente Empfänger irgendeines Signals ist. Wird der zweite Parameter auf `true` gesetzt, wird geprüft, ob die angegebene Komponente oder einer ihrer Nachfahren Empfänger ist.

In jeder Phase vor `handle<Signal>` können wir das Signal manuell ausführen, indem wir die Methode [processSignal()|api:Nette\Application\UI\Presenter::processSignal()] aufrufen, die sich um die Verarbeitung des Signals kümmert - sie nimmt die als Signalempfänger bestimmte Komponente (ist kein Empfänger angegeben, ist es der Presenter selbst) und sendet ihr das Signal.

Beispiel:

```php
if ($this->isSignalReceiver($this, 'paging') || $this->isSignalReceiver($this, 'sorting')) {
	$this->processSignal();
}
```

Dadurch wird das Signal vorzeitig ausgeführt und nicht erneut aufgerufen.

Interaktive Komponenten

Komponenten sind eigenständige, wiederverwendbare Objekte, die wir in Seiten einbetten. Das können Formulare, Datagrids, Umfragen sein – kurz alles, was sich sinnvoll wiederholt verwenden lässt. Wir zeigen:

  • wie man Komponenten verwendet?
  • wie man sie schreibt?
  • was Signale sind?

Nette hat ein eingebautes Komponentensystem. Etwas Ähnliches kennen Veteranen vielleicht aus Delphi oder ASP.NET Web Forms; React oder Vue.js bauen auf entfernt Ähnlichem auf. In der Welt der PHP-Frameworks ist das jedoch eine einzigartige Eigenschaft.

Zugleich beeinflussen Komponenten die Herangehensweise an die Anwendungsentwicklung grundlegend. Sie können Seiten aus vorbereiteten Einheiten zusammensetzen. Brauchen Sie in Ihrer Administration ein Datagrid? Finden Sie es auf Componette, einem Repository von Open-Source-Erweiterungen (nicht nur Komponenten) für Nette, und fügen Sie es einfach in den Presenter ein.

Sie können beliebig viele Komponenten in den Presenter einbinden. Und in manche Komponenten können Sie weitere Komponenten einbetten. So entsteht ein Komponentenbaum, dessen Wurzel der Presenter ist.

Factory-Methoden

Wie werden Komponenten in den Presenter eingefügt und anschließend verwendet? Üblicherweise über Factory-Methoden.

Eine Factory für Komponenten ist ein eleganter Weg, Komponenten erst dann zu erzeugen, wenn sie tatsächlich gebraucht werden (lazy / on demand). Die ganze Magie steckt in der Implementierung einer Methode namens createComponent<Name>(), wobei <Name> der Name der erzeugten Komponente ist und die die Komponente erzeugt und zurückgibt.

class DefaultPresenter extends Nette\Application\UI\Presenter
{
	protected function createComponentPoll(): PollControl
	{
		$poll = new PollControl;
		$poll->items = $this->items;
		return $poll;
	}
}

Weil alle Komponenten in eigenen Methoden erzeugt werden, wird der Code übersichtlicher.

Namen von Komponenten beginnen immer mit einem Kleinbuchstaben, obwohl sie im Methodennamen großgeschrieben werden.

Wir rufen Factories nie direkt auf; sie werden automatisch beim ersten Verwenden der Komponente aufgerufen. Dadurch wird die Komponente im richtigen Moment erzeugt und nur dann, wenn sie tatsächlich gebraucht wird. Verwenden wir die Komponente nicht (z. B. bei einem AJAX-Request, bei dem nur ein Teil der Seite übertragen wird, oder beim Cachen des Templates), wird sie überhaupt nicht erzeugt, was Serverleistung spart.

// wir greifen auf die Komponente zu, und wenn es das erste Mal war,
// wird createComponentPoll() aufgerufen, die sie erzeugt
$poll = $this->getComponent('poll');
// alternative Schreibweise: $poll = $this['poll'];

Im Template lässt sich eine Komponente mit dem Tag {control} rendern. Komponenten müssen daher nicht von Hand an das Template übergeben werden.

<h2>Bitte stimmen Sie ab</h2>

{control poll}

Um dynamisch eine variable Anzahl von Komponenten zu erzeugen, verwenden Sie den Multiplier.

Die Factory-Methoden createComponent<Name>() funktionieren nicht nur in Presentern. Auf dieselbe Weise können Sie eine Komponente in eine andere Komponente verschachteln und sie zu einem Baum zusammensetzen – praktisch zum Beispiel für ein separat gerendertes Formular innerhalb einer Komponente.

Hollywood Style

Komponenten nutzen gewöhnlich eine erfrischende Technik, die wir gerne Hollywood Style nennen. Sie kennen sicher das Klischee, das Teilnehmer von Filmcastings oft hören: „Rufen Sie uns nicht an, wir rufen Sie an.“ Und genau darum geht es.

In Nette müssen Sie nicht ständig Fragen stellen („wurde das Formular abgeschickt?“, „war es gültig?“, oder „hat der Benutzer diese Schaltfläche gedrückt?“), sondern Sie sagen dem Framework „wenn das passiert, rufe diese Methode auf“ und überlassen ihm die weitere Arbeit. Wenn Sie in JavaScript programmieren, ist Ihnen dieser Programmierstil bestens vertraut. Sie schreiben Funktionen, die aufgerufen werden, wenn ein bestimmtes Ereignis eintritt. Und die Sprache übergibt ihnen die passenden Parameter.

Das ändert die Sicht auf das Schreiben von Anwendungen vollständig. Je mehr Aufgaben Sie dem Framework überlassen können, desto weniger Arbeit haben Sie. Und desto weniger können Sie übersehen.

Eine Komponente schreiben

Mit dem Begriff Komponente meinen wir üblicherweise einen Nachfahren der Klasse Nette\Application\UI\Control. (Genauer wäre der Begriff „Controls“, aber der hat in manchen Sprachen eine andere Bedeutung, und „Komponenten“ hat sich stärker eingebürgert.) Auch der Presenter Nette\Application\UI\Presenter selbst ist ein Nachfahre der Klasse Control.

use Nette\Application\UI\Control;

class PollControl extends Control
{
}

Rendering

Wir wissen bereits, dass zum Rendern einer Komponente der Tag {control componentName} dient. Er ruft in Wirklichkeit die Methode render() der Komponente auf, in der wir uns um das Rendering kümmern. Wir haben, genau wie im Presenter, in der Variablen $this->template ein Latte-Template zur Verfügung, dem wir Parameter übergeben. Anders als im Presenter müssen wir die Template-Datei angeben und sie rendern lassen:

public function render(): void
{
	// einige Parameter in das Template einfügen
	$this->template->param = $value;
	// und es rendern
	$this->template->render(__DIR__ . '/poll.latte');
}

Der Tag {control} erlaubt es, der Methode render() Parameter zu übergeben:

{control poll $id, $message}
public function render(int $id, string $message): void
{
	// ...
}

Manchmal kann eine Komponente aus mehreren Teilen bestehen, die wir getrennt rendern wollen. Für jeden davon legen wir eine eigene Rendering-Methode an, im Beispiel hier renderPaginator():

public function renderPaginator(): void
{
	// ...
}

Und im Template rufen wir sie dann so auf:

{control poll:paginator}

Zum besseren Verständnis ist es gut zu wissen, wie sich dieser Tag in PHP-Code übersetzt.

{control poll}
{control poll:paginator 123, 'hello'}

übersetzt sich zu:

$control->getComponent('poll')->render();
$control->getComponent('poll')->renderPaginator(123, 'hello');

Die Methode getComponent() gibt die Komponente poll zurück, und auf dieser Komponente wird die Methode render() aufgerufen, bzw. renderPaginator(), wenn im Tag nach dem Doppelpunkt eine andere Rendering-Methode angegeben ist.

Achtung: Erscheint in den Parametern außerhalb eckiger Klammern ein =>, werden alle Parameter in ein Array verpackt und als erstes Argument übergeben:

{control poll, id: 123, message: 'hello'}

übersetzt sich zu:

$control->getComponent('poll')->render(['id' => 123, 'message' => 'hello']);

Rendering einer Unterkomponente:

{control cartControl-someForm}

übersetzt sich zu:

$control->getComponent("cartControl-someForm")->render();

Komponenten übergeben Templates, ebenso wie Presenter, automatisch mehrere nützliche Variablen:

  • $basePath ist der absolute URL-Pfad zum Wurzelverzeichnis (z. B. /eshop)
  • $baseUrl ist die absolute URL zum Wurzelverzeichnis (z. B. http://localhost/eshop)
  • $user ist ein Objekt, das den Benutzer repräsentiert
  • $presenter ist der aktuelle Presenter
  • $control ist die aktuelle Komponente
  • $flashes ist ein Array von Meldungen, die mit der Funktion flashMessage() gesendet wurden

Signal

Wir wissen bereits, dass die Navigation in einer Nette-Anwendung im Verlinken oder Weiterleiten auf Paare Presenter:action besteht. Was aber, wenn wir nur eine Aktion auf der aktuellen Seite ausführen wollen? Zum Beispiel die Sortierung von Spalten einer Tabelle ändern; ein Element löschen; zwischen hellem und dunklem Modus umschalten; ein Formular absenden; in einer Umfrage abstimmen usw.

Diese Art von Request nennt man Signal. Und so, wie Aktionen die Methoden action<Aktion>() oder render<Aktion>() aufrufen, rufen Signale die Methoden handle<Signal>() auf. Während sich der Begriff Aktion (oder View) rein auf Presenter bezieht, betreffen Signale alle Komponenten. Und damit auch Presenter, denn UI\Presenter ist ein Nachfahre von UI\Control.

public function handleClick(int $x, int $y): void
{
	// ... Verarbeitung des Signals ...
}

Einen Link, der ein Signal aufruft, erstellt man auf die übliche Weise, also im Template mit dem Attribut n:href oder dem Tag {link}, im Code mit der Methode link(). Mehr im Kapitel Erstellen von URL-Links.

<a n:href="click! $x, $y">hier klicken</a>

Ein Signal wird immer auf dem aktuellen Presenter und der aktuellen Aktion aufgerufen; es lässt sich nicht auf einem anderen Presenter oder einer anderen Aktion auslösen.

Ein Signal bewirkt also, dass die Seite genau wie beim ursprünglichen Request neu geladen wird, ruft zusätzlich aber die Methode zur Verarbeitung des Signals mit den passenden Parametern auf. Existiert die Methode nicht, wird eine Exception Nette\Application\UI\BadSignalException geworfen, die dem Benutzer als Fehlerseite 403 Forbidden angezeigt wird.

Snippets und AJAX

Signale erinnern Sie vielleicht ein wenig an AJAX: Handler, die auf der aktuellen Seite aufgerufen werden. Und Sie haben recht, Signale werden tatsächlich oft mit AJAX aufgerufen, und anschließend werden nur die geänderten Teile der Seite in den Browser übertragen. Diese nennt man Snippets. Mehr dazu finden Sie auf der Seite über AJAX.

Flash-Meldungen

Eine Komponente hat einen eigenen, vom Presenter unabhängigen Speicher für Flash-Meldungen. Das sind Meldungen, die zum Beispiel über das Ergebnis einer Operation informieren. Eine wichtige Eigenschaft von Flash-Meldungen ist, dass sie im Template auch nach einer Weiterleitung verfügbar sind. Auch nach der Anzeige bleiben sie noch 30 Sekunden aktiv – zum Beispiel für den Fall, dass der Benutzer die Seite wegen eines Übertragungsfehlers neu lädt, verschwindet die Meldung nicht sofort.

Das Senden übernimmt die Methode flashMessage. Der erste Parameter ist der Text der Meldung (string, Stringable) oder ein Objekt stdClass, das die Meldung repräsentiert. Der optionale zweite Parameter ist ihr Typ (error, warning, info usw.). Die Methode flashMessage() gibt eine Instanz der Flash-Meldung als Objekt stdClass zurück, dem sich weitere Informationen hinzufügen lassen.

$this->flashMessage('Das Element wurde gelöscht.');
$this->redirect(/* ... */); // und weiterleiten

Diese Meldungen stehen dem Template in der Variablen $flashes als Objekte stdClass zur Verfügung, die die Properties message (Text der Meldung) und type (Typ der Meldung) enthalten und die erwähnten eigenen Informationen enthalten können. Wir rendern sie zum Beispiel so:

{foreach $flashes as $flash}
	<div class="flash {$flash->type}">{$flash->message}</div>
{/foreach}

Weiterleitung nach Verarbeitung eines Signals

Auf die Verarbeitung eines Signals einer Komponente folgt oft eine Weiterleitung. Das ist ähnlich wie bei Formularen – nach dem Absenden leiten wir ebenfalls weiter, damit die Daten beim Neuladen der Seite im Browser nicht erneut abgeschickt werden.

$this->redirect('this'); // leitet auf den aktuellen Presenter und die aktuelle Aktion weiter

Da eine Komponente ein wiederverwendbares Element ist und üblicherweise keine direkte Bindung an konkrete Presenter haben sollte, interpretieren die Methoden redirect() und link() den Parameter automatisch als Signal der Komponente:

$this->redirect('click'); // leitet auf das Signal 'click' derselben Komponente weiter

Müssen Sie auf einen anderen Presenter oder eine andere Aktion weiterleiten, geht das über den Presenter:

$this->getPresenter()->redirect('Product:show'); // leitet auf einen anderen Presenter/eine andere Aktion weiter

Persistente Parameter

Persistente Parameter dienen dazu, den Zustand von Komponenten über verschiedene Requests hinweg zu erhalten. Ihr Wert bleibt auch nach dem Klick auf einen Link derselbe. Anders als Daten in der Session werden sie in der URL übertragen. Und das geschieht vollautomatisch, einschließlich der Links, die in anderen Komponenten auf derselben Seite erstellt werden.

Sie haben zum Beispiel eine Komponente zur Paginierung von Inhalten. Auf einer Seite können mehrere solcher Komponenten sein. Und wir wollen, dass alle Komponenten nach dem Klick auf einen Link auf ihrer aktuellen Seite bleiben. Deshalb machen wir die Seitennummer (page) zu einem persistenten Parameter.

Einen persistenten Parameter in Nette zu erstellen ist ausgesprochen einfach. Legen Sie einfach eine public Property an und kennzeichnen Sie sie mit dem Attribut: (früher wurde /** @persistent */ verwendet)

use Nette\Application\Attributes\Persistent;  // diese Zeile ist wichtig

class PaginatingControl extends Control
{
	#[Persistent]
	public int $page = 1; // muss public sein
}

Wir empfehlen, für die Property den Datentyp anzugeben (z. B. int), und Sie können auch einen Standardwert angeben. Die Werte der Parameter lassen sich validieren.

Beim Erstellen eines Links lässt sich der Wert eines persistenten Parameters ändern:

<a n:href="this page: $page + 1">weiter</a>

Oder er lässt sich zurücksetzen, also aus der URL entfernen. Er nimmt dann seinen Standardwert an:

<a n:href="this page: null">zurücksetzen</a>

Persistente Komponenten

Nicht nur Parameter, sondern auch Komponenten können persistent sein. Ihre persistenten Parameter werden dann auch zwischen verschiedenen Aktionen des Presenters oder zwischen mehreren Presentern übertragen. Persistente Komponenten kennzeichnen wir mit einem Attribut an der Klasse des Presenters. So kennzeichnen wir zum Beispiel die Komponenten calendar und poll:

use Nette\Application\Attributes\Persistent;

#[Persistent('calendar', 'poll')]
class DefaultPresenter extends Nette\Application\UI\Presenter
{
}

Unterkomponenten innerhalb dieser Komponenten müssen nicht gekennzeichnet werden; auch sie werden persistent.

Die ältere Annotation @persistent funktioniert weiterhin, ist aber veraltet und löst eine Warnung aus:

/**
 * @persistent(calendar, poll)
 */
class DefaultPresenter extends Nette\Application\UI\Presenter
{
}

Komponenten mit Abhängigkeiten

Wie erstellt man Komponenten mit Abhängigkeiten, ohne die Presenter, die sie verwenden, zu „verstopfen“? Dank der klugen Fähigkeiten des DI-Containers in Nette lässt sich, ähnlich wie bei klassischen Services, der Großteil der Arbeit dem Framework überlassen.

Nehmen wir als Beispiel eine Komponente, die eine Abhängigkeit vom Service PollFacade hat:

class PollControl extends Control
{
	public function __construct(
		private int $id, // ID der Umfrage, für die wir die Komponente erstellen
		private PollFacade $facade,
	) {
	}

	public function handleVote(int $voteId): void
	{
		$this->facade->vote($this->id, $voteId);
		// ...
	}
}

Würden wir einen klassischen Service schreiben, gäbe es nichts zu besprechen. Der DI-Container würde die Übergabe aller Abhängigkeiten unsichtbar erledigen. Bei Komponenten lösen wir das jedoch üblicherweise so, dass wir in den Factory-Methoden createComponent…() direkt im Presenter eine neue Instanz erzeugen. Aber alle Abhängigkeiten aller Komponenten in den Presenter zu übergeben, nur um sie an die Komponenten weiterzureichen, ist umständlich. Und die Menge des geschriebenen Codes …

Die logische Frage lautet: Warum registrieren wir die Komponente nicht einfach als klassischen Service, übergeben sie dem Presenter und geben sie dann in der Methode createComponent…() zurück? Dieser Ansatz ist jedoch ungeeignet, weil wir die Möglichkeit haben wollen, die Komponente bei Bedarf mehrfach zu erzeugen.

Die richtige Lösung ist, für die Komponente eine Factory zu schreiben, also eine Klasse, die die Komponente für uns erzeugt:

class PollControlFactory
{
	public function __construct(
		private PollFacade $facade,
	) {
	}

	public function create(int $id): PollControl
	{
		return new PollControl($id, $this->facade);
	}
}

Diese Factory registrieren wir in der Konfiguration in unserem Container:

services:
	- PollControlFactory

und schließlich verwenden wir sie in unserem Presenter:

class PollPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private PollControlFactory $pollControlFactory,
	) {
	}

	protected function createComponentPollControl(): PollControl
	{
		$pollId = 1; // wir können unseren Parameter übergeben
		return $this->pollControlFactory->create($pollId);
	}
}

Das Großartige daran ist, dass Nette DI solche einfachen Factories generieren kann, sodass Sie statt des gesamten Codes nur ihr Interface schreiben müssen:

interface PollControlFactory
{
	public function create(int $id): PollControl;
}

Und das ist alles. Nette implementiert dieses Interface intern und injiziert es in den Presenter, wo wir es verwenden können. Es ergänzt unsere Komponente auf magische Weise um den Parameter $id und eine Instanz der Klasse PollFacade.

Komponenten im Detail

Komponenten stellen in Nette Application wiederverwendbare Teile einer Webanwendung dar, die wir in Seiten einbetten und denen dieses ganze Kapitel gewidmet ist. Was genau kann eine solche Komponente?

  1. Sie ist in einem Template renderbar
  2. Sie weiß, welchen Teil von sich sie bei einem AJAX-Request rendern soll (Snippets)
  3. Sie kann ihren Zustand in der URL speichern (persistente Parameter)
  4. Sie kann auf Benutzeraktionen reagieren (Signale)
  5. Sie bildet eine hierarchische Struktur (deren Wurzel der Presenter ist)

Jede dieser Funktionen übernimmt eine der Klassen in der Vererbungslinie. Um das Rendering (1 + 2) kümmert sich Nette\Application\UI\Control, um die Einbindung in den Lebenszyklus (3, 4) die Klasse Nette\Application\UI\Component, und um den Aufbau der hierarchischen Struktur (5) die Klassen Container und Component.

Nette\ComponentModel\Component  { IComponent }
|
+- Nette\ComponentModel\Container  { IContainer }
	|
	+- Nette\Application\UI\Component  { SignalReceiver, StatePersistent }
		|
		+- Nette\Application\UI\Control  { Renderable }
			|
			+- Nette\Application\UI\Presenter  { IPresenter }

Lebenszyklus einer Komponente

Lebenszyklus einer Komponente

Validierung persistenter Parameter

Die aus der URL empfangenen Werte der Persistente Parameter werden von der Methode loadState() in die Properties geschrieben. Sie prüft außerdem, ob der für die Property angegebene Datentyp passt; andernfalls antwortet sie mit dem Fehler 404 und die Seite wird nicht angezeigt.

Vertrauen Sie persistenten Parametern niemals blind, denn sie lassen sich vom Benutzer in der URL leicht überschreiben. So prüfen wir zum Beispiel, ob die Seitennummer $this->page größer als 0 ist. Ein geeigneter Weg ist, die erwähnte Methode loadState() zu überschreiben:

class PaginatingControl extends Control
{
	#[Persistent]
	public int $page = 1;

	public function loadState(array $params): void
	{
		parent::loadState($params); // hier wird $this->page gesetzt
		// es folgt die eigene Wertprüfung:
		if ($this->page < 1) {
			$this->error();
		}
	}
}

Den umgekehrten Vorgang, also das Einsammeln der Werte aus den persistenten Properties, übernimmt die Methode saveState().

Verbindung mit dem Presenter

In dem Moment, in dem eine Komponente Teil der Hierarchie des Presenters wird, werden ihre im Array $onAnchor gespeicherten Callbacks aufgerufen. Ab diesem Zeitpunkt hat die Komponente den Presenter zur Verfügung, kann gefahrlos Links erstellen, persistente Parameter lesen und so weiter.

$control->onAnchor[] = function ($control): void {
	// die Komponente hat nun den Presenter zur Verfügung
};

Signale im Detail

Ein Signal bewirkt, dass die Seite genau wie beim ursprünglichen Request neu geladen wird (außer beim Aufruf über AJAX), und ruft die Methode signalReceived($signal) auf, deren Standardimplementierung in der Klasse Nette\Application\UI\Component versucht, eine aus den Wörtern handle<Signal> zusammengesetzte Methode aufzurufen. Die weitere Verarbeitung liegt beim jeweiligen Objekt. Objekte, die von Component erben (also Control und Presenter), reagieren so, dass sie versuchen, die Methode handle<Signal> mit den passenden Parametern aufzurufen.

Mit anderen Worten: Es wird die Definition der Funktion handle<Signal> genommen, samt allen Parametern, die mit dem Request kamen, und die Parameter aus der URL werden den Argumenten anhand des Namens zugeordnet, dann wird versucht, die Methode aufzurufen. Zum Beispiel wird der Wert des Parameters id aus der URL als Argument $id übergeben, something aus der URL als $something usw. Und existiert die Methode nicht, wirft die Methode signalReceived eine Exception.

Neben den Parametern aus der URL liest ein Signal auch die im POST-Body des Requests gesendeten Parameter. Das ist praktisch, weil Signale oft über JavaScript aufgerufen werden, wo es natürlich ist, Daten mit der Methode POST zu senden. Kommt ein Parameter gleichen Namens jedoch sowohl aus der URL als auch aus dem POST-Body, hat der Wert aus der URL Vorrang. Vermeiden Sie es daher, einem POST-Feld denselben Namen wie einem URL- oder Route-Parameter zu geben, sonst würde der Wert aus der URL ihn stillschweigend überschreiben. Die Signalparameter teilen sich einen gemeinsamen Raum mit den Parametern der Aktion und den persistenten Parametern, siehe Gemeinsamer Parameterraum.

Ein Signal kann jede Komponente, jeder Presenter oder jedes Objekt empfangen, das das Interface SignalReceiver implementiert und mit dem Komponentenbaum verbunden ist.

Die Hauptempfänger von Signalen werden Presenter und visuelle Komponenten sein, die von Control erben. Ein Signal soll einem Objekt als Zeichen dienen, dass es etwas tun soll – eine Umfrage soll eine Stimme des Benutzers zählen, ein Nachrichtenblock soll sich ausklappen und doppelt so viele Nachrichten anzeigen, ein Formular wurde abgeschickt und soll Daten verarbeiten und so weiter.

Die URL für ein Signal wird mit der Methode Component::link() erstellt. Als Parameter $destination übergeben wir den String {signal}! und als $args ein Array von Argumenten, die wir dem Signal übergeben wollen. Das Signal wird immer auf dem aktuellen Presenter und der aktuellen Aktion mit den aktuellen Parametern aufgerufen; die Parameter des Signals kommen lediglich hinzu. Zusätzlich wird der Parameter ?do, der das Signal angibt, ergänzt.

Sein Format lautet entweder {signal} oder {signalReceiver}-{signal}. {signalReceiver} ist der Name der Komponente im Presenter. Deshalb darf im Namen der Komponente kein Bindestrich vorkommen – er dient der Trennung von Komponentenname und Signal, wobei sich auf diese Weise mehrere Komponenten verschachteln lassen.

Die Methode isSignalReceiver() prüft, ob die Komponente (erstes Argument) Empfänger des Signals (zweites Argument) ist. Das zweite Argument kann weggelassen werden – dann wird geprüft, ob die Komponente Empfänger irgendeines Signals ist. Wird der zweite Parameter auf true gesetzt, wird geprüft, ob die angegebene Komponente oder einer ihrer Nachfahren Empfänger ist.

In jeder Phase vor handle<Signal> können wir das Signal manuell ausführen, indem wir die Methode processSignal() aufrufen, die sich um die Verarbeitung des Signals kümmert – sie nimmt die als Signalempfänger bestimmte Komponente (ist kein Empfänger angegeben, ist es der Presenter selbst) und sendet ihr das Signal.

Beispiel:

if ($this->isSignalReceiver($this, 'paging') || $this->isSignalReceiver($this, 'sorting')) {
	$this->processSignal();
}

Dadurch wird das Signal vorzeitig ausgeführt und nicht erneut aufgerufen.