Module

In Nette stellen Module die logischen Einheiten dar, aus denen eine Anwendung besteht. Sie umfassen Presenter, Templates, eventuell auch Komponenten und Modellklassen.

Ein Verzeichnis für Presenter und eines für Templates würde für echte Projekte nicht ausreichen. Dutzende von Dateien in einem Ordner zu haben, ist zumindest unorganisiert. Wie kommt man da wieder raus? Wir teilen sie einfach in Unterverzeichnisse auf der Festplatte und in Namensräume im Code auf. Und das ist genau das, was die Nette-Module tun.

Vergessen wir also einen einzigen Ordner für Präsentatoren und Vorlagen und erstellen wir stattdessen Module, zum Beispiel Admin und Front.

app/
├── Presenters/
├── Modules/              ← Verzeichnis mit Modulen
│   ├── Admin/            ← Modul Admin
│   │   ├── Presenters/   ← seine Presenters
│   │   │   ├── DashboardPresenter.php
│   │   │   └── templates/
│   └── Front/            ← Modul Front
│       └── Presenters/   ← seine Presenters
│           └── ...

Diese Verzeichnisstruktur spiegelt sich in den Klassennamensräumen wider, so dass z. B. DashboardPresenter im Namensraum App\Modules\Admin\Presenters liegt:

namespace App\Modules\Admin\Presenters;

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

Der Präsentator Dashboard innerhalb des Moduls Admin wird innerhalb der Anwendung mit der Doppelpunktschreibweise als Admin:Dashboard referenziert, und seine Aktion default als Admin:Dashboard:default. Und woher weiß Nette selbst, dass Admin:Dashboard die Klasse App\Modules\Admin\Presenters\DashboardPresenter repräsentiert? Dies wird durch ein Mapping in der Konfiguration festgelegt. Die vorgegebene Struktur ist also nicht fest vorgegeben und kann nach Belieben verändert werden.

Module können neben Presentern und Templates natürlich auch alle anderen Elemente enthalten, wie z.B. Komponenten, Modellklassen, etc.

Verschachtelte Module

Module müssen nicht nur eine flache Struktur bilden, Sie können auch Untermodule erstellen, zum Beispiel:

app/
├── Modules/              ← Verzeichnis mit Modulen
│   ├── Blog/             ← Modul Blog
│   │   ├── Admin/        ← Submodul Admin
│   │   │   ├── Presenters/
│   │   │   └── ...
│   │   └── Front/        ← Submodul Front
│   │       ├── Presenters/
│   │       └── ...
│   ├── Forum/            ← Modul Forum
│   │   └── ...

So wird das Modul Blog in die Untermodule Admin und Front aufgeteilt. Dies spiegelt sich auch in den Namensräumen wider, die dann App\Modules\Blog\Admin\Presenters usw. lauten. Der Präsentator Dashboard innerhalb des Submoduls wird als Blog:Admin:Dashboard bezeichnet.

Die Verschachtelung kann beliebig tief gehen, so dass Sub-Submodule erstellt werden können.

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.

Abbildung

Legt die Regeln fest, nach denen der Klassenname aus dem Namen des Präsentators abgeleitet wird. Wir schreiben sie in die Konfiguration unter dem Schlüssel application › mapping.

Beginnen wir mit einem Beispiel, das keine Module verwendet. Wir wollen nur, dass die Presenter-Klassen den Namespace App\Presenters haben. Das bedeutet, dass ein Presenter wie Home auf die Klasse App\Presenters\HomePresenter abgebildet werden soll. Dies kann durch die folgende Konfiguration erreicht werden:

application:
	mapping:
		*: App\Presenters\*Presenter

Der Name des Presenters wird durch das Sternchen in der Klassenmaske ersetzt und das Ergebnis ist der Klassenname. Einfach!

Wenn wir die Vortragenden in Module unterteilen, können wir für jedes Modul eine eigene Zuordnung vornehmen:

application:
	mapping:
		Front: App\Modules\Front\Presenters\*Presenter
		Admin: App\Modules\Admin\Presenters\*Presenter
		Api: App\Api\*Presenter

Der Referent Front:Home wird der Klasse App\Modules\Front\Presenters\HomePresenter zugeordnet und der Referent Admin:Dashboard der Klasse App\Modules\Admin\Presenters\DashboardPresenter.

Es ist praktischer, eine allgemeine (Stern-)Regel zu erstellen, um die ersten beiden zu ersetzen. Das zusätzliche Sternchen wird der Klassenmaske nur für dieses Modul hinzugefügt:

application:
	mapping:
		*: App\Modules\*\Presenters\*Presenter
		Api: App\Api\*Presenter

Was aber, wenn wir verschachtelte Module verwenden und einen Präsentator Admin:User:Edit haben? In diesem Fall wird das Segment mit dem Sternchen, das das Modul für jede Ebene darstellt, einfach wiederholt und das Ergebnis ist die Klasse App\Modules\Admin\User\Presenters\EditPresenter.

Eine alternative Schreibweise ist die Verwendung eines Arrays, das aus drei Segmenten anstelle einer Zeichenkette besteht. Diese Notation ist gleichwertig mit der vorherigen:

application:
	mapping:
		*: [App\Modules, *, Presenters\*Presenter]

Der Standardwert ist *: *Module\*Presenter.