Module

Module bringen Klarheit in Nette-Anwendungen, indem sie eine einfache Unterteilung in logische Einheiten ermöglichen.

Ähnlich wie bei der Organisation von Dateien in Ordnern auf einer Festplatte, können wir in Nette Presenter, Vorlagen und andere Hilfsklassen in Module unterteilen. Wie funktioniert das in der Praxis? Ganz einfach, indem man neue Unterverzeichnisse in die Struktur einfügt. Hier ist ein Beispiel für eine Struktur mit zwei Modulen, Front und Admin:

app/
├── UI/
│   ├── Admin/            ← Admin module
│   │   ├── @layout.latte
│   │   ├── Dashboard/
│   │   │   ├── DashboardPresenter.php
│   │   │   └── default.latte
│   │   └── ...
│   ├── Front/            ← Front module
│   │   ├── @layout.latte
│   │   ├── Home/
│   │   │   ├── HomePresenter.php
│   │   │   └── default.latte
│   │   └── ...

Diese Verzeichnisstruktur spiegelt sich in den Namespaces der Klassen wider, so befindet sich z.B. DashboardPresenter im Namespace App\UI\Admin\Dashboard:

namespace App\UI\Admin\Dashboard;

class DashboardPresenter extends Nette\Application\UI\Presenter
{
	// ...
}

In der Anwendung wird der Presenter Dashboard innerhalb des Moduls Admin mit Doppelpunkt als Admin:Dashboard bezeichnet. Für die Aktion default wird er als Admin:Dashboard:default bezeichnet.

Die vorgestellte Struktur ist nicht starr; Sie können sie in der Konfiguration vollständig an Ihre Bedürfnisse anpassen.

Module können neben Presentern und Vorlagen auch alle anderen Dateien, wie Komponenten und Hilfsklassen, enthalten. Wenn Sie überlegen, wo Sie diese ablegen wollen, sollten Sie einen Ordner Accessory verwenden:

app/
├── UI/
│   ├── Admin/
│   │   ├── Accessory/
│   │   │   ├── FormFactory.php
│   │   │   └── AdminLayout.php
│   │   ├── Dashboard/
│   │   └── ...

Verschachtelte Module

Module können mehrere Verschachtelungsebenen haben, ähnlich wie eine Verzeichnisstruktur auf einer Festplatte:

app/
├── UI/
│   ├── Blog/             ← Blog module
│   │   ├── Admin/        ← Admin submodule
│   │   │   ├── Dashboard/
│   │   │   └── ...
│   │   ├── Front/        ← Front submodule
│   │   │   ├── @layout.latte
│   │   │   ├── Home/
│   │   │   └── ...
│   ├── Forum/            ← Forum module
│   │   └── ...

Das Modul Blog ist in die Untermodule Admin und Front unterteilt. Dies spiegelt sich auch in den Namespaces wider, die dann als App\UI\Blog\Admin und ähnlich erscheinen. Um auf den Präsentator Dashboard innerhalb des Submoduls Admin zu verweisen, wird er als Blog:Admin:Dashboard bezeichnet.

Die Verschachtelung kann so tief wie nötig sein und erlaubt die Erstellung von Sub-Submodulen.

Wenn Sie zum Beispiel in der Verwaltung viele Präsentatoren haben, die mit der Auftragsverwaltung zusammenhängen, wie OrderDetail, OrderEdit, OrderDispatch, usw., könnten Sie ein Order Modul erstellen, in dem Präsentatoren wie Detail, Edit, Dispatch und andere organisiert werden.

Links in Präsentatorvorlagen sind relativ zum aktuellen Modul. So führt der Link Foo:default zu dem Präsentator Foo im gleichen Modul wie der aktuelle Präsentator. Wenn das aktuelle Modul zum Beispiel Front ist, sieht der Link so aus:

<a n:href="Product:show">link to Front:Product:show</a>

Ein Link ist auch dann relativ, wenn er den Namen eines Moduls enthält, das dann als Untermodul betrachtet wird:

<a n:href="Shop:Product:show">link to Front:Shop:Product:show</a>

Absolute Links werden analog zu absoluten Pfaden auf der Festplatte geschrieben, jedoch mit Doppelpunkten anstelle von Schrägstrichen. Ein absoluter Link beginnt also mit einem Doppelpunkt:

<a n:href=":Admin:Product:show">link to Admin:Product:show</a>

Um herauszufinden, ob wir uns in einem bestimmten Modul oder dessen Untermodul befinden, können wir die Funktion isModuleCurrent(moduleName) verwenden.

<li n:class="isModuleCurrent('MyEshop:Users') ? active">
	<a n:href="Product:">...</a>
</li>

Routing

Siehe Kapitel über Routing.

Kartierung

Mapping definiert die Regeln für die Ableitung des Klassennamens aus dem Presenter-Namen. Diese Regeln werden in der Konfiguration unter dem Schlüssel application › mapping angegeben.

Die oben auf dieser Seite erwähnten Verzeichnisstrukturen basieren auf der folgenden Zuordnung:

application:
	mapping: App\UI\*\**Presenter

Wie funktioniert das Mapping? Zum besseren Verständnis stellen wir uns zunächst eine Anwendung ohne Module vor. Wir wollen, dass die Presenter-Klassen in den Namensraum App\UI fallen, so dass der Presenter Home auf die Klasse App\UI\HomePresenter abgebildet wird. Dies kann mit dieser Konfiguration erreicht werden:

application:
	mapping: App\UI\*Presenter

Diese Zuordnung funktioniert, indem das Sternchen in der Maske App\UI\*Presenter durch den Presenter-Namen Home ersetzt wird, was zu dem endgültigen Klassennamen App\UI\HomePresenter führt. Einfach!

Wie Sie jedoch in den Beispielen in diesem und anderen Kapiteln sehen können, platzieren wir Presenter-Klassen in gleichnamigen Unterverzeichnissen, z. B. wird der Presenter Home der Klasse App\UI\Home\HomePresenter zugeordnet. Dies wird durch die Verdoppelung des Sternchens erreicht (erfordert Nette Application 3.2):

application:
	mapping: App\UI\**Presenter

Gehen wir nun dazu über, Presenter in Modulen abzubilden. Für jedes Modul können wir spezifische Zuordnungen definieren:

application:
	mapping:
		Front: App\UI\Front\**Presenter
		Admin: App\UI\Admin\**Presenter
		Api: App\Api\*Presenter

Nach dieser Konfiguration wird der Präsentator Front:Home der Klasse App\UI\Front\Home\HomePresenter zugeordnet, während der Präsentator Api:OAuth der Klasse App\Api\OAuthPresenter zugeordnet wird.

Da die Module Front und Admin einen ähnlichen Zuordnungsansatz haben und es wahrscheinlich noch mehr solcher Module gibt, ist es möglich, eine allgemeine Regel zu erstellen, die sie ersetzt. In der Klassenmaske wird ein neues Sternchen für das Modul hinzugefügt:

application:
	mapping:
		*: App\UI\*\**Presenter
		Api: App\Api\*Presenter

Bei mehrstufig verschachtelten Modulen, wie z. B. dem Moderator Admin:User:Edit, wird das Sternchen-Segment für jede Stufe wiederholt, was zu der Klasse App\UI\Admin\User\Edit\EditPresenter führt.

Eine alternative Schreibweise ist die Verwendung eines Arrays, das aus drei Segmenten besteht, anstelle einer Zeichenkette. Diese Notation ist äquivalent zur vorherigen:

application:
	mapping:
		*: [App\UI, *, **Presenter]
		Api: [App\Api, '', *Presenter]

Wenn wir nur eine Regel in der Konfiguration haben, die allgemeine, können wir kurz schreiben:

application:
	mapping: App\UI\*\**Presenter