Nette Documentation Preview

syntax
Verzeichnisstruktur der Anwendung
*********************************

<div class=perex>

Wie entwirft man eine klare und skalierbare Verzeichnisstruktur für Projekte in Nette Framework? Wir zeigen Ihnen bewährte Verfahren, die Ihnen helfen, Ihren Code zu organisieren. Sie werden es lernen:

- wie Sie Ihre Anwendung **logisch** in Verzeichnisse strukturieren
- wie man die Struktur so gestaltet, dass sie bei wachsendem Projekt **gut skalierbar** ist
- was die **möglichen Alternativen** sind und welche Vor- und Nachteile sie haben

</div>


Es ist wichtig zu erwähnen, dass das Nette Framework selbst nicht auf einer bestimmten Struktur besteht. Es ist so konzipiert, dass es sich leicht an alle Bedürfnisse und Vorlieben anpassen lässt.


Grundlegende Projektstruktur .[#toc-basic-project-structure]
============================================================

Obwohl Nette Framework keine feste Verzeichnisstruktur vorgibt, gibt es eine bewährte Standardanordnung in Form von [Web Project |https://github.com/nette/web-project]:

/--pre
<b>web-project/</b>
├── <b>app/</b>              ← Anwendungsverzeichnis
├── <b>assets/</b>           ← SCSS, JS-Dateien, Bilder..., alternativ resources/
├── <b>bin/</b>              ← Befehlszeilenskripte
├── <b>config/</b>           ← Konfiguration
├── <b>log/</b>              ← protokollierte Fehler
├── <b>temp/</b>             ← temporäre Dateien, Cache
├── <b>tests/</b>            ← Tests
├── <b>vendor/</b>           ← vom Composer installierte Bibliotheken
└── <b>www/</b>              ← öffentliches Verzeichnis (document-root)
\--

Sie können diese Struktur frei nach Ihren Bedürfnissen verändern - Ordner umbenennen oder verschieben. Dann müssen Sie nur die relativen Pfade zu den Verzeichnissen in `Bootstrap.php` und eventuell `composer.json` anpassen. Mehr ist nicht nötig, keine komplexe Neukonfiguration, keine ständigen Änderungen. Nette verfügt über eine intelligente automatische Erkennung und erkennt automatisch den Speicherort der Anwendung einschließlich ihrer URL-Basis.


Grundsätze der Code-Organisation .[#toc-code-organization-principles]
=====================================================================

Wenn Sie zum ersten Mal ein neues Projekt erkunden, sollten Sie in der Lage sein, sich schnell zu orientieren. Stellen Sie sich vor, Sie klicken auf das Verzeichnis `app/Model/` und sehen diese Struktur:

/--pre
<b>app/Model/</b>
├── <b>Services/</b>
├── <b>Repositories/</b>
└── <b>Entities/</b>
\--

Daraus erfahren Sie nur, dass das Projekt einige Dienste, Repositories und Entitäten verwendet. Sie werden nichts über den eigentlichen Zweck der Anwendung erfahren.

Schauen wir uns einen anderen Ansatz an - **Organisation nach Domänen**:

/--pre
<b>app/Model/</b>
├── <b>Cart/</b>
├── <b>Payment/</b>
├── <b>Order/</b>
└── <b>Product/</b>
\--

Dies ist anders - auf den ersten Blick ist klar, dass es sich um eine E-Commerce-Site handelt. Die Verzeichnisnamen selbst verraten, was die Anwendung kann - sie arbeitet mit Zahlungen, Bestellungen und Produkten.

Der erste Ansatz (Organisation nach Klassentyp) bringt in der Praxis mehrere Probleme mit sich: Logisch zusammenhängender Code ist über verschiedene Ordner verstreut und man muss zwischen ihnen hin- und herspringen. Daher werden wir nach Domänen organisieren.


Namespaces .[#toc-namespaces]
-----------------------------

Es ist üblich, dass die Verzeichnisstruktur den Namensräumen in der Anwendung entspricht. Das bedeutet, dass der physische Speicherort von Dateien ihrem Namensraum entspricht. Zum Beispiel sollte eine Klasse, die sich in `app/Model/Product/ProductRepository.php` befindet, den Namensraum `App\Model\Product` haben. Dieses Prinzip hilft bei der Orientierung im Code und vereinfacht das automatische Laden.


Singular vs. Plural in Namen .[#toc-singular-vs-plural-in-names]
----------------------------------------------------------------

Beachten Sie, dass wir für die Hauptanwendungsverzeichnisse Singular verwenden: `app`, `config`, `log`, `temp`, `www`. Das Gleiche gilt innerhalb der Anwendung: `Model`, `Core`, `Presentation`. Der Grund dafür ist, dass jeder Begriff ein einheitliches Konzept darstellt.

In ähnlicher Weise repräsentiert `app/Model/Product` alles über Produkte. Wir nennen ihn nicht `Products`, weil es sich nicht um einen Ordner voller Produkte handelt (der Dateien wie `iphone.php`, `samsung.php` enthalten würde). Es ist ein Namensraum, der Klassen für die Arbeit mit Produkten enthält - `ProductRepository.php`, `ProductService.php`.

Der Ordner `app/Tasks` ist plural, weil er eine Reihe von eigenständigen ausführbaren Skripten enthält - `CleanupTask.php`, `ImportTask.php`. Jedes dieser Skripte ist eine unabhängige Einheit.

Aus Gründen der Konsistenz empfehlen wir die Verwendung von:
- Singular für Namensräume, die eine funktionale Einheit darstellen (auch wenn mit mehreren Entitäten gearbeitet wird)
- Plural für Sammlungen von unabhängigen Einheiten
- Bei Unsicherheiten oder wenn Sie nicht darüber nachdenken wollen, wählen Sie Singular


Öffentliches Verzeichnis `www/` .[#toc-public-directory-www]
============================================================

Dieses Verzeichnis ist das einzige, das vom Web aus zugänglich ist (sogenanntes Document-Root). Sie werden oft den Namen `public/` anstelle von `www/` finden - das ist nur eine Frage der Konvention und hat keinen Einfluss auf die Funktionalität. Das Verzeichnis enthält:
- [Einstiegspunkt |bootstrap#index.php] der Anwendung `index.php`
- `.htaccess` Datei mit mod_rewrite Regeln (für Apache)
- Statische Dateien (CSS, JavaScript, Bilder)
- Hochgeladene Dateien

Für die Sicherheit der Anwendung ist es wichtig, dass [die Dokumenten-Wurzel |nette:troubleshooting#how-to-change-or-remove-www-directory-from-url] korrekt [konfiguriert ist |nette:troubleshooting#how-to-change-or-remove-www-directory-from-url].

.[note]
Legen Sie niemals den Ordner `node_modules/` in dieses Verzeichnis - er enthält Tausende von Dateien, die ausführbar sein können und nicht öffentlich zugänglich sein sollten.


Anwendungsverzeichnis `app/` .[#toc-application-directory-app]
==============================================================

Dies ist das Hauptverzeichnis mit dem Anwendungscode. Grundlegende Struktur:

/--pre
<b>app/</b>
├── <b>Core/</b>               ← Infrastrukturfragen
├── <b>Model/</b>              ← Geschäftslogik
├── <b>Presentation/</b>       ← Präsentatoren und Vorlagen
├── <b>Tasks/</b>              ← Befehlsskripte
└── <b>Bootstrap.php</b>       ← Anwendungs-Bootstrap-Klasse
\--

`Bootstrap.php` ist die [Startklasse der Anwendung |bootstrap], die die Umgebung initialisiert, die Konfiguration lädt und den DI-Container erstellt.

Schauen wir uns nun die einzelnen Unterverzeichnisse im Detail an.


Präsentatoren und Vorlagen .[#toc-presenters-and-templates]
===========================================================

Wir haben den Präsentationsteil der Anwendung im Verzeichnis `app/Presentation`. Eine Alternative ist das kurze `app/UI`. Dies ist der Ort für alle Präsentatoren, ihre Vorlagen und alle Hilfsklassen.

Wir organisieren diese Schicht nach Domänen. In einem komplexen Projekt, das E-Commerce, Blog und API kombiniert, würde die Struktur wie folgt aussehen:

/--pre
<b>app/Presentation/</b>
├── <b>Shop/</b>              ← E-Commerce-Frontend
│   ├── <b>Product/</b>
│   ├── <b>Cart/</b>
│   └── <b>Order/</b>
├── <b>Blog/</b>              ← Blog
│   ├── <b>Home/</b>
│   └── <b>Post/</b>
├── <b>Admin/</b>             ← Verwaltung
│   ├── <b>Dashboard/</b>
│   └── <b>Products/</b>
└── <b>Api/</b>               ← API-Endpunkte
	└── <b>V1/</b>
\--

Für ein einfaches Blog hingegen würden wir diese Struktur verwenden:

/--pre
<b>app/Presentation/</b>
├── <b>Front/</b>             ← Website-Frontend
│   ├── <b>Home/</b>
│   └── <b>Post/</b>
├── <b>Admin/</b>             ← Verwaltung
│   ├── <b>Dashboard/</b>
│   └── <b>Posts/</b>
├── <b>Error/</b>
└── <b>Export/</b>            ← RSS, Sitemaps usw.
\--

Ordner wie `Home/` oder `Dashboard/` enthalten Moderatoren und Vorlagen. Ordner wie `Front/`, `Admin/` oder `Api/` werden **Module** genannt. Technisch gesehen sind dies reguläre Verzeichnisse, die der logischen Organisation der Anwendung dienen.

Jeder Ordner mit einem Presenter enthält einen ähnlich benannten Presenter und dessen Vorlagen. Zum Beispiel enthält der Ordner `Dashboard/`:

/--pre
<b>Dashboard/</b>
├── <b>DashboardPresenter.php</b>     ← Präsentator
└── <b>default.latte</b>              ← Vorlage
\--

Diese Verzeichnisstruktur spiegelt sich in den Namespaces der Klassen wider. So befindet sich z. B. `DashboardPresenter` im Namensraum `App\Presentation\Admin\Dashboard` (siehe [Presenter-Zuordnung |#presenter mapping]):

```php
namespace App\Presentation\Admin\Dashboard;

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

Wir bezeichnen den Presenter `Dashboard` innerhalb des Moduls `Admin` in der Anwendung mit der Doppelpunktschreibweise als `Admin:Dashboard`. Auf seine `default` Aktion dann als `Admin:Dashboard:default`. Für verschachtelte Module verwenden wir mehr Doppelpunkte, zum Beispiel `Shop:Order:Detail:default`.


Flexible Strukturentwicklung .[#toc-flexible-structure-development]
-------------------------------------------------------------------

Einer der großen Vorteile dieser Struktur ist, dass sie sich elegant an wachsende Projektanforderungen anpassen lässt. Nehmen wir als Beispiel den Teil, der XML-Feeds erzeugt. Am Anfang haben wir ein einfaches Formular:

/--pre
<b>Export/</b>
├── <b>ExportPresenter.php</b>   ← ein Presenter für alle Exporte
├── <b>sitemap.latte</b>         ← Vorlage für Sitemap
└── <b>feed.latte</b>            ← Vorlage für RSS-Feed
\--

Mit der Zeit kommen weitere Feed-Typen hinzu, für die wir mehr Logik benötigen... Kein Problem! Der Ordner `Export/` wird einfach zu einem Modul:

/--pre
<b>Export/</b>
├── <b>Sitemap/</b>
│   ├── <b>SitemapPresenter.php</b>
│   └── <b>sitemap.latte</b>
└── <b>Feed/</b>
	├── <b>FeedPresenter.php</b>
	├── <b>amazon.latte</b>         ← Feed für Amazon
	└── <b>ebay.latte</b>           ← Feed für eBay
\--

Diese Umwandlung ist völlig problemlos - es müssen lediglich neue Unterordner erstellt, der Code in diese aufgeteilt und die Links aktualisiert werden (z. B. von `Export:feed` zu `Export:Feed:amazon`). Auf diese Weise können wir die Struktur schrittweise nach Bedarf erweitern, die Verschachtelungsebene ist in keiner Weise begrenzt.

Wenn Sie zum Beispiel in der Verwaltung viele Präsenter haben, die mit der Auftragsverwaltung zusammenhängen, wie `OrderDetail`, `OrderEdit`, `OrderDispatch` usw., können Sie zur besseren Organisation ein Modul (Ordner) `Order` erstellen, das (Ordner für) Präsenter `Detail`, `Edit`, `Dispatch` und andere enthält.


Standort der Vorlage .[#toc-template-location]
----------------------------------------------

In den vorangegangenen Beispielen haben wir gesehen, dass sich die Vorlagen direkt in dem Ordner mit dem Präsentator befinden:

/--pre
<b>Dashboard/</b>
├── <b>DashboardPresenter.php</b>     ← Präsentator
├── <b>DashboardTemplate.php</b>      ← optionale Vorlagenklasse
└── <b>default.latte</b>              ← Vorlage
\--

Dieser Speicherort erweist sich in der Praxis als der praktischste - Sie haben alle zugehörigen Dateien direkt zur Hand.

Alternativ können Sie die Vorlagen auch in einem Unterordner von `templates/` ablegen. Nette unterstützt beide Varianten. Sie können Vorlagen sogar komplett außerhalb des Ordners `Presentation/` ablegen. Alles über die Ablagemöglichkeiten von Vorlagen finden Sie im Kapitel [Vorlagen-Suche |templates#Template Lookup].


Hilfsklassen und Komponenten .[#toc-helper-classes-and-components]
------------------------------------------------------------------

Präsentatoren und Vorlagen werden oft mit anderen Hilfsdateien geliefert. Wir ordnen sie logisch nach ihrem Anwendungsbereich an:

1. **Direkt mit dem Präsentator**, wenn es sich um spezifische Komponenten für den jeweiligen Präsentator handelt:

/--pre
<b>Product/</b>
├── <b>ProductPresenter.php</b>
├── <b>ProductGrid.php</b>        ← Komponente für die Produktauflistung
└── <b>FilterForm.php</b>         ← Formular für die Filterung
\--

2. **Für Module** - wir empfehlen die Verwendung des Ordners `Accessory`, der ordentlich am Anfang des Alphabets platziert ist:

/--pre
<b>Front/</b>
├── <b>Accessory/</b>
│   ├── <b>NavbarControl.php</b>    ← Komponenten für das Frontend
│   └── <b>TemplateFilters.php</b>
├── <b>Product/</b>
└── <b>Cart/</b>
\--

3. **Für die gesamte Anwendung** - in `Presentation/Accessory/`:
/--pre
<b>app/Presentation/</b>
├── <b>Accessory/</b>
│   ├── <b>LatteExtension.php</b>
│   └── <b>TemplateFilters.php</b>
├── <b>Front/</b>
└── <b>Admin/</b>
\--

Oder Sie können Hilfsklassen wie `LatteExtension.php` oder `TemplateFilters.php` in den Infrastruktur-Ordner `app/Core/Latte/` legen. Und Komponenten in `app/Components`. Die Wahl hängt von den Konventionen des Teams ab.


Modell - Herzstück der Anwendung .[#toc-model-heart-of-the-application]
=======================================================================

Das Modell enthält die gesamte Geschäftslogik der Anwendung. Für seine Organisation gilt die gleiche Regel - wir strukturieren nach Domänen:

/--pre
<b>app/Model/</b>
├── <b>Payment/</b>                   ← alles über Zahlungen
│   ├── <b>PaymentFacade.php</b>      ← Haupteinstiegspunkt
│   ├── <b>PaymentRepository.php</b>
│   ├── <b>Payment.php</b>            ← Entität
├── <b>Order/</b>                     ← alles über Bestellungen
│   ├── <b>OrderFacade.php</b>
│   ├── <b>OrderRepository.php</b>
│   ├── <b>Order.php</b>
└── <b>Shipping/</b>                  ← alles über den Versand
\--

Im Modell finden Sie typischerweise diese Arten von Klassen:

**Fassaden**: stellen den Haupteinstiegspunkt in einen bestimmten Bereich der Anwendung dar. Sie fungieren als Orchestrator, der die Zusammenarbeit zwischen verschiedenen Diensten koordiniert, um vollständige Anwendungsfälle zu implementieren (wie "Bestellung erstellen" oder "Zahlung verarbeiten"). Unter ihrer Orchestrierungsschicht verbirgt die Fassade Implementierungsdetails vor dem Rest der Anwendung und bietet so eine saubere Schnittstelle für die Arbeit mit der jeweiligen Domäne.

```php
class OrderFacade
{
	public function createOrder(Cart $cart): Order
	{
		// Validierung
		// Auftragserstellung
		// E-Mail-Versand
		// Schreiben in die Statistik
	}
}
```

**Dienste**: konzentrieren sich auf spezifische Geschäftsvorgänge innerhalb einer Domäne. Im Gegensatz zu Fassaden, die ganze Anwendungsfälle orchestrieren, implementiert ein Dienst spezifische Geschäftslogik (wie Preisberechnungen oder Zahlungsverarbeitung). Dienste sind in der Regel zustandslos und können entweder von Fassaden als Bausteine für komplexere Vorgänge oder direkt von anderen Teilen der Anwendung für einfachere Aufgaben verwendet werden.

```php
class PricingService
{
	public function calculateTotal(Order $order): Money
	{
		// Preiskalkulation
	}
}
```

**Repositories**: übernehmen die gesamte Kommunikation mit dem Datenspeicher, in der Regel einer Datenbank. Ihre Aufgabe ist es, Entitäten zu laden und zu speichern und Methoden für die Suche nach ihnen zu implementieren. Ein Repository schirmt den Rest der Anwendung von den Details der Datenbankimplementierung ab und bietet eine objektorientierte Schnittstelle für die Arbeit mit Daten.

```php
class OrderRepository
{
	public function find(int $id): ?Order
	{
	}

	public function findByCustomer(int $customerId): array
	{
	}
}
```

**Entitäten**: Objekte, die die wichtigsten Geschäftskonzepte in der Anwendung darstellen, die ihre Identität haben und sich im Laufe der Zeit ändern. In der Regel handelt es sich um Klassen, die mithilfe von ORM (wie Nette Database Explorer oder Doctrine) auf Datenbanktabellen abgebildet werden. Entitäten können Geschäftsregeln für ihre Daten und Validierungslogik enthalten.

```php
// Entität, die der Datenbanktabelle Aufträge zugeordnet ist
class Order extends Nette\Database\Table\ActiveRow
{
	public function addItem(Product $product, int $quantity): void
	{
		$this->related('order_items')->insert([
			'product_id' => $product->id,
			'quantity' => $quantity,
			'unit_price' => $product->price,
		]);
	}
}
```

**Wertobjekte**: unveränderliche Objekte, die Werte ohne eigene Identität darstellen - zum Beispiel einen Geldbetrag oder eine E-Mail-Adresse. Zwei Instanzen eines Wertobjekts mit denselben Werten werden als identisch betrachtet.


Infrastruktur-Code .[#toc-infrastructure-code]
==============================================

Der Ordner `Core/` (oder auch `Infrastructure/`) beherbergt die technische Grundlage der Anwendung. Der Infrastrukturcode umfasst in der Regel:

/--pre
<b>app/Core/</b>
├── <b>Router/</b>               ← Routing und URL-Verwaltung
│   └── <b>RouterFactory.php</b>
├── <b>Security/</b>             ← Authentifizierung und Autorisierung
│   ├── <b>Authenticator.php</b>
│   └── <b>Authorizator.php</b>
├── <b>Logging/</b>              ← Protokollierung und Überwachung
│   ├── <b>SentryLogger.php</b>
│   └── <b>FileLogger.php
├── <b>Cache/</b>                ← Caching-Schicht
│   └── <b>FullPageCache.php</b>
└── <b>Integration/</b>          ← Integration mit externen Diensten
	├── <b>Slack/</b>
	└── <b>Stripe/</b>
\--

Für kleinere Projekte ist eine flache Struktur natürlich ausreichend:

/--pre
<b>Core/</b>
├── <b>RouterFactory.php</b>
├── <b>Authenticator.php</b>
└── <b>QueueMailer.php</b>
\--

Dies ist Code, der:

- die technische Infrastruktur verwaltet (Routing, Protokollierung, Caching)
- Integration externer Dienste (Sentry, Elasticsearch, Redis)
- grundlegende Dienste für die gesamte Anwendung bereitstellt (Mail, Datenbank)
- weitgehend unabhängig von der jeweiligen Domäne ist - Cache oder Logger funktionieren für E-Commerce oder Blog gleichermaßen.

Sie fragen sich, ob eine bestimmte Klasse hierher oder in das Modell gehört? Der Hauptunterschied ist, dass der Code in `Core/`:

- Er weiß nichts über die Domäne (Produkte, Bestellungen, Artikel)
- Kann normalerweise in ein anderes Projekt übertragen werden
- Löst die Frage, "wie es funktioniert" (wie man Mails verschickt), nicht "was es tut" (welche Mails verschickt werden sollen)

Beispiel zum besseren Verständnis:

- `App\Core\MailerFactory` - erstellt Instanzen der E-Mail-Versandklasse, verwaltet SMTP-Einstellungen
- `App\Model\OrderMailer` - verwendet `MailerFactory`, um E-Mails über Bestellungen zu versenden, kennt deren Vorlagen und weiß, wann sie versendet werden sollen


Befehlsskripte .[#toc-command-scripts]
======================================

Anwendungen müssen oft Aufgaben außerhalb der regulären HTTP-Anfragen ausführen - sei es die Datenverarbeitung im Hintergrund, die Wartung oder regelmäßige Aufgaben. Einfache Skripte im Verzeichnis `bin/` werden zur Ausführung verwendet, während die eigentliche Implementierungslogik in `app/Tasks/` (oder `app/Commands/`) untergebracht ist.

Beispiel:

/--pre
<b>app/Tasks/</b>
├── <b>Maintenance/</b>               ← Wartungsskripte
│   ├── <b>CleanupCommand.php</b>     ← Löschen von alten Daten
│   └── <b>DbOptimizeCommand.php</b>  ← Optimierung der Datenbank
├── <b>Integration/</b>               ← Integration mit externen Systemen
│   ├── <b>ImportProducts.php</b>     ← Import aus Lieferantensystemen
│   └── <b>SyncOrders.php</b>         ← Bestellsynchronisation
└── <b>Scheduled/</b>                 ← Regelmäßige Aufgaben
	├── <b>NewsletterCommand.php</b>  ← Versand von Newslettern
	└── <b>ReminderCommand.php</b>    ← Kundenbenachrichtigungen
\--

Was gehört in das Modell und was in die Befehlsskripte? Zum Beispiel ist die Logik für den Versand einer E-Mail Teil des Modells, der Massenversand von Tausenden von E-Mails gehört in `Tasks/`.

Aufgaben werden in der Regel [über die Befehlszeile |https://blog.nette.org/en/cli-scripts-in-nette-application] oder über Cron [ausgeführt |https://blog.nette.org/en/cli-scripts-in-nette-application]. Sie können auch über eine HTTP-Anfrage ausgeführt werden, aber die Sicherheit muss berücksichtigt werden. Der Präsentator, der die Aufgabe ausführt, muss gesichert werden, z. B. nur für angemeldete Benutzer oder mit einem starken Token und Zugriff von erlaubten IP-Adressen. Bei langen Aufgaben muss das Zeitlimit für das Skript erhöht und `session_write_close()` verwendet werden, um ein Sperren der Sitzung zu vermeiden.


Andere mögliche Verzeichnisse .[#toc-other-possible-directories]
================================================================

Zusätzlich zu den genannten Basisverzeichnissen können Sie je nach Projektbedarf weitere spezialisierte Verzeichnisse hinzufügen. Schauen wir uns die gängigsten Verzeichnisse und ihre Verwendung an:

/--pre
<b>app/</b>
├── <b>Api/</b>              ← API-Logik unabhängig von der Präsentationsschicht
├── <b>Database/</b>         ← Migrationsskripte und Seeder für Testdaten
├── <b>Components/</b>       ← gemeinsame visuelle Komponenten für die gesamte Anwendung
├── <b>Event/</b>            ← nützlich bei Verwendung einer ereignisgesteuerten Architektur
├── <b>Mail/</b>             ← E-Mail-Vorlagen und zugehörige Logik
└── <b>Utils/</b>            ← Hilfsklassen
\--

Für gemeinsam genutzte visuelle Komponenten, die in Präsentatoren in der gesamten Anwendung verwendet werden, können Sie den Ordner `app/Components` oder `app/Controls` verwenden:

/--pre
<b>app/Components/</b>
├── <b>Form/</b>                 ← gemeinsame Formular-Komponenten
│   ├── <b>SignInForm.php</b>
│   └── <b>UserForm.php</b>
├── <b>Grid/</b>                 ← Komponenten für Datenauflistungen
│   └── <b>DataGrid.php</b>
└── <b>Navigation/</b>           ← Navigationselemente
	├── <b>Breadcrumbs.php</b>
	└── <b>Menu.php</b>
\--

Dorthin gehören Komponenten mit komplexerer Logik. Wenn Sie Komponenten für mehrere Projekte gemeinsam nutzen möchten, sollten Sie sie in ein eigenständiges Composer-Paket aufteilen.

Im Verzeichnis `app/Mail` können Sie die Verwaltung der E-Mail-Kommunikation unterbringen:

/--pre
<b>app/Mail/</b>
├── <b>templates/</b>            ← E-Mail-Vorlagen
│   ├── <b>order-confirmation.latte</b>
│   └── <b>welcome.latte</b>
└── <b>OrderMailer.php</b>
\--


Präsentator-Mapping .[#toc-presenter-mapping]
=============================================

Mapping definiert Regeln für die Ableitung von Klassennamen aus Presenter-Namen. Wir geben sie in der [Konfiguration |configuration] unter dem Schlüssel `application › mapping` an.

Auf dieser Seite haben wir gezeigt, dass wir Presenter im Ordner `app/Presentation` (oder `app/UI`) ablegen. Wir müssen Nette über diese Konvention in der Konfigurationsdatei informieren. Eine Zeile reicht aus:

```neon
application:
	mapping: App\Presentation\*\**Presenter
```

Wie funktioniert das Mapping? Um das besser zu verstehen, stellen wir uns zunächst eine Anwendung ohne Module vor. Wir möchten, dass die Presenter-Klassen in den Namensraum `App\Presentation` fallen, so dass der Presenter `Home` auf die Klasse `App\Presentation\HomePresenter` abgebildet wird. Dies wird mit dieser Konfiguration erreicht:

```neon
application:
	mapping: App\Presentation\*Presenter
```

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

Wie Sie jedoch in den Beispielen in diesem und anderen Kapiteln sehen, platzieren wir Presenter-Klassen in gleichnamigen Unterverzeichnissen, z. B. wird der Presenter `Home` der Klasse `App\Presentation\Home\HomePresenter` zugeordnet. Wir erreichen dies, indem wir den Doppelpunkt verdoppeln (erfordert Nette Application 3.2):

```neon
application:
	mapping: App\Presentation\**Presenter
```

Nun gehen wir dazu über, Presenter in Modulen abzubilden. Wir können für jedes Modul eine spezifische Zuordnung definieren:

```neon
application:
	mapping:
		Front: App\Presentation\Front\**Presenter
		Admin: App\Presentation\Admin\**Presenter
		Api: App\Api\*Presenter
```

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

Da die Module `Front` und `Admin` eine ähnliche Zuordnungsmethode haben und es wahrscheinlich noch mehr solcher Module geben wird, ist es möglich, eine allgemeine Regel zu erstellen, die sie ersetzen wird. Ein neues Sternchen für das Modul wird der Klassenmaske hinzugefügt:

```neon
application:
	mapping:
		*: App\Presentation\*\**Presenter
		Api: App\Api\*Presenter
```

Dies funktioniert auch für tiefer verschachtelte Verzeichnisstrukturen, wie z. B. Presenter `Admin:User:Edit`, wo das Segment mit Sternchen für jede Ebene wiederholt wird und die Klasse `App\Presentation\Admin\User\Edit\EditPresenter` ergibt.

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

```neon
application:
	mapping:
		*: [App\Presentation, *, **Presenter]
		Api: [App\Api, '', *Presenter]
```

Verzeichnisstruktur der Anwendung

Wie entwirft man eine klare und skalierbare Verzeichnisstruktur für Projekte in Nette Framework? Wir zeigen Ihnen bewährte Verfahren, die Ihnen helfen, Ihren Code zu organisieren. Sie werden es lernen:

  • wie Sie Ihre Anwendung logisch in Verzeichnisse strukturieren
  • wie man die Struktur so gestaltet, dass sie bei wachsendem Projekt gut skalierbar ist
  • was die möglichen Alternativen sind und welche Vor- und Nachteile sie haben

Es ist wichtig zu erwähnen, dass das Nette Framework selbst nicht auf einer bestimmten Struktur besteht. Es ist so konzipiert, dass es sich leicht an alle Bedürfnisse und Vorlieben anpassen lässt.

Grundlegende Projektstruktur

Obwohl Nette Framework keine feste Verzeichnisstruktur vorgibt, gibt es eine bewährte Standardanordnung in Form von Web Project:

web-project/
├── app/              ← Anwendungsverzeichnis
├── assets/           ← SCSS, JS-Dateien, Bilder..., alternativ resources/
├── bin/              ← Befehlszeilenskripte
├── config/           ← Konfiguration
├── log/              ← protokollierte Fehler
├── temp/             ← temporäre Dateien, Cache
├── tests/            ← Tests
├── vendor/           ← vom Composer installierte Bibliotheken
└── www/              ← öffentliches Verzeichnis (document-root)

Sie können diese Struktur frei nach Ihren Bedürfnissen verändern – Ordner umbenennen oder verschieben. Dann müssen Sie nur die relativen Pfade zu den Verzeichnissen in Bootstrap.php und eventuell composer.json anpassen. Mehr ist nicht nötig, keine komplexe Neukonfiguration, keine ständigen Änderungen. Nette verfügt über eine intelligente automatische Erkennung und erkennt automatisch den Speicherort der Anwendung einschließlich ihrer URL-Basis.

Grundsätze der Code-Organisation

Wenn Sie zum ersten Mal ein neues Projekt erkunden, sollten Sie in der Lage sein, sich schnell zu orientieren. Stellen Sie sich vor, Sie klicken auf das Verzeichnis app/Model/ und sehen diese Struktur:

app/Model/
├── Services/
├── Repositories/
└── Entities/

Daraus erfahren Sie nur, dass das Projekt einige Dienste, Repositories und Entitäten verwendet. Sie werden nichts über den eigentlichen Zweck der Anwendung erfahren.

Schauen wir uns einen anderen Ansatz an – Organisation nach Domänen:

app/Model/
├── Cart/
├── Payment/
├── Order/
└── Product/

Dies ist anders – auf den ersten Blick ist klar, dass es sich um eine E-Commerce-Site handelt. Die Verzeichnisnamen selbst verraten, was die Anwendung kann – sie arbeitet mit Zahlungen, Bestellungen und Produkten.

Der erste Ansatz (Organisation nach Klassentyp) bringt in der Praxis mehrere Probleme mit sich: Logisch zusammenhängender Code ist über verschiedene Ordner verstreut und man muss zwischen ihnen hin- und herspringen. Daher werden wir nach Domänen organisieren.

Namespaces

Es ist üblich, dass die Verzeichnisstruktur den Namensräumen in der Anwendung entspricht. Das bedeutet, dass der physische Speicherort von Dateien ihrem Namensraum entspricht. Zum Beispiel sollte eine Klasse, die sich in app/Model/Product/ProductRepository.php befindet, den Namensraum App\Model\Product haben. Dieses Prinzip hilft bei der Orientierung im Code und vereinfacht das automatische Laden.

Singular vs. Plural in Namen

Beachten Sie, dass wir für die Hauptanwendungsverzeichnisse Singular verwenden: app, config, log, temp, www. Das Gleiche gilt innerhalb der Anwendung: Model, Core, Presentation. Der Grund dafür ist, dass jeder Begriff ein einheitliches Konzept darstellt.

In ähnlicher Weise repräsentiert app/Model/Product alles über Produkte. Wir nennen ihn nicht Products, weil es sich nicht um einen Ordner voller Produkte handelt (der Dateien wie iphone.php, samsung.php enthalten würde). Es ist ein Namensraum, der Klassen für die Arbeit mit Produkten enthält – ProductRepository.php, ProductService.php.

Der Ordner app/Tasks ist plural, weil er eine Reihe von eigenständigen ausführbaren Skripten enthält – CleanupTask.php, ImportTask.php. Jedes dieser Skripte ist eine unabhängige Einheit.

Aus Gründen der Konsistenz empfehlen wir die Verwendung von:

  • Singular für Namensräume, die eine funktionale Einheit darstellen (auch wenn mit mehreren Entitäten gearbeitet wird)
  • Plural für Sammlungen von unabhängigen Einheiten
  • Bei Unsicherheiten oder wenn Sie nicht darüber nachdenken wollen, wählen Sie Singular

Öffentliches Verzeichnis www/

Dieses Verzeichnis ist das einzige, das vom Web aus zugänglich ist (sogenanntes Document-Root). Sie werden oft den Namen public/ anstelle von www/ finden – das ist nur eine Frage der Konvention und hat keinen Einfluss auf die Funktionalität. Das Verzeichnis enthält:

  • Einstiegspunkt der Anwendung index.php
  • .htaccess Datei mit mod_rewrite Regeln (für Apache)
  • Statische Dateien (CSS, JavaScript, Bilder)
  • Hochgeladene Dateien

Für die Sicherheit der Anwendung ist es wichtig, dass die Dokumenten-Wurzel korrekt konfiguriert ist.

Legen Sie niemals den Ordner node_modules/ in dieses Verzeichnis – er enthält Tausende von Dateien, die ausführbar sein können und nicht öffentlich zugänglich sein sollten.

Anwendungsverzeichnis app/

Dies ist das Hauptverzeichnis mit dem Anwendungscode. Grundlegende Struktur:

app/
├── Core/               ← Infrastrukturfragen
├── Model/              ← Geschäftslogik
├── Presentation/       ← Präsentatoren und Vorlagen
├── Tasks/              ← Befehlsskripte
└── Bootstrap.php       ← Anwendungs-Bootstrap-Klasse

Bootstrap.php ist die Startklasse der Anwendung, die die Umgebung initialisiert, die Konfiguration lädt und den DI-Container erstellt.

Schauen wir uns nun die einzelnen Unterverzeichnisse im Detail an.

Präsentatoren und Vorlagen

Wir haben den Präsentationsteil der Anwendung im Verzeichnis app/Presentation. Eine Alternative ist das kurze app/UI. Dies ist der Ort für alle Präsentatoren, ihre Vorlagen und alle Hilfsklassen.

Wir organisieren diese Schicht nach Domänen. In einem komplexen Projekt, das E-Commerce, Blog und API kombiniert, würde die Struktur wie folgt aussehen:

app/Presentation/
├── Shop/              ← E-Commerce-Frontend
│   ├── Product/
│   ├── Cart/
│   └── Order/
├── Blog/              ← Blog
│   ├── Home/
│   └── Post/
├── Admin/             ← Verwaltung
│   ├── Dashboard/
│   └── Products/
└── Api/               ← API-Endpunkte
	└── V1/

Für ein einfaches Blog hingegen würden wir diese Struktur verwenden:

app/Presentation/
├── Front/             ← Website-Frontend
│   ├── Home/
│   └── Post/
├── Admin/             ← Verwaltung
│   ├── Dashboard/
│   └── Posts/
├── Error/
└── Export/            ← RSS, Sitemaps usw.

Ordner wie Home/ oder Dashboard/ enthalten Moderatoren und Vorlagen. Ordner wie Front/, Admin/ oder Api/ werden Module genannt. Technisch gesehen sind dies reguläre Verzeichnisse, die der logischen Organisation der Anwendung dienen.

Jeder Ordner mit einem Presenter enthält einen ähnlich benannten Presenter und dessen Vorlagen. Zum Beispiel enthält der Ordner Dashboard/:

Dashboard/
├── DashboardPresenter.php     ← Präsentator
└── default.latte              ← Vorlage

Diese Verzeichnisstruktur spiegelt sich in den Namespaces der Klassen wider. So befindet sich z. B. DashboardPresenter im Namensraum App\Presentation\Admin\Dashboard (siehe Presenter-Zuordnung):

namespace App\Presentation\Admin\Dashboard;

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

Wir bezeichnen den Presenter Dashboard innerhalb des Moduls Admin in der Anwendung mit der Doppelpunktschreibweise als Admin:Dashboard. Auf seine default Aktion dann als Admin:Dashboard:default. Für verschachtelte Module verwenden wir mehr Doppelpunkte, zum Beispiel Shop:Order:Detail:default.

Flexible Strukturentwicklung

Einer der großen Vorteile dieser Struktur ist, dass sie sich elegant an wachsende Projektanforderungen anpassen lässt. Nehmen wir als Beispiel den Teil, der XML-Feeds erzeugt. Am Anfang haben wir ein einfaches Formular:

Export/
├── ExportPresenter.php   ← ein Presenter für alle Exporte
├── sitemap.latte         ← Vorlage für Sitemap
└── feed.latte            ← Vorlage für RSS-Feed

Mit der Zeit kommen weitere Feed-Typen hinzu, für die wir mehr Logik benötigen… Kein Problem! Der Ordner Export/ wird einfach zu einem Modul:

Export/
├── Sitemap/
│   ├── SitemapPresenter.php
│   └── sitemap.latte
└── Feed/
	├── FeedPresenter.php
	├── amazon.latte         ← Feed für Amazon
	└── ebay.latte           ← Feed für eBay

Diese Umwandlung ist völlig problemlos – es müssen lediglich neue Unterordner erstellt, der Code in diese aufgeteilt und die Links aktualisiert werden (z. B. von Export:feed zu Export:Feed:amazon). Auf diese Weise können wir die Struktur schrittweise nach Bedarf erweitern, die Verschachtelungsebene ist in keiner Weise begrenzt.

Wenn Sie zum Beispiel in der Verwaltung viele Präsenter haben, die mit der Auftragsverwaltung zusammenhängen, wie OrderDetail, OrderEdit, OrderDispatch usw., können Sie zur besseren Organisation ein Modul (Ordner) Order erstellen, das (Ordner für) Präsenter Detail, Edit, Dispatch und andere enthält.

Standort der Vorlage

In den vorangegangenen Beispielen haben wir gesehen, dass sich die Vorlagen direkt in dem Ordner mit dem Präsentator befinden:

Dashboard/
├── DashboardPresenter.php     ← Präsentator
├── DashboardTemplate.php      ← optionale Vorlagenklasse
└── default.latte              ← Vorlage

Dieser Speicherort erweist sich in der Praxis als der praktischste – Sie haben alle zugehörigen Dateien direkt zur Hand.

Alternativ können Sie die Vorlagen auch in einem Unterordner von templates/ ablegen. Nette unterstützt beide Varianten. Sie können Vorlagen sogar komplett außerhalb des Ordners Presentation/ ablegen. Alles über die Ablagemöglichkeiten von Vorlagen finden Sie im Kapitel Vorlagen-Suche.

Hilfsklassen und Komponenten

Präsentatoren und Vorlagen werden oft mit anderen Hilfsdateien geliefert. Wir ordnen sie logisch nach ihrem Anwendungsbereich an:

1. Direkt mit dem Präsentator, wenn es sich um spezifische Komponenten für den jeweiligen Präsentator handelt:

Product/
├── ProductPresenter.php
├── ProductGrid.php        ← Komponente für die Produktauflistung
└── FilterForm.php         ← Formular für die Filterung

2. Für Module – wir empfehlen die Verwendung des Ordners Accessory, der ordentlich am Anfang des Alphabets platziert ist:

Front/
├── Accessory/
│   ├── NavbarControl.php    ← Komponenten für das Frontend
│   └── TemplateFilters.php
├── Product/
└── Cart/

3. Für die gesamte Anwendung – in Presentation/Accessory/:

app/Presentation/
├── Accessory/
│   ├── LatteExtension.php
│   └── TemplateFilters.php
├── Front/
└── Admin/

Oder Sie können Hilfsklassen wie LatteExtension.php oder TemplateFilters.php in den Infrastruktur-Ordner app/Core/Latte/ legen. Und Komponenten in app/Components. Die Wahl hängt von den Konventionen des Teams ab.

Modell – Herzstück der Anwendung

Das Modell enthält die gesamte Geschäftslogik der Anwendung. Für seine Organisation gilt die gleiche Regel – wir strukturieren nach Domänen:

app/Model/
├── Payment/                   ← alles über Zahlungen
│   ├── PaymentFacade.php      ← Haupteinstiegspunkt
│   ├── PaymentRepository.php
│   ├── Payment.php            ← Entität
├── Order/                     ← alles über Bestellungen
│   ├── OrderFacade.php
│   ├── OrderRepository.php
│   ├── Order.php
└── Shipping/                  ← alles über den Versand

Im Modell finden Sie typischerweise diese Arten von Klassen:

Fassaden: stellen den Haupteinstiegspunkt in einen bestimmten Bereich der Anwendung dar. Sie fungieren als Orchestrator, der die Zusammenarbeit zwischen verschiedenen Diensten koordiniert, um vollständige Anwendungsfälle zu implementieren (wie „Bestellung erstellen“ oder „Zahlung verarbeiten“). Unter ihrer Orchestrierungsschicht verbirgt die Fassade Implementierungsdetails vor dem Rest der Anwendung und bietet so eine saubere Schnittstelle für die Arbeit mit der jeweiligen Domäne.

class OrderFacade
{
	public function createOrder(Cart $cart): Order
	{
		// Validierung
		// Auftragserstellung
		// E-Mail-Versand
		// Schreiben in die Statistik
	}
}

Dienste: konzentrieren sich auf spezifische Geschäftsvorgänge innerhalb einer Domäne. Im Gegensatz zu Fassaden, die ganze Anwendungsfälle orchestrieren, implementiert ein Dienst spezifische Geschäftslogik (wie Preisberechnungen oder Zahlungsverarbeitung). Dienste sind in der Regel zustandslos und können entweder von Fassaden als Bausteine für komplexere Vorgänge oder direkt von anderen Teilen der Anwendung für einfachere Aufgaben verwendet werden.

class PricingService
{
	public function calculateTotal(Order $order): Money
	{
		// Preiskalkulation
	}
}

Repositories: übernehmen die gesamte Kommunikation mit dem Datenspeicher, in der Regel einer Datenbank. Ihre Aufgabe ist es, Entitäten zu laden und zu speichern und Methoden für die Suche nach ihnen zu implementieren. Ein Repository schirmt den Rest der Anwendung von den Details der Datenbankimplementierung ab und bietet eine objektorientierte Schnittstelle für die Arbeit mit Daten.

class OrderRepository
{
	public function find(int $id): ?Order
	{
	}

	public function findByCustomer(int $customerId): array
	{
	}
}

Entitäten: Objekte, die die wichtigsten Geschäftskonzepte in der Anwendung darstellen, die ihre Identität haben und sich im Laufe der Zeit ändern. In der Regel handelt es sich um Klassen, die mithilfe von ORM (wie Nette Database Explorer oder Doctrine) auf Datenbanktabellen abgebildet werden. Entitäten können Geschäftsregeln für ihre Daten und Validierungslogik enthalten.

// Entität, die der Datenbanktabelle Aufträge zugeordnet ist
class Order extends Nette\Database\Table\ActiveRow
{
	public function addItem(Product $product, int $quantity): void
	{
		$this->related('order_items')->insert([
			'product_id' => $product->id,
			'quantity' => $quantity,
			'unit_price' => $product->price,
		]);
	}
}

Wertobjekte: unveränderliche Objekte, die Werte ohne eigene Identität darstellen – zum Beispiel einen Geldbetrag oder eine E-Mail-Adresse. Zwei Instanzen eines Wertobjekts mit denselben Werten werden als identisch betrachtet.

Infrastruktur-Code

Der Ordner Core/ (oder auch Infrastructure/) beherbergt die technische Grundlage der Anwendung. Der Infrastrukturcode umfasst in der Regel:

app/Core/
├── Router/               ← Routing und URL-Verwaltung
│   └── RouterFactory.php
├── Security/             ← Authentifizierung und Autorisierung
│   ├── Authenticator.php
│   └── Authorizator.php
├── Logging/              ← Protokollierung und Überwachung
│   ├── SentryLogger.php
│   └── FileLogger.php
├── Cache/                ← Caching-Schicht
│   └── FullPageCache.php
└── Integration/          ← Integration mit externen Diensten
	├── Slack/
	└── Stripe/

Für kleinere Projekte ist eine flache Struktur natürlich ausreichend:

Core/
├── RouterFactory.php
├── Authenticator.php
└── QueueMailer.php

Dies ist Code, der:

  • die technische Infrastruktur verwaltet (Routing, Protokollierung, Caching)
  • Integration externer Dienste (Sentry, Elasticsearch, Redis)
  • grundlegende Dienste für die gesamte Anwendung bereitstellt (Mail, Datenbank)
  • weitgehend unabhängig von der jeweiligen Domäne ist – Cache oder Logger funktionieren für E-Commerce oder Blog gleichermaßen.

Sie fragen sich, ob eine bestimmte Klasse hierher oder in das Modell gehört? Der Hauptunterschied ist, dass der Code in Core/:

  • Er weiß nichts über die Domäne (Produkte, Bestellungen, Artikel)
  • Kann normalerweise in ein anderes Projekt übertragen werden
  • Löst die Frage, „wie es funktioniert“ (wie man Mails verschickt), nicht „was es tut“ (welche Mails verschickt werden sollen)

Beispiel zum besseren Verständnis:

  • App\Core\MailerFactory – erstellt Instanzen der E-Mail-Versandklasse, verwaltet SMTP-Einstellungen
  • App\Model\OrderMailer – verwendet MailerFactory, um E-Mails über Bestellungen zu versenden, kennt deren Vorlagen und weiß, wann sie versendet werden sollen

Befehlsskripte

Anwendungen müssen oft Aufgaben außerhalb der regulären HTTP-Anfragen ausführen – sei es die Datenverarbeitung im Hintergrund, die Wartung oder regelmäßige Aufgaben. Einfache Skripte im Verzeichnis bin/ werden zur Ausführung verwendet, während die eigentliche Implementierungslogik in app/Tasks/ (oder app/Commands/) untergebracht ist.

Beispiel:

app/Tasks/
├── Maintenance/               ← Wartungsskripte
│   ├── CleanupCommand.php     ← Löschen von alten Daten
│   └── DbOptimizeCommand.php  ← Optimierung der Datenbank
├── Integration/               ← Integration mit externen Systemen
│   ├── ImportProducts.php     ← Import aus Lieferantensystemen
│   └── SyncOrders.php         ← Bestellsynchronisation
└── Scheduled/                 ← Regelmäßige Aufgaben
	├── NewsletterCommand.php  ← Versand von Newslettern
	└── ReminderCommand.php    ← Kundenbenachrichtigungen

Was gehört in das Modell und was in die Befehlsskripte? Zum Beispiel ist die Logik für den Versand einer E-Mail Teil des Modells, der Massenversand von Tausenden von E-Mails gehört in Tasks/.

Aufgaben werden in der Regel über die Befehlszeile oder über Cron ausgeführt. Sie können auch über eine HTTP-Anfrage ausgeführt werden, aber die Sicherheit muss berücksichtigt werden. Der Präsentator, der die Aufgabe ausführt, muss gesichert werden, z. B. nur für angemeldete Benutzer oder mit einem starken Token und Zugriff von erlaubten IP-Adressen. Bei langen Aufgaben muss das Zeitlimit für das Skript erhöht und session_write_close() verwendet werden, um ein Sperren der Sitzung zu vermeiden.

Andere mögliche Verzeichnisse

Zusätzlich zu den genannten Basisverzeichnissen können Sie je nach Projektbedarf weitere spezialisierte Verzeichnisse hinzufügen. Schauen wir uns die gängigsten Verzeichnisse und ihre Verwendung an:

app/
├── Api/              ← API-Logik unabhängig von der Präsentationsschicht
├── Database/         ← Migrationsskripte und Seeder für Testdaten
├── Components/       ← gemeinsame visuelle Komponenten für die gesamte Anwendung
├── Event/            ← nützlich bei Verwendung einer ereignisgesteuerten Architektur
├── Mail/             ← E-Mail-Vorlagen und zugehörige Logik
└── Utils/            ← Hilfsklassen

Für gemeinsam genutzte visuelle Komponenten, die in Präsentatoren in der gesamten Anwendung verwendet werden, können Sie den Ordner app/Components oder app/Controls verwenden:

app/Components/
├── Form/                 ← gemeinsame Formular-Komponenten
│   ├── SignInForm.php
│   └── UserForm.php
├── Grid/                 ← Komponenten für Datenauflistungen
│   └── DataGrid.php
└── Navigation/           ← Navigationselemente
	├── Breadcrumbs.php
	└── Menu.php

Dorthin gehören Komponenten mit komplexerer Logik. Wenn Sie Komponenten für mehrere Projekte gemeinsam nutzen möchten, sollten Sie sie in ein eigenständiges Composer-Paket aufteilen.

Im Verzeichnis app/Mail können Sie die Verwaltung der E-Mail-Kommunikation unterbringen:

app/Mail/
├── templates/            ← E-Mail-Vorlagen
│   ├── order-confirmation.latte
│   └── welcome.latte
└── OrderMailer.php

Präsentator-Mapping

Mapping definiert Regeln für die Ableitung von Klassennamen aus Presenter-Namen. Wir geben sie in der Konfiguration unter dem Schlüssel application › mapping an.

Auf dieser Seite haben wir gezeigt, dass wir Presenter im Ordner app/Presentation (oder app/UI) ablegen. Wir müssen Nette über diese Konvention in der Konfigurationsdatei informieren. Eine Zeile reicht aus:

application:
	mapping: App\Presentation\*\**Presenter

Wie funktioniert das Mapping? Um das besser zu verstehen, stellen wir uns zunächst eine Anwendung ohne Module vor. Wir möchten, dass die Presenter-Klassen in den Namensraum App\Presentation fallen, so dass der Presenter Home auf die Klasse App\Presentation\HomePresenter abgebildet wird. Dies wird mit dieser Konfiguration erreicht:

application:
	mapping: App\Presentation\*Presenter

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

Wie Sie jedoch in den Beispielen in diesem und anderen Kapiteln sehen, platzieren wir Presenter-Klassen in gleichnamigen Unterverzeichnissen, z. B. wird der Presenter Home der Klasse App\Presentation\Home\HomePresenter zugeordnet. Wir erreichen dies, indem wir den Doppelpunkt verdoppeln (erfordert Nette Application 3.2):

application:
	mapping: App\Presentation\**Presenter

Nun gehen wir dazu über, Presenter in Modulen abzubilden. Wir können für jedes Modul eine spezifische Zuordnung definieren:

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

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

Da die Module Front und Admin eine ähnliche Zuordnungsmethode haben und es wahrscheinlich noch mehr solcher Module geben wird, ist es möglich, eine allgemeine Regel zu erstellen, die sie ersetzen wird. Ein neues Sternchen für das Modul wird der Klassenmaske hinzugefügt:

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

Dies funktioniert auch für tiefer verschachtelte Verzeichnisstrukturen, wie z. B. Presenter Admin:User:Edit, wo das Segment mit Sternchen für jede Ebene wiederholt wird und die Klasse App\Presentation\Admin\User\Edit\EditPresenter ergibt.

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

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