` er
```
-Dynamische Schnipsel .[#toc-dynamic-snippets]
-=============================================
+Schnipsel Bereiche .[#toc-snippet-areas]
+----------------------------------------
-In Nette können Sie auch Snippets mit einem dynamischen Namen definieren, der auf einem Laufzeitparameter basiert. Dies eignet sich besonders für verschiedene Listen, bei denen nur eine Zeile geändert werden muss, aber nicht die ganze Liste mit übertragen werden soll. Ein Beispiel hierfür wäre:
+Snippet-Namen können auch Ausdrücke sein:
```latte
-
- {foreach $list as $id => $item}
- - {$item} update
- {/foreach}
-
+{foreach $items as $id => $item}
+
{$item}
+{/foreach}
```
-Es gibt ein statisches Snippet namens `itemsContainer`, das mehrere dynamische Snippets enthält: `item-0`, `item-1` und so weiter.
+Auf diese Weise erhalten wir mehrere Snippets wie `item-0`, `item-1`, usw. Wenn wir einen dynamischen Ausschnitt (z. B. `item-1`) direkt ungültig machen würden, würde nichts neu gezeichnet werden. Der Grund dafür ist, dass Snippets als echte Auszüge funktionieren und nur sie selbst direkt gerendert werden. In der Vorlage gibt es jedoch technisch gesehen kein Snippet namens `item-1`. Es taucht nur auf, wenn der umgebende Code des Snippets ausgeführt wird, in diesem Fall die foreach-Schleife. Daher markieren wir den Teil der Vorlage, der ausgeführt werden muss, mit dem Tag `{snippetArea}`:
-Ein dynamisches Snippet kann nicht direkt neu gezeichnet werden (das erneute Zeichnen von `item-1` hat keine Auswirkung), Sie müssen das übergeordnete Snippet (in diesem Beispiel `itemsContainer`) neu zeichnen. Dies bewirkt, dass der Code des übergeordneten Snippets ausgeführt wird, aber nur seine Unter-Snippets an den Browser gesendet werden. Wenn Sie nur einen der Teil-Snippets senden wollen, müssen Sie die Eingabe für das übergeordnete Snippet so ändern, dass die anderen Teil-Snippets nicht erzeugt werden.
+```latte
+
+ {foreach $items as $id => $item}
+ - {$item}
+ {/foreach}
+
+```
-Im obigen Beispiel müssen Sie sicherstellen, dass bei einer AJAX-Anfrage nur ein Element zum Array `$list` hinzugefügt wird, so dass die Schleife `foreach` nur ein dynamisches Snippet ausgibt.
+Und wir werden sowohl das einzelne Snippet als auch den gesamten übergreifenden Bereich neu zeichnen:
```php
-class HomePresenter extends Nette\Application\UI\Presenter
-{
- /**
- * This method returns data for the list.
- * Usually this would just request the data from a model.
- * For the purpose of this example, the data is hard-coded.
- */
- private function getTheWholeList(): array
- {
- return [
- 'First',
- 'Second',
- 'Third',
- ];
- }
-
- public function renderDefault(): void
- {
- if (!isset($this->template->list)) {
- $this->template->list = $this->getTheWholeList();
- }
- }
-
- public function handleUpdate(int $id): void
- {
- $this->template->list = $this->isAjax()
- ? []
- : $this->getTheWholeList();
- $this->template->list[$id] = 'Updated item';
- $this->redrawControl('itemsContainer');
- }
-}
+$this->redrawControl('itemsContainer');
+$this->redrawControl('item-1');
```
+Es ist auch wichtig, dass das Array `$items` nur die Elemente enthält, die neu gezeichnet werden sollen.
-Snippets in einer eingebundenen Vorlage .[#toc-snippets-in-an-included-template]
-================================================================================
-
-Es kann vorkommen, dass sich das Snippet in einer Vorlage befindet, die von einer anderen Vorlage eingebunden wird. In diesem Fall müssen wir den Einbindungscode in der zweiten Vorlage mit dem `snippetArea` -Makro einschließen, dann zeichnen wir sowohl den Snippet-Bereich als auch das eigentliche Snippet neu.
-
-Das Makro `snippetArea` sorgt dafür, dass der darin enthaltene Code ausgeführt wird, aber nur das eigentliche Snippet in der eingebundenen Vorlage an den Browser gesendet wird.
+Beim Einfügen einer anderen Vorlage in die Hauptvorlage unter Verwendung des `{include}` -Tags, das Snippets enthält, muss die eingefügte Vorlage erneut in ein `snippetArea` -Tag eingeschlossen und sowohl das Snippet als auch der Bereich gemeinsam ungültig gemacht werden:
```latte
-{* parent.latte *}
-{snippetArea wrapper}
- {include 'child.latte'}
+{snippetArea include}
+ {include 'included.latte'}
{/snippetArea}
```
+
```latte
-{* child.latte *}
+{* enthalten.latte *}
{snippet item}
-...
+ ...
{/snippet}
```
+
```php
-$this->redrawControl('wrapper');
+$this->redrawControl('include');
$this->redrawControl('item');
```
-Sie können es auch mit dynamischen Snippets kombinieren.
-
-
-Hinzufügen und Löschen .[#toc-adding-and-deleting]
-==================================================
-Wenn Sie ein neues Element in die Liste einfügen und `itemsContainer` ungültig machen, liefert die AJAX-Anfrage Snippets, die das neue Element enthalten, aber der Javascript-Handler kann es nicht darstellen. Das liegt daran, dass es kein HTML-Element mit der neu erstellten ID gibt.
+Schnipsel in Komponenten .[#toc-snippets-in-components]
+-------------------------------------------------------
-In diesem Fall besteht die einfachste Möglichkeit darin, die gesamte Liste in ein weiteres Snippet zu verpacken und alles ungültig zu machen:
+Sie können Snippets in [Komponenten |components] erstellen, und Nette wird sie automatisch neu zeichnen. Es gibt jedoch eine bestimmte Einschränkung: Um Snippets neu zu zeichnen, wird die Methode `render()` ohne Parameter aufgerufen. Die Übergabe von Parametern in der Vorlage funktioniert also nicht:
```latte
-{snippet wholeList}
-
- {foreach $list as $id => $item}
- - {$item} update
- {/foreach}
-
-{/snippet}
-
Add
+OK
+{control productGrid}
+
+will not work:
+{control productGrid $arg, $arg}
+{control productGrid:paginator}
```
+
+Senden von Benutzerdaten .[#toc-sending-user-data]
+--------------------------------------------------
+
+Neben den Snippets können Sie beliebige weitere Daten an den Client senden. Schreiben Sie sie einfach in das `payload` Objekt:
+
```php
-public function handleAdd(): void
+public function actionDelete(int $id): void
{
- $this->template->list = $this->getTheWholeList();
- $this->template->list[] = 'New one';
- $this->redrawControl('wholeList');
+ //...
+ if ($this->isAjax()) {
+ $this->payload->message = 'Success';
+ }
}
```
-Das Gleiche gilt für das Löschen eines Eintrags. Es wäre möglich, ein leeres Snippet zu senden, aber in der Regel können Listen paginiert werden, und es wäre kompliziert, das Löschen eines Elements und das Laden eines anderen (das sich auf einer anderen Seite der paginierten Liste befand) zu implementieren.
-
-Parameter an die Komponente senden .[#toc-sending-parameters-to-component]
-==========================================================================
+Senden von Parametern .[#toc-sending-parameters]
+================================================
Wenn wir Parameter über eine AJAX-Anfrage an die Komponente senden, egal ob es sich um Signalparameter oder dauerhafte Parameter handelt, müssen wir ihren globalen Namen angeben, der auch den Namen der Komponente enthält. Den vollständigen Namen des Parameters gibt die Methode `getParameterId()` zurück.
```js
-$.getJSON(
- {link changeCountBasket!},
- {
- {$control->getParameterId('id')}: id,
- {$control->getParameterId('count')}: count
- }
-});
+let url = new URL({link //foo!});
+url.searchParams.set({$control->getParameterId('bar')}, bar);
+
+fetch(url, {
+ headers: {'X-Requested-With': 'XMLHttpRequest'},
+})
```
-Und behandeln Sie die Methode mit den entsprechenden Parametern in der Komponente.
+Eine Handle-Methode mit den entsprechenden Parametern in der Komponente:
```php
-public function handleChangeCountBasket(int $id, int $count): void
+public function handleFoo(int $bar): void
{
-
}
```
diff --git a/application/de/bootstrap.texy b/application/de/bootstrap.texy
index 590e4e76ef..bc84d8e61a 100644
--- a/application/de/bootstrap.texy
+++ b/application/de/bootstrap.texy
@@ -20,18 +20,44 @@ use Nette\Bootstrap\Configurator;
class Bootstrap
{
- public static function boot(): Configurator
+ private Configurator $configurator;
+ private string $rootDir;
+
+ public function __construct()
+ {
+ $this->rootDir = dirname(__DIR__);
+ // Der Konfigurator ist für das Einrichten der Anwendungsumgebung und der Dienste zuständig.
+ $this->configurator = new Configurator;
+ // Legen Sie das Verzeichnis für temporäre Dateien fest, die von Nette erzeugt werden (z. B. kompilierte Vorlagen)
+ $this->configurator->setTempDirectory($this->rootDir . '/temp');
+ }
+
+ public function bootWebApplication(): Nette\DI\Container
+ {
+ $this->initializeEnvironment();
+ $this->setupContainer();
+ return $this->configurator->createContainer();
+ }
+
+ private function initializeEnvironment(): void
{
- $appDir = dirname(__DIR__);
- $configurator = new Configurator;
- //$configurator->setDebugMode('secret@23.75.345.200');
- $configurator->enableTracy($appDir . '/log');
- $configurator->setTempDirectory($appDir . '/temp');
- $configurator->createRobotLoader()
+ // Nette ist intelligent, und der Entwicklungsmodus wird automatisch aktiviert,
+ // oder Sie können ihn für eine bestimmte IP-Adresse aktivieren, indem Sie die folgende Zeile auskommentieren:
+ // $this->configurator->setDebugMode('secret@23.75.345.200');
+
+ // Aktiviert Tracy: das ultimative "Schweizer Taschenmesser" zur Fehlersuche.
+ $this->configurator->enableTracy($this->rootDir . '/log');
+
+ // RobotLoader: Lädt automatisch alle Klassen im angegebenen Verzeichnis
+ $this->configurator->createRobotLoader()
->addDirectory(__DIR__)
->register();
- $configurator->addConfig($appDir . '/config/common.neon');
- return $configurator;
+ }
+
+ private function setupContainer(): void
+ {
+ // Konfigurationsdateien laden
+ $this->configurator->addConfig($this->rootDir . '/config/common.neon');
}
}
```
@@ -40,16 +66,15 @@ class Bootstrap
index.php .[#toc-index-php]
===========================
-Im Falle von Webanwendungen ist die Ausgangsdatei `index.php`, die sich im öffentlichen Verzeichnis `www/` befindet. Sie überlässt es der Klasse `Bootstrap`, die Umgebung zu initialisieren und den `$configurator` zurückzugeben, der den DI-Container erstellt. Dann wird der Dienst `Application` aufgerufen, der die Webanwendung ausführt:
+Im Falle von Webanwendungen ist die primäre Datei `index.php`, die sich im [öffentlichen Verzeichnis |directory-structure#public-directory-www] `www/` befindet. Dadurch wird die Bootstrap-Klasse die Umgebung initialisieren und einen DI-Container erzeugen. Von diesem wird dann der Dienst `Application` abgerufen, der die Webanwendung startet:
```php
-// Initialisieren der Umgebung + Abrufen des Configurator-Objekts
-$configurator = App\Bootstrap::boot();
-// Erstellen eines DI-Containers
-$container = $configurator->createContainer();
-// DI-Container erstellt ein Nette\Application\Application-Objekt
+$bootstrap = new App\Bootstrap;
+// Initialisierung der Umgebung + Erstellung eines DI-Containers
+$container = $bootstrap->bootWebApplication();
+// DI-Container erstellt ein Nette\Anwendung\Anwendungsobjekt
$application = $container->getByType(Nette\Application\Application::class);
-// Nette-Anwendung starten
+// Starten Sie die Nette-Anwendung und bearbeiten Sie die eingehende Anfrage
$application->run();
```
@@ -59,26 +84,42 @@ Wie Sie sehen, hilft die Klasse [api:Nette\Bootstrap\Configurator], die wir nun
Entwicklungs- vs. Produktionsmodus .[#toc-development-vs-production-mode]
=========================================================================
-Nette unterscheidet zwischen zwei grundlegenden Modi, in denen eine Anfrage ausgeführt wird: Entwicklungs- und Produktionsmodus. Der Entwicklungsmodus ist auf maximalen Komfort für den Programmierer ausgerichtet, Tracy wird angezeigt, der Cache wird automatisch aktualisiert, wenn Vorlagen oder die DI-Containerkonfiguration geändert werden, usw. Im Produktionsmodus liegt der Schwerpunkt auf der Leistung, Tracy protokolliert nur Fehler und Änderungen an Vorlagen und anderen Dateien werden nicht überprüft.
+Nette verhält sich unterschiedlich, je nachdem, ob es auf einem Entwicklungs- oder Produktionsserver ausgeführt wird:
+
+🛠️ Entwicklungsmodus:
+ - Zeigt die Tracy-Debug-Leiste mit nützlichen Informationen an (z. B. SQL-Abfragen, Ausführungszeit, Speicherverbrauch).
+ - Zeigt eine detaillierte Fehlerseite mit Funktionsaufrufspuren und Variableninhalten, wenn ein Fehler auftritt.
+ - Aktualisiert automatisch den Cache, wenn Latte-Vorlagen, Konfigurationsdateien usw. geändert werden.
+
+
+🚀 Produktionsmodus:
+ - Es werden keine Debugging-Informationen angezeigt; alle Fehler werden protokolliert.
+ - Zeigt eine `ErrorPresenter` oder eine allgemeine "Server Error"-Seite an, wenn ein Fehler auftritt.
+ - Der Cache wird nie automatisch aktualisiert!
+ - Optimiert für Geschwindigkeit und Sicherheit.
-Die Auswahl des Modus erfolgt durch automatische Erkennung, so dass in der Regel keine Notwendigkeit besteht, etwas manuell zu konfigurieren oder umzuschalten. Der Modus ist Entwicklung, wenn die Anwendung auf localhost läuft (d.h. IP-Adresse `127.0.0.1` oder `::1`) und kein Proxy vorhanden ist (d.h. sein HTTP-Header). Ansonsten läuft sie im Produktionsmodus.
+
+Der Modus wird automatisch bestimmt, so dass es in den meisten Fällen nicht notwendig ist, ihn manuell zu konfigurieren oder zu wechseln:
+
+- Entwicklungsmodus: Aktiv auf localhost (IP-Adresse `127.0.0.1` oder `::1`), es sei denn, es wird ein Proxy verwendet (d. h. basierend auf den HTTP-Headern).
+- Produktionsmodus: Überall sonst aktiv.
Wenn Sie den Entwicklungsmodus in anderen Fällen aktivieren möchten, z. B. für Programmierer, die von einer bestimmten IP-Adresse aus zugreifen, können Sie `setDebugMode()` verwenden:
```php
-$configurator->setDebugMode('23.75.345.200'); // eine oder mehrere IP-Adressen
+$this->configurator->setDebugMode('23.75.345.200'); // eine oder mehrere IP-Adressen
```
Wir empfehlen auf jeden Fall, eine IP-Adresse mit einem Cookie zu kombinieren. Wir speichern ein geheimes Token im `nette-debug` Cookie, z.B. `secret1234`, und der Entwicklungsmodus wird für Programmierer mit dieser Kombination von IP und Cookie aktiviert.
```php
-$configurator->setDebugMode('secret1234@23.75.345.200');
+$this->configurator->setDebugMode('secret1234@23.75.345.200');
```
Wir können den Entwicklermodus auch komplett abschalten, sogar für localhost:
```php
-$configurator->setDebugMode(false);
+$this->configurator->setDebugMode(false);
```
Beachten Sie, dass der Wert `true` den Entwicklermodus standardmäßig einschaltet, was auf einem Produktionsserver niemals passieren sollte.
@@ -90,7 +131,7 @@ Debugging-Werkzeug Tracy .[#toc-debugging-tool-tracy]
Zur einfachen Fehlersuche werden wir das großartige Tool [Tracy |tracy:] einschalten. Im Entwicklermodus zeigt es Fehler an und im Produktionsmodus protokolliert es Fehler in das angegebene Verzeichnis:
```php
-$configurator->enableTracy($appDir . '/log');
+$this->configurator->enableTracy($this->rootDir . '/log');
```
@@ -100,7 +141,7 @@ Temporäre Dateien .[#toc-temporary-files]
Nette verwendet den Cache für DI-Container, RobotLoader, Vorlagen usw. Daher ist es notwendig, den Pfad zu dem Verzeichnis festzulegen, in dem der Cache gespeichert werden soll:
```php
-$configurator->setTempDirectory($appDir . '/temp');
+$this->configurator->setTempDirectory($this->rootDir . '/temp');
```
Unter Linux oder macOS setzen Sie die [Schreibrechte |nette:troubleshooting#Setting directory permissions] für die Verzeichnisse `log/` und `temp/`.
@@ -112,7 +153,7 @@ RobotLoader .[#toc-robotloader]
Normalerweise wollen wir die Klassen automatisch mit [RobotLoader |robot-loader:] laden, also müssen wir ihn starten und ihn Klassen aus dem Verzeichnis laden lassen, in dem sich `Bootstrap.php` befindet (d.h. `__DIR__`) und aus allen seinen Unterverzeichnissen:
```php
-$configurator->createRobotLoader()
+$this->configurator->createRobotLoader()
->addDirectory(__DIR__)
->register();
```
@@ -126,7 +167,7 @@ Zeitzone .[#toc-timezone]
Configurator ermöglicht es Ihnen, eine Zeitzone für Ihre Anwendung festzulegen.
```php
-$configurator->setTimeZone('Europe/Prague');
+$this->configurator->setTimeZone('Europe/Prague');
```
@@ -143,16 +184,17 @@ Im Entwicklungsmodus wird der Container jedes Mal automatisch aktualisiert, wenn
Konfigurationsdateien werden mit `addConfig()` geladen:
```php
-$configurator->addConfig($appDir . '/config/common.neon');
+$this->configurator->addConfig($this->rootDir . '/config/common.neon');
```
Die Methode `addConfig()` kann mehrfach aufgerufen werden, um mehrere Dateien hinzuzufügen.
```php
-$configurator->addConfig($appDir . '/config/common.neon');
-$configurator->addConfig($appDir . '/config/local.neon');
+$configDir = $this->rootDir . '/config';
+$this->configurator->addConfig($configDir . '/common.neon');
+$this->configurator->addConfig($configDir . '/services.neon');
if (PHP_SAPI === 'cli') {
- $configurator->addConfig($appDir . '/config/cli.php');
+ $this->configurator->addConfig($configDir . '/cli.php');
}
```
@@ -169,7 +211,7 @@ Statische Parameter .[#toc-static-parameters]
Parameter, die in Konfigurationsdateien verwendet werden, können [im Abschnitt `parameters` |dependency-injection:configuration#parameters] definiert und auch von der Methode `addStaticParameters()` übergeben (oder überschrieben) werden (sie hat den Alias `addParameters()`). Wichtig ist, dass unterschiedliche Parameterwerte die Erzeugung zusätzlicher DI-Container, d.h. zusätzlicher Klassen, bewirken.
```php
-$configurator->addStaticParameters([
+$this->configurator->addStaticParameters([
'projectId' => 23,
]);
```
@@ -183,7 +225,7 @@ Dynamische Parameter .[#toc-dynamic-parameters]
Wir können dem Container auch dynamische Parameter hinzufügen, deren unterschiedliche Werte, im Gegensatz zu statischen Parametern, nicht die Erzeugung neuer DI-Container verursachen.
```php
-$configurator->addDynamicParameters([
+$this->configurator->addDynamicParameters([
'remoteIp' => $_SERVER['REMOTE_ADDR'],
]);
```
@@ -191,7 +233,7 @@ $configurator->addDynamicParameters([
Umgebungsvariablen können mit dynamischen Parametern leicht verfügbar gemacht werden. Wir können über `%env.variable%` in Konfigurationsdateien auf sie zugreifen.
```php
-$configurator->addDynamicParameters([
+$this->configurator->addDynamicParameters([
'env' => getenv(),
]);
```
@@ -206,6 +248,7 @@ Sie können die folgenden statischen Parameter in den Konfigurationsdateien verw
- `%wwwDir%` ist der absolute Pfad zu dem Verzeichnis, das die `index.php` Eintragsdatei enthält
- `%tempDir%` ist der absolute Pfad zu dem Verzeichnis für temporäre Dateien
- `%vendorDir%` ist der absolute Pfad zu dem Verzeichnis, in dem Composer die Bibliotheken installiert
+- `%rootDir%` ist der absolute Pfad zum Stammverzeichnis des Projekts
- `%debugMode%` gibt an, ob sich die Anwendung im Debug-Modus befindet
- `%consoleMode%` zeigt an, ob die Anfrage über die Befehlszeile kam
@@ -225,7 +268,7 @@ services:
Erstellen Sie eine neue Instanz und fügen Sie sie in Bootstrap ein:
```php
-$configurator->addServices([
+$this->configurator->addServices([
'myservice' => new App\Model\MyCustomService('foobar'),
]);
```
@@ -234,13 +277,21 @@ $configurator->addServices([
Verschiedene Umgebungen .[#toc-different-environments]
======================================================
-Es steht Ihnen frei, die Klasse `Bootstrap` an Ihre Bedürfnisse anzupassen. Sie können der Methode `boot()` Parameter hinzufügen, um Webprojekte zu unterscheiden, oder andere Methoden hinzufügen, wie `bootForTests()`, die die Umgebung für Unit-Tests initialisiert, `bootForCli()` für Skripte, die von der Befehlszeile aus aufgerufen werden, und so weiter.
+Zögern Sie nicht, die Klasse `Bootstrap` nach Ihren Bedürfnissen anzupassen. Sie können der Methode `bootWebApplication()` Parameter hinzufügen, um zwischen Webprojekten zu unterscheiden. Alternativ können Sie auch andere Methoden hinzufügen, z. B. `bootTestEnvironment()`, um die Umgebung für Unit-Tests zu initialisieren, `bootConsoleApplication()` für Skripte, die von der Befehlszeile aus aufgerufen werden, und so weiter.
```php
-public static function bootForTests(): Configurator
+public function bootTestEnvironment(): Nette\DI\Container
+{
+ Tester\Environment::setup(); // Initialisierung des Nette-Testers
+ $this->setupContainer();
+ return $this->configurator->createContainer();
+}
+
+public function bootConsoleApplication(): Nette\DI\Container
{
- $configurator = self::boot();
- Tester\Environment::setup(); // Nette Tester Initialisierung
- return $configurator;
+ $this->configurator->setDebugMode(false);
+ $this->initializeEnvironment();
+ $this->setupContainer();
+ return $this->configurator->createContainer();
}
```
diff --git a/application/de/components.texy b/application/de/components.texy
index b4540b4808..d47cc5bd78 100644
--- a/application/de/components.texy
+++ b/application/de/components.texy
@@ -230,6 +230,28 @@ In der Vorlage stehen diese Meldungen in der Variablen `$flashes` als Objekte `s
```
+Umleitung nach einem Signal .[#toc-redirection-after-a-signal]
+==============================================================
+
+Nach der Verarbeitung eines Komponentensignals folgt oft eine Umleitung. Diese Situation ist ähnlich wie bei Formularen - nach dem Absenden eines Formulars leiten wir ebenfalls um, um eine erneute Übermittlung von Daten zu verhindern, wenn die Seite im Browser aktualisiert wird.
+
+```php
+$this->redirect('this') // redirects to the current presenter and action
+```
+
+Da eine Komponente ein wiederverwendbares Element ist und in der Regel keine direkte Abhängigkeit von bestimmten Presentern haben sollte, interpretieren die Methoden `redirect()` und `link()` den Parameter automatisch als Komponentensignal:
+
+```php
+$this->redirect('click') // redirects to the 'click' signal of the same component
+```
+
+Wenn Sie zu einem anderen Präsentator oder einer Aktion umleiten müssen, können Sie dies über den Präsentator tun:
+
+```php
+$this->getPresenter()->redirect('Product:show'); // redirects to a different presenter/action
+```
+
+
Dauerhafte Parameter .[#toc-persistent-parameters]
==================================================
@@ -347,7 +369,7 @@ services:
Schließlich werden wir diese Fabrik in unserem Präsentator verwenden:
```php
-class PollPresenter extends Nette\UI\Application\Presenter
+class PollPresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private PollControlFactory $pollControlFactory,
@@ -380,7 +402,7 @@ Komponenten im Detail .[#toc-components-in-depth]
Komponenten in einer Nette-Anwendung sind die wiederverwendbaren Teile einer Web-Anwendung, die wir in Seiten einbetten, was das Thema dieses Kapitels ist. Was genau sind die Fähigkeiten einer solchen Komponente?
1) sie ist in einer Vorlage renderbar
-2) sie weiß, welcher Teil von ihr bei einer [AJAX-Anfrage |ajax#invalidation] gerendert werden soll (Snippets)
+2) es weiß, [welcher Teil von sich selbst |ajax#snippets] während einer AJAX-Anfrage zu rendern ist (Snippets)
3) er kann seinen Zustand in einer URL speichern (persistente Parameter)
4) es hat die Fähigkeit, auf Benutzeraktionen zu reagieren (Signale)
5) er erstellt eine hierarchische Struktur (wobei die Wurzel der Präsentator ist)
diff --git a/application/de/configuration.texy b/application/de/configuration.texy
index da3a0fcebf..de4d41b0b7 100644
--- a/application/de/configuration.texy
+++ b/application/de/configuration.texy
@@ -13,11 +13,15 @@ Anwendung:
# zeigt das Feld "Nette Anwendung" in Tracy BlueScreen?
debugger: ... # (bool) standardmäßig true
- # wird der error-presenter im Fehlerfall aufgerufen?
- catchExceptions: ... # (bool) steht im Produktionsmodus standardmäßig auf true
+ # wird error-presenter im Fehlerfall aufgerufen?
+ # hat nur im Entwicklermodus Auswirkungen
+ catchExceptions: ... # (bool) ist standardmäßig true
# Name des Fehlermelders
- errorPresenter: Error # (string) Standardwert ist 'Nette:Error'
+ errorPresenter: Error # (string|array) Standardwert ist 'Nette:Error'
+
+ # definiert Aliase für Moderatoren und Veranstaltungen
+ aliases: ...
# definiert die Regeln für die Auflösung des Presenter-Namens in eine Klasse
mapping: ...
@@ -27,11 +31,20 @@ Anwendung:
silentLinks: ... # (bool) ist standardmäßig auf false eingestellt
```
-Da Fehlerpräsenter im Entwicklungsmodus standardmäßig nicht aufgerufen werden und die Fehler von Tracy angezeigt werden, hilft das Ändern des Wertes `catchExceptions` in `true` dabei, zu überprüfen, ob die Fehlerpräsenter während der Entwicklung korrekt funktionieren.
+Ab `nette/application` Version 3.2 ist es möglich, ein Paar von Fehlerpräsentern zu definieren:
+
+```neon
+application:
+ errorPresenter:
+ 4xx: Error4xx # für Nette\Application\BadRequestException
+ 5xx: Error5xx # für andere Ausnahmen
+```
Die Option `silentLinks` legt fest, wie sich Nette im Entwicklermodus verhält, wenn die Link-Generierung fehlschlägt (z. B. weil kein Presenter vorhanden ist usw.). Der Standardwert `false` bedeutet, dass Nette `E_USER_WARNING` auslöst. Die Einstellung `true` unterdrückt diese Fehlermeldung. In einer Produktionsumgebung wird immer `E_USER_WARNING` aufgerufen. Wir können dieses Verhalten auch beeinflussen, indem wir die Presenter-Variable [$invalidLinkMode |creating-links#Invalid Links] setzen.
-Das [Mapping definiert die Regeln |modules#mapping], nach denen der Klassenname aus dem Presenter-Namen abgeleitet wird.
+[Aliasnamen vereinfachen das Aufsuchen |creating-links#aliases] häufig verwendeter Moderatoren.
+
+Die [Zuordnung definiert die Regeln |directory-structure#Presenter Mapping], nach denen der Klassenname aus dem Namen des Präsentators abgeleitet wird.
Automatische Registrierung von Präsentatoren .[#toc-automatic-registration-of-presenters]
@@ -82,6 +95,9 @@ Latte:
# aktiviert die [Überprüfung von generiertem Code |latte:develop#Checking Generated Code]
phpLinter: ... # (string) Voreinstellung ist null
+ # legt das Gebietsschema fest
+ locale: cs_CZ # (string) Voreinstellung ist null
+
# Klasse von $this->template
templateClass: App\MyTemplateClass # Standardwert ist Nette\Bridges\ApplicationLatte\DefaultTemplate
```
@@ -91,7 +107,7 @@ Wenn Sie Latte Version 3 verwenden, können Sie neue [Erweiterungen |latte:creat
```neon
latte:
extensions:
- - Latte\Essential\TranslatorExtension
+ - Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)
```
/--comment
diff --git a/application/de/creating-links.texy b/application/de/creating-links.texy
index 1ec3fbd1e9..4e4210e9a7 100644
--- a/application/de/creating-links.texy
+++ b/application/de/creating-links.texy
@@ -38,7 +38,7 @@ Es ist auch möglich, benannte Parameter zu übergeben. Der folgende Link überg
detail
```
-Wenn die Methode `ProductPresenter::renderShow()` nicht `$lang` in ihrer Signatur hat, kann sie den Wert des Parameters mit `$lang = $this->getParameter('lang')` lesen.
+Wenn die Methode `ProductPresenter::renderShow()` nicht `$lang` in ihrer Signatur hat, kann sie den Wert des Parameters über `$lang = $this->getParameter('lang')` oder über die [Eigenschaft |presenters#Request Parameters] abrufen.
Wenn die Parameter in einem Array gespeichert sind, können sie mit dem `...` -Operator (oder `(expand)` -Operator in Latte 2.x) expandiert werden:
@@ -103,7 +103,7 @@ Wenn die Aktion `default` lautet, können wir sie weglassen, aber der Doppelpunk
home
```
-Links können auch auf andere [Module |modules] verweisen. Hier unterscheidet man zwischen relativen und absoluten Links zu den Untermodulen. Das Prinzip ist analog zu Plattenpfaden, nur dass anstelle von Schrägstrichen Doppelpunkte stehen. Nehmen wir an, dass der eigentliche Präsentator Teil des Moduls `Front` ist, dann schreiben wir:
+Links können auch auf andere [Module |directory-structure#Presenters and Templates] verweisen. Dabei werden die Links in relativ zu den Untermodulen oder absolut unterschieden. Das Prinzip ist analog zu Plattenpfaden, nur dass anstelle von Schrägstrichen Doppelpunkte stehen. Nehmen wir an, dass der eigentliche Präsentator Teil des Moduls `Front` ist, dann schreiben wir:
```latte
link to Front:Shop:Product:show
@@ -140,7 +140,7 @@ Das Ziel `this` wird einen Link zur aktuellen Seite erstellen:
refresh
```
-Gleichzeitig werden alle Parameter, die in der Signatur des Befehls `render
()` oder `action()` Methode angegeben sind, übertragen. Wenn wir uns also auf den Seiten `Product:show` und `id:123` befinden, wird der Link zu `this` auch diesen Parameter übergeben.
+Gleichzeitig werden alle Parameter, die in der Signatur des Befehls `action()` oder `render()` Methode angegebenen Parameter, wenn die `action()` nicht definiert ist, übertragen. Wenn wir uns also auf den Seiten `Product:show` und `id:123` befinden, wird der Link zu `this` auch diesen Parameter übergeben.
Natürlich ist es auch möglich, die Parameter direkt anzugeben:
@@ -213,7 +213,7 @@ Da [Komponenten |components] separate, wiederverwendbare Einheiten sind, die kei
Wenn wir auf Präsentatoren in der Komponentenvorlage verlinken wollen, verwenden wir das Tag `{plink}`:
```latte
-home
+home
```
oder im Code
@@ -223,6 +223,30 @@ $this->getPresenter()->link('Home:default')
```
+Aliasnamen .[#toc-aliases]{data-version:v3.2.2}
+===============================================
+
+Manchmal ist es sinnvoll, einem Presenter:Action-Paar einen leicht zu merkenden Alias zuzuweisen. Zum Beispiel könnten Sie die Homepage `Front:Home:default` einfach als `home` oder `Admin:Dashboard:default` als `admin` benennen.
+
+Aliasnamen werden in der [Konfiguration |configuration] unter dem Schlüssel `application › aliases` definiert:
+
+```neon
+application:
+ aliases:
+ home: Front:Home:default
+ admin: Admin:Dashboard:default
+ sign: Front:Sign:in
+```
+
+In Links werden sie mit dem at-Symbol geschrieben, zum Beispiel:
+
+```latte
+administration
+```
+
+Sie werden in allen Methoden unterstützt, die mit Links arbeiten, wie `redirect()` und ähnliche.
+
+
Ungültige Links .[#toc-invalid-links]
=====================================
@@ -257,6 +281,6 @@ Wie erstellt man Links mit der Methode `link()` comfort, aber ohne die Anwesenhe
LinkGenerator ist ein Dienst, den Sie über den Konstruktor übergeben können und dann Links mit seiner Methode `link()` erstellen.
-Es gibt einen Unterschied zu Presentern. LinkGenerator erstellt alle Links als absolute URLs. Außerdem gibt es keinen "aktuellen Präsentator", so dass es nicht möglich ist, nur den Namen der Aktion `link('default')` oder die relativen Pfade zu den [Modulen |modules] anzugeben.
+Im Vergleich zu Präsentatoren gibt es einen Unterschied. LinkGenerator erstellt alle Links direkt als absolute URLs. Außerdem gibt es keinen "eigentlichen Präsentator", so dass Sie nicht einfach den Aktionsnamen `link('default')` als Ziel angeben oder relative Pfade zu Modulen auflisten können.
Ungültige Links führen immer zu `Nette\Application\UI\InvalidLinkException`.
diff --git a/application/de/directory-structure.texy b/application/de/directory-structure.texy
new file mode 100644
index 0000000000..fa8341ab7d
--- /dev/null
+++ b/application/de/directory-structure.texy
@@ -0,0 +1,526 @@
+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 .[#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
+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 .[#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
+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**:
+
+/--pre
+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 .[#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
+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 |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
+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:
+
+/--pre
+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/`:
+
+/--pre
+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 |#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
+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:
+
+/--pre
+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 .[#toc-template-location]
+----------------------------------------------
+
+In den vorangegangenen Beispielen haben wir gesehen, dass sich die Vorlagen direkt in dem Ordner mit dem Präsentator befinden:
+
+/--pre
+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 |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
+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:
+
+/--pre
+Front/
+├── Accessory/
+│ ├── NavbarControl.php ← Komponenten für das Frontend
+│ └── TemplateFilters.php
+├── Product/
+└── Cart/
+\--
+
+3. **Für die gesamte Anwendung** - in `Presentation/Accessory/`:
+/--pre
+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 .[#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
+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.
+
+```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
+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:
+
+/--pre
+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 .[#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
+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 |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
+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:
+
+/--pre
+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:
+
+/--pre
+app/Mail/
+├── templates/ ← E-Mail-Vorlagen
+│ ├── order-confirmation.latte
+│ └── welcome.latte
+└── OrderMailer.php
+\--
+
+
+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]
+```
diff --git a/application/de/how-it-works.texy b/application/de/how-it-works.texy
index 3ac55697e4..481efa71d8 100644
--- a/application/de/how-it-works.texy
+++ b/application/de/how-it-works.texy
@@ -22,18 +22,18 @@ Die Verzeichnisstruktur sieht in etwa so aus:
/--pre
web-project/
├── app/ ← Verzeichnis mit Anwendung
-│ ├── Presenters/ ← Presenter-Klassen
-│ │ ├── HomePresenter.php ← Home presenterklasse
-│ │ └── templates/ ← Vorlagenverzeichnis
-│ │ ├── @layout.latte ← Vorlage für gemeinsames Layout
-│ │ └── Home/ ← Vorlagen für Home-presenter
-│ │ └── default.latte ← Vorlage für Aktion `default`
-│ ├── Router/ ← Konfiguration von URL-Adressen
+│ ├── Core/ ← grundlegende notwendige Klassen
+│ │ └── RouterFactory.php ← Konfiguration der URL-Adressen
+│ ├── Presentation/ ← Moderatoren, Vorlagen & Co.
+│ │ ├── @layout.latte ← Vorlage für gemeinsames Layout
+│ │ └── Home/ ← Home Presenter Verzeichnis
+│ │ ├── HomePresenter.php ← Home Presenter Klasse
+│ │ └── default.latte ← Vorlage für Aktion default
│ └── Bootstrap.php ← bootende Klasse Bootstrap
├── bin/ ← Skripte für die Kommandozeile
├── config/ ← Konfigurationsdateien
│ ├── common.neon
-│ └── local.neon
+│ └── services.neon
├── log/ ← Fehlerprotokolle
├── temp/ ← temporäre Dateien, Cache, …
├── vendor/ ← vom Composer installierte Bibliotheken
@@ -45,9 +45,9 @@ Die Verzeichnisstruktur sieht in etwa so aus:
└── .htaccess ← verbietet den Zugriff auf alle Verzeichnisse außer www
\--
-Sie können die Verzeichnisstruktur beliebig ändern, Ordner umbenennen oder verschieben, und dann einfach die Pfade zu `log/` und `temp/` in der Datei `Bootstrap.php` und den Pfad zu dieser Datei in `composer.json` im Abschnitt `autoload` ändern. Nichts weiter, keine komplizierte Neukonfiguration, keine ständigen Änderungen. Nette hat eine [intelligente automatische Erkennung |bootstrap#development-vs-production-mode].
+Sie können die Verzeichnisstruktur nach Belieben ändern, Ordner umbenennen oder verschieben - es ist völlig flexibel. Nette verfügt außerdem über eine intelligente automatische Erkennung und erkennt automatisch den Speicherort der Anwendung einschließlich ihrer URL-Basis.
-Für etwas größere Anwendungen können wir Ordner mit Präsentatoren und Vorlagen in Unterverzeichnisse (auf der Festplatte) und in Namensräume (im Code) unterteilen, die wir [Module |modules] nennen.
+Bei etwas größeren Anwendungen können wir Präsentations- und Vorlagenordner in [Unterverzeichnissen |directory-structure#Presenters and templates] organisieren und Klassen in Namensräumen gruppieren, die wir Module nennen.
Das Verzeichnis `www/` ist das öffentliche Verzeichnis oder die Dokumenten-Wurzel des Projekts. Sie können es umbenennen, ohne etwas anderes auf der Anwendungsseite einstellen zu müssen. Sie müssen nur [das Hosting |nette:troubleshooting#How to change or remove www directory from URL] so [konfigurieren |nette:troubleshooting#How to change or remove www directory from URL], dass die Dokumentenwurzel in dieses Verzeichnis führt.
@@ -75,7 +75,7 @@ Seine Aufgabe ist:
Welche Art von Fabrik? Wir stellen keine Traktoren her, sondern Websites! Warten Sie, ich erkläre es Ihnen gleich.
-Mit "die Umgebung initialisieren" meinen wir zum Beispiel, dass [Tracy |tracy:] aktiviert wird, ein erstaunliches Werkzeug zur Protokollierung oder Visualisierung von Fehlern. Es protokolliert Fehler auf dem Produktionsserver und zeigt sie direkt auf dem Entwicklungsserver an. Daher muss bei der Initialisierung auch entschieden werden, ob die Site im Produktions- oder im Entwicklermodus läuft. Hierfür verwendet Nette eine automatische Erkennung: Wenn Sie die Site auf localhost ausführen, läuft sie im Entwicklermodus. Sie müssen nichts konfigurieren, und die Anwendung ist sowohl für die Entwicklung als auch für den Produktionseinsatz bereit. Diese Schritte werden im Kapitel über die [Bootstrap-Klasse |bootstrap] durchgeführt und ausführlich beschrieben.
+Mit "Umgebungsinitialisierung" meinen wir z. B. die Aktivierung von [Tracy |tracy:], einem fantastischen Werkzeug für die Protokollierung und Fehlervisualisierung. Auf Produktionsservern protokolliert es Fehler, während es sie auf Entwicklungsservern direkt anzeigt. Zur Initialisierung gehört also auch die Festlegung, ob die Website im Produktions- oder Entwicklungsmodus läuft. Hierfür verwendet Nette eine [intelligente automatische Erkennung |bootstrap#development-vs-production-mode]: Wenn Sie die Website auf localhost ausführen, läuft sie im Entwicklungsmodus. Es ist keine Konfiguration erforderlich, und die Anwendung ist sowohl für die Entwicklung als auch für den Produktionseinsatz bereit. Diese Schritte werden im Kapitel über die [Bootstrap-Klassen |bootstrap] durchgeführt und ausführlich beschrieben.
Der dritte Punkt (ja, wir haben den zweiten übersprungen, aber wir werden darauf zurückkommen) ist das Starten der Anwendung. Die Bearbeitung von HTTP-Anfragen in Nette erfolgt durch die Klasse `Nette\Application\Application` (im Folgenden `Application` genannt). Wenn wir also sagen "eine Anwendung starten", meinen wir den Aufruf einer Methode mit dem Namen `run()` auf einem Objekt dieser Klasse.
@@ -91,7 +91,7 @@ In Nette geschriebene Anwendungen sind in viele so genannte Presenter unterteilt
Die Anwendung beginnt damit, dass sie den so genannten Router bittet, zu entscheiden, an welchen der Presenter die aktuelle Anfrage zur Bearbeitung weitergeleitet werden soll. Der Router entscheidet, wer dafür zuständig ist. Er sieht sich die Eingabe-URL `https://example.com/product/123` handelt, der ein Produkt mit `id: 123` als Aktion an `show` weiterleiten möchte. Es ist eine gute Angewohnheit, ein durch einen Doppelpunkt getrenntes Paar aus Präsentator + Aktion als `Product:show` zu schreiben.
-Der Router verwandelt also die URL in ein Paar `Presenter:action` + Parameter, in unserem Fall `Product:show` + `id: 123`. Sie können sehen, wie ein Router in der Datei `app/Router/RouterFactory.php` aussieht, und wir werden ihn im Kapitel [Routing] ausführlich beschreiben.
+Der Router verwandelt also die URL in ein Paar `Presenter:action` + Parameter, in unserem Fall `Product:show` + `id: 123`. Sie können sehen, wie ein Router in der Datei `app/Core/RouterFactory.php` aussieht, und wir werden ihn im Kapitel [Routing] ausführlich beschreiben.
Machen wir weiter. Die Anwendung kennt bereits den Namen des Präsentators und kann fortfahren. Sie erstellt ein Objekt `ProductPresenter`, das den Code des Presenters `Product` darstellt. Genauer gesagt, sie bittet den DI-Container um die Erstellung des Presenters, denn die Erstellung von Objekten ist seine Aufgabe.
@@ -121,12 +121,9 @@ So wurde die Methode `renderShow(123)` aufgerufen, deren Code ein fiktives Beisp
Anschließend gibt der Präsentator die Antwort zurück. Dies kann eine HTML-Seite, ein Bild, ein XML-Dokument, das Senden einer Datei von der Festplatte, JSON oder die Routing zu einer anderen Seite sein. Wichtig ist, dass, wenn wir nicht ausdrücklich sagen, wie zu antworten ist (was bei `ProductPresenter` der Fall ist), die Antwort darin besteht, die Vorlage mit einer HTML-Seite wiederzugeben. Und warum? Nun, weil wir in 99 % der Fälle eine Vorlage zeichnen wollen, so dass der Präsentator dieses Verhalten als Standard annimmt und uns die Arbeit erleichtern will. Das ist der Punkt von Nette.
-Wir müssen nicht einmal angeben, welche Vorlage gezeichnet werden soll, er leitet den Pfad dorthin nach einer einfachen Logik ab. Im Fall von presenter `Product` und action `show` versucht er zu sehen, ob eine dieser Vorlagendateien relativ zu dem Verzeichnis existiert, in dem sich die Klasse `ProductPresenter` befindet:
+Wir müssen nicht einmal angeben, welche Vorlage gerendert werden soll; das Framework wird den Pfad selbst ermitteln. Im Fall der Aktion `show` versucht es einfach, die Vorlage `show.latte` im Verzeichnis mit der Klasse `ProductPresenter` zu laden. Es versucht auch, das Layout in der Datei `@layout.latte` zu finden (mehr über die [Vorlagensuche |templates#Template Lookup]).
-- `templates/Product/show.latte`
-- `templates/Product.show.latte`
-
-Außerdem wird versucht, das Layout in der Datei `@layout.latte` zu finden, und dann wird die Vorlage gerendert. Nun ist die Aufgabe des Präsentators und der gesamten Anwendung abgeschlossen. Wenn die Vorlage nicht existiert, wird eine Seite mit dem Fehler 404 zurückgegeben. Weitere Informationen über Präsentatoren finden Sie auf der Seite [Präsentatoren |Presenters].
+Anschließend werden die Vorlagen gerendert. Damit ist die Aufgabe des Präsentators und der gesamten Anwendung abgeschlossen, und die Arbeit ist getan. Wenn die Vorlage nicht vorhanden wäre, würde eine 404-Fehlerseite zurückgegeben werden. Weitere Informationen über Präsentatoren finden Sie auf der Seite [Präsentatoren |presenters].
[* request-flow.svg *]
@@ -137,7 +134,7 @@ Um sicherzugehen, versuchen wir, den gesamten Prozess mit einer etwas anderen UR
3) der Router dekodiert die URL als ein Paar `Home:default`
4) ein `HomePresenter` Objekt wird erstellt
5) die Methode `renderDefault()` wird aufgerufen (falls vorhanden)
-6) eine Vorlage `templates/Home/default.latte` mit einem Layout `templates/@layout.latte` wird gerendert
+6) eine Vorlage `default.latte` mit einem Layout `@layout.latte` wird gerendert
Vielleicht sind Sie jetzt auf eine Menge neuer Konzepte gestoßen, aber wir glauben, dass sie sinnvoll sind. Das Erstellen von Anwendungen in Nette ist ein Kinderspiel.
diff --git a/application/de/modules.texy b/application/de/modules.texy
deleted file mode 100644
index fcdf4331c4..0000000000
--- a/application/de/modules.texy
+++ /dev/null
@@ -1,148 +0,0 @@
-Module
-******
-
-.[perex]
-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`.
-
-/--pre
-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:
-
-```php
-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 |#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 .[#toc-nested-modules]
---------------------------------------------
-
-Module müssen nicht nur eine flache Struktur bilden, Sie können auch Untermodule erstellen, zum Beispiel:
-
-/--pre
-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.
-
-
-Erstellen von Links .[#toc-creating-links]
-------------------------------------------
-
-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:
-
-```latte
-link to Front:Product:show
-```
-
-Ein Link ist auch dann relativ, wenn er den Namen eines Moduls enthält, das dann als Untermodul betrachtet wird:
-
-```latte
-link to Front:Shop:Product:show
-```
-
-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:
-
-```latte
-link to Admin:Product:show
-```
-
-Um herauszufinden, ob wir uns in einem bestimmten Modul oder dessen Untermodul befinden, können wir die Funktion `isModuleCurrent(moduleName)` verwenden.
-
-```latte
-
- ...
-
-```
-
-
-Routing .[#toc-routing]
------------------------
-
-Siehe [Kapitel über Routing |routing#Modules].
-
-
-Abbildung .[#toc-mapping]
--------------------------
-
-Legt die Regeln fest, nach denen der Klassenname aus dem Namen des Präsentators abgeleitet wird. Wir schreiben sie in die [Konfiguration |configuration] 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:
-
-```neon
-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:
-
-```neon
-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:
-
-```neon
-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:
-
-```neon
-application:
- mapping:
- *: [App\Modules, *, Presenters\*Presenter]
-```
-
-Der Standardwert ist `*: *Module\*Presenter`.
diff --git a/application/de/presenters.texy b/application/de/presenters.texy
index 35bc4c3513..8622224006 100644
--- a/application/de/presenters.texy
+++ b/application/de/presenters.texy
@@ -60,7 +60,7 @@ Unmittelbar nach Erhalt der Anfrage wird die Methode `startup ()` aufgerufen. Si
Es ist wichtig, dass `action()` vor aufgerufen wird `render()`aufgerufen wird, damit wir darin möglicherweise den weiteren Verlauf des Lebenszyklus ändern können, d. h. die Vorlage, die gerendert wird, und auch die Methode `render()` die aufgerufen wird, mit `setView('otherView')`.
-Die Parameter der Anfrage werden an die Methode übergeben. Es ist möglich und empfehlenswert, Typen für die Parameter anzugeben, z. B. `actionShow(int $id, string $slug = null)` - wenn der Parameter `id` fehlt oder keine ganze Zahl ist, gibt der Präsentator den [Fehler 404 |#Error 404 etc.] zurück und bricht die Operation ab.
+Die Parameter der Anfrage werden an die Methode übergeben. Es ist möglich und empfehlenswert, Typen für die Parameter anzugeben, z. B. `actionShow(int $id, ?string $slug = null)` - wenn der Parameter `id` fehlt oder keine ganze Zahl ist, gibt der Präsentator den [Fehler 404 |#Error 404 etc.] zurück und bricht die Operation ab.
`handle(args...)` .{toc: handle()}
@@ -205,7 +205,7 @@ In der Vorlage sind diese Meldungen in der Variablen `$flashes` als Objekte `std
Fehler 404 usw. .[#toc-error-404-etc]
=====================================
-Wenn wir die Anfrage nicht erfüllen können, weil z.B. der Artikel, den wir anzeigen wollen, nicht in der Datenbank existiert, werden wir den Fehler 404 mit der Methode `error(string $message = null, int $httpCode = 404)` ausgeben, die den HTTP-Fehler 404 darstellt:
+Wenn wir die Anfrage nicht erfüllen können, weil z.B. der Artikel, den wir anzeigen wollen, nicht in der Datenbank existiert, werden wir den Fehler 404 mit der Methode `error(?string $message = null, int $httpCode = 404)` ausgeben, die den HTTP-Fehler 404 darstellt:
```php
public function renderShow(int $id): void
@@ -236,6 +236,32 @@ public function actionData(): void
```
+Parameter anfordern .[#toc-request-parameters]
+==============================================
+
+Der Präsentator, wie auch jede Komponente, bezieht seine Parameter aus der HTTP-Anfrage. Ihre Werte können mit der Methode `getParameter($name)` oder `getParameters()` abgerufen werden. Bei den Werten handelt es sich um Strings oder Arrays von Strings, also im Wesentlichen um Rohdaten, die direkt aus der URL bezogen werden.
+
+Für zusätzlichen Komfort empfiehlt es sich, die Parameter über Eigenschaften zugänglich zu machen. Beschriften Sie sie einfach mit dem `#[Parameter]` Attribut:
+
+```php
+use Nette\Application\Attributes\Parameter; // diese Zeile ist wichtig
+
+class HomePresenter extends Nette\Application\UI\Presenter
+{
+ #[Parameter]
+ public string $theme; // muss öffentlich sein
+}
+```
+
+Für Eigenschaften empfehlen wir die Angabe des Datentyps (z. B. `string`). Nette wird den Wert dann automatisch auf der Grundlage dieses Typs umwandeln. Parameterwerte können auch [validiert |#Validation of Parameters] werden.
+
+Beim Erstellen einer Verknüpfung können Sie den Wert für die Parameter direkt festlegen:
+
+```latte
+click
+```
+
+
Dauerhafte Parameter .[#toc-persistent-parameters]
==================================================
@@ -257,7 +283,7 @@ class ProductPresenter extends Nette\Application\UI\Presenter
Wenn `$this->lang` einen Wert wie `'en'` hat, dann werden Links, die mit `link()` oder `n:href` erstellt werden, auch den Parameter `lang=en` enthalten. Und wenn der Link angeklickt wird, wird er wieder `$this->lang = 'en'` sein.
-Für Eigenschaften wird empfohlen, den Datentyp anzugeben (z. B. `string`), und Sie können auch einen Standardwert angeben. Parameterwerte können [validiert |#Validation of Persistent Parameters] werden.
+Für Eigenschaften wird empfohlen, den Datentyp anzugeben (z. B. `string`), und Sie können auch einen Standardwert angeben. Parameterwerte können [validiert |#Validation of Parameters] werden.
Persistente Parameter werden standardmäßig zwischen allen Aktionen eines bestimmten Präsentators weitergegeben. Um sie zwischen mehreren Präsentatoren zu übergeben, müssen Sie sie entweder definieren:
@@ -307,18 +333,12 @@ Tiefer gehen .[#toc-going-deeper]
Was wir bisher in diesem Kapitel gezeigt haben, wird wahrscheinlich ausreichen. Die folgenden Zeilen sind für diejenigen gedacht, die sich eingehend mit Moderatoren beschäftigen und alles wissen wollen.
-Anforderung und Parameter .[#toc-requirement-and-parameters]
-------------------------------------------------------------
-
-Die vom Präsentator bearbeitete Anfrage ist das Objekt [api:Nette\Application\Request] und wird von der Methode `getRequest()` des Präsentators zurückgegeben. Sie enthält ein Array von Parametern, und jeder von ihnen gehört entweder zu einer der Komponenten oder direkt zum Präsentator (der eigentlich auch eine Komponente ist, wenn auch eine spezielle). Nette verteilt also die Parameter um und übergibt sie zwischen den einzelnen Komponenten (und dem Präsentator) durch Aufruf der Methode `loadState(array $params)`. Die Parameter können mit der Methode `getParameters(): array`, einzeln mit `getParameter($name)` abgerufen werden. Bei den Parameterwerten handelt es sich um Strings oder Arrays von Strings, also im Grunde um Rohdaten, die direkt aus einer URL bezogen werden.
+Validierung von Parametern .[#toc-validation-of-parameters]
+-----------------------------------------------------------
+Die Werte von [Anfrageparametern |#request parameters] und [dauerhaften Parametern |#persistent parameters], die von URLs empfangen werden, werden von der Methode `loadState()` in Eigenschaften geschrieben. Sie prüft auch, ob der in der Eigenschaft angegebene Datentyp übereinstimmt, andernfalls antwortet sie mit einem 404-Fehler und die Seite wird nicht angezeigt.
-Validierung von persistenten Parametern .[#toc-validation-of-persistent-parameters]
------------------------------------------------------------------------------------
-
-Die Werte von [persistenten Parametern |#persistent parameters], die von URLs empfangen werden, werden von der Methode `loadState()` in Eigenschaften geschrieben. Sie prüft auch, ob der in der Eigenschaft angegebene Datentyp übereinstimmt, andernfalls antwortet sie mit einem 404-Fehler und die Seite wird nicht angezeigt.
-
-Verlassen Sie sich niemals blind auf persistente Parameter, da sie leicht vom Benutzer in der URL überschrieben werden können. So überprüfen wir zum Beispiel, ob `$this->lang` zu den unterstützten Sprachen gehört. Eine gute Möglichkeit, dies zu tun, besteht darin, die oben erwähnte Methode `loadState()` zu überschreiben:
+Verlassen Sie sich niemals blind auf Parameter, da sie leicht vom Benutzer in der URL überschrieben werden können. So überprüfen wir zum Beispiel, ob `$this->lang` zu den unterstützten Sprachen gehört. Eine gute Möglichkeit, dies zu tun, besteht darin, die oben erwähnte Methode `loadState()` zu überschreiben:
```php
class ProductPresenter extends Nette\Application\UI\Presenter
@@ -341,7 +361,9 @@ class ProductPresenter extends Nette\Application\UI\Presenter
Speichern und Wiederherstellen der Anfrage .[#toc-save-and-restore-the-request]
-------------------------------------------------------------------------------
-Sie können die aktuelle Anfrage in einer Session speichern oder sie aus der Session wiederherstellen und den Präsentator sie erneut ausführen lassen. Dies ist z. B. nützlich, wenn ein Benutzer ein Formular ausfüllt und seine Anmeldung abläuft. Um keine Daten zu verlieren, speichern wir vor der Routing zur Anmeldeseite die aktuelle Anfrage in der Session mit `$reqId = $this->storeRequest()`, die einen Bezeichner in Form einer kurzen Zeichenkette zurückgibt und ihn als Parameter an den Anmeldepräsentator übergibt.
+Die Anfrage, die der Präsentator bearbeitet, ist ein Objekt [api:Nette\Application\Request] und wird von der Methode `getRequest()` des Präsentators zurückgegeben.
+
+Sie können die aktuelle Anfrage in einer Sitzung speichern oder sie aus der Sitzung wiederherstellen und den Präsentator sie erneut ausführen lassen. Dies ist z. B. nützlich, wenn ein Benutzer ein Formular ausfüllt und sein Login abläuft. Um keine Daten zu verlieren, speichern wir vor der Weiterleitung zur Anmeldeseite die aktuelle Anfrage in der Sitzung mit `$reqId = $this->storeRequest()`, die einen Bezeichner in Form eines kurzen Strings zurückgibt und diesen als Parameter an den Anmeldepräsentator übergibt.
Nach der Anmeldung rufen wir die Methode `$this->restoreRequest($reqId)` auf, die die Anfrage aus der Session abholt und an diese weiterleitet. Die Methode prüft, ob die Anfrage von demselben Benutzer erstellt wurde, der jetzt angemeldet ist. Wenn sich ein anderer Benutzer anmeldet oder der Schlüssel ungültig ist, tut sie nichts und das Programm läuft weiter.
@@ -362,7 +384,7 @@ Eine Umleitung findet bei einer AJAX- oder POST-Anfrage nicht statt, da dies zu
Sie können die Kanonisierung auch manuell mit der Methode `canonicalize()` aufrufen, die wie die Methode `link()` den Präsentator, Aktionen und Parameter als Argumente erhält. Sie erstellt einen Link und vergleicht ihn mit der aktuellen URL. Wenn sie sich unterscheidet, wird sie auf den erzeugten Link umgeleitet.
```php
-public function actionShow(int $id, string $slug = null): void
+public function actionShow(int $id, ?string $slug = null): void
{
$realSlug = $this->facade->getSlugForId($id);
// leitet um, wenn $slug nicht mit $realSlug übereinstimmt
@@ -425,6 +447,51 @@ $this->sendResponse(new Responses\CallbackResponse($callback));
```
+Zugangsbeschränkung mit `#[Requires]` .[#toc-access-restriction-using-requires]{data-version:3.2.2}
+---------------------------------------------------------------------------------------------------
+
+Das Attribut `#[Requires]` Attribut bietet erweiterte Optionen zur Einschränkung des Zugriffs auf Präsentatoren und ihre Methoden. Es kann verwendet werden, um HTTP-Methoden zu spezifizieren, AJAX-Anfragen zu verlangen, den Zugriff auf denselben Ursprung zu beschränken und den Zugriff nur auf Weiterleitungen zu beschränken. Das Attribut kann sowohl auf Presenter-Klassen als auch auf einzelne Methoden angewendet werden, z. B. `action()`, `render()`, `handle()`, und `createComponent()`.
+
+Sie können diese Einschränkungen angeben:
+- auf HTTP-Methoden: `#[Requires(methods: ['GET', 'POST'])]`
+- die eine AJAX-Anfrage erfordern: `#[Requires(ajax: true)]`
+- Zugriff nur vom selben Ursprung: `#[Requires(sameOrigin: true)]`
+- Zugriff nur über Weiterleitung: `#[Requires(forward: true)]`
+- Einschränkungen für bestimmte Aktionen: `#[Requires(actions: 'default')]`
+
+Für Einzelheiten siehe [Verwendung des Requires Attributs |best-practices:attribute-requires].
+
+
+HTTP-Methodenprüfung .[#toc-http-method-check]
+----------------------------------------------
+
+In Nette überprüfen die Moderatoren die HTTP-Methode jeder eingehenden Anfrage automatisch, hauptsächlich aus Sicherheitsgründen. Standardmäßig sind die Methoden `GET`, `POST`, `HEAD`, `PUT`, `DELETE`, `PATCH` zugelassen.
+
+Wenn Sie zusätzliche Methoden wie `OPTIONS` aktivieren möchten, können Sie das `#[Requires]` Attribut verwenden (ab Nette Application v3.2):
+
+```php
+#[Requires(methods: ['GET', 'POST', 'HEAD', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'])]
+class MyPresenter extends Nette\Application\UI\Presenter
+{
+}
+```
+
+In Version 3.1 wird die Überprüfung in `checkHttpMethod()` durchgeführt, das prüft, ob die in der Anfrage angegebene Methode im Array `$presenter->allowedMethods` enthalten ist. Fügen Sie eine Methode wie diese hinzu:
+
+```php
+class MyPresenter extends Nette\Application\UI\Presenter
+{
+ protected function checkHttpMethod(): void
+ {
+ $this->allowedMethods[] = 'OPTIONS';
+ parent::checkHttpMethod();
+ }
+}
+```
+
+Es ist wichtig zu betonen, dass, wenn Sie die `OPTIONS` -Methode zulassen, Sie sie auch in Ihrem Präsentator richtig behandeln müssen. Diese Methode wird häufig als so genannte Preflight-Anfrage verwendet, die von Browsern automatisch vor der eigentlichen Anfrage gesendet wird, wenn es darum geht, festzustellen, ob die Anfrage aus Sicht der CORS-Richtlinie (Cross-Origin Resource Sharing) zulässig ist. Wenn Sie diese Methode zulassen, aber keine angemessene Antwort implementieren, kann dies zu Inkonsistenzen und potenziellen Sicherheitsproblemen führen.
+
+
Weitere Lektüre .[#toc-further-reading]
=======================================
diff --git a/application/de/routing.texy b/application/de/routing.texy
index dac42c271d..c60b743cb3 100644
--- a/application/de/routing.texy
+++ b/application/de/routing.texy
@@ -216,7 +216,7 @@ $router->addRoute('//www.%sld%.%tld%/%basePath%//addRoute('/[/]', [
@@ -225,7 +225,7 @@ $router->addRoute('/[/]', [
]);
```
-Oder wir können diese Form verwenden, beachten Sie die Umschreibung des regulären Ausdrucks für die Validierung:
+Für eine detailliertere Spezifikation kann eine noch umfangreichere Form verwendet werden, bei der zusätzlich zu den Standardwerten weitere Parametereigenschaften festgelegt werden können, wie z. B. ein regulärer Ausdruck für die Validierung (siehe den Parameter `id` ):
```php
use Nette\Routing\Route;
@@ -243,7 +243,7 @@ $router->addRoute('/[/]', [
]);
```
-Diese gesprächigeren Formate sind nützlich, um andere Metadaten hinzuzufügen.
+Es ist wichtig zu beachten, dass, wenn die im Array definierten Parameter nicht in der Pfadmaske enthalten sind, ihre Werte nicht geändert werden können, auch nicht mit Abfrageparametern, die nach einem Fragezeichen in der URL angegeben werden.
Filter und Übersetzungen .[#toc-filters-and-translations]
@@ -368,7 +368,7 @@ $router->addRoute('', function (string $lang) {
Module .[#toc-modules]
----------------------
-Wenn wir mehrere Routen haben, die zu einem [Modul |modules] gehören, können wir `withModule()` verwenden, um sie zu gruppieren:
+Wenn wir mehrere Routen haben, die zu einem [Modul |directory-structure#Presenters and Templates] gehören, können wir `withModule()` verwenden, um sie zu gruppieren:
```php
$router = new RouteList;
@@ -477,10 +477,10 @@ $router->addRoute('index.html \.html?|\.php|>', /* ... */);
Einbindung .[#toc-integration]
==============================
-Um unseren Router in die Anwendung einzubinden, müssen wir ihn dem DI-Container mitteilen. Am einfachsten ist es, die Fabrik vorzubereiten, die das Router-Objekt erstellt, und der Container-Konfiguration mitzuteilen, dass sie es verwenden soll. Schreiben wir also eine Methode für diesen Zweck `App\Router\RouterFactory::createRouter()`:
+Um unseren Router in die Anwendung einzubinden, müssen wir ihn dem DI-Container mitteilen. Am einfachsten ist es, die Fabrik vorzubereiten, die das Router-Objekt erstellt, und der Container-Konfiguration mitzuteilen, dass sie es verwenden soll. Schreiben wir also eine Methode für diesen Zweck `App\Core\RouterFactory::createRouter()`:
```php
-namespace App\Router;
+namespace App\Core;
use Nette\Application\Routers\RouteList;
@@ -499,7 +499,7 @@ Dann schreiben wir in [configuration |dependency-injection:services]:
```neon
services:
- - App\Router\RouterFactory::createRouter
+ - App\Core\RouterFactory::createRouter
```
Alle Abhängigkeiten, wie z. B. eine Datenbankverbindung usw., werden der Factory-Methode als Parameter über [Autowiring |dependency-injection:autowiring] übergeben:
@@ -663,7 +663,7 @@ Unter getrennter Nutzung verstehen wir die Verwendung der Router-Funktionen in e
Wir werden also wieder eine Methode erstellen, die einen Router aufbaut, zum Beispiel:
```php
-namespace App\Router;
+namespace App\Core;
use Nette\Routing\RouteList;
@@ -694,7 +694,7 @@ $httpRequest = $container->getByType(Nette\Http\IRequest::class);
Oder wir erstellen die Objekte direkt:
```php
-$router = App\Router\RouterFactory::createRouter();
+$router = App\Core\RouterFactory::createRouter();
$httpRequest = (new Nette\Http\RequestFactory)->fromGlobals();
```
diff --git a/application/de/templates.texy b/application/de/templates.texy
index a21bd7266f..9af8b475a7 100644
--- a/application/de/templates.texy
+++ b/application/de/templates.texy
@@ -34,35 +34,81 @@ Und dies könnte die Aktionsvorlage sein:
Sie definiert den Block `content`, der anstelle von `{include content}` in das Layout eingefügt wird, und definiert auch den Block `title` neu, der `{block title}` im Layout überschreibt. Versuchen Sie, sich das Ergebnis vorzustellen.
-Suche nach Templates .[#toc-search-for-templates]
--------------------------------------------------
+Vorlage nachschlagen .[#toc-template-lookup]
+--------------------------------------------
+
+In Presentern müssen Sie nicht angeben, welche Vorlage gerendert werden soll; das Framework bestimmt den Pfad automatisch, was die Codierung für Sie einfacher macht.
+
+Wenn Sie eine Verzeichnisstruktur verwenden, in der jeder Präsentator sein eigenes Verzeichnis hat, legen Sie die Vorlage einfach in diesem Verzeichnis unter dem Namen der Aktion (d. h. der Ansicht) ab. Verwenden Sie zum Beispiel für die Aktion `default` die Vorlage `default.latte`:
-Der Pfad zu den Vorlagen wird nach einer einfachen Logik hergeleitet. Es wird versucht zu sehen, ob eine dieser Vorlagendateien relativ zu dem Verzeichnis existiert, in dem sich die Presenter-Klasse befindet, wobei `` der Name des aktuellen Präsentators ist und `` der Name der aktuellen Aktion ist:
+/--pre
+app/
+└── Presentation/
+ └── Home/
+ ├── HomePresenter.php
+ └── default.latte
+\--
-- `templates//.latte`
-- `templates/..latte`
+Wenn Sie eine Struktur verwenden, bei der sich die Präsentatoren in einem Verzeichnis und die Vorlagen in einem Ordner `templates` befinden, speichern Sie sie entweder in einer Datei `..latte` oder `/.latte`:
-Wird die Vorlage nicht gefunden, wird versucht, im Verzeichnis `templates` eine Ebene höher zu suchen, d. h. auf der gleichen Ebene wie das Verzeichnis mit der Presenter-Klasse.
+/--pre
+app/
+└── Presenters/
+ ├── HomePresenter.php
+ └── templates/
+ ├── Home.default.latte ← 1st variant
+ └── Home/
+ └── default.latte ← 2nd variant
+\--
-Wenn die Vorlage auch dort nicht gefunden wird, ist die Antwort ein [404-Fehler |presenters#Error 404 etc.].
+Das Verzeichnis `templates` kann auch eine Ebene höher platziert werden, auf derselben Ebene wie das Verzeichnis mit den Presenter-Klassen.
-Sie können die Ansicht auch mit `$this->setView('otherView')` ändern. Oder geben Sie statt der Suche direkt den Namen der Vorlagendatei mit `$this->template->setFile('/path/to/template.latte')` an.
+Wenn die Vorlage nicht gefunden wird, antwortet der Präsentator mit dem [Fehler 404 - Seite nicht gefunden |presenters#Error 404 etc].
+
+Sie können die Ansicht mit `$this->setView('anotherView')` ändern. Es ist auch möglich, die Vorlagendatei direkt mit `$this->template->setFile('/path/to/template.latte')` anzugeben.
.[note]
-Sie können die Pfade, in denen Vorlagen gesucht werden, ändern, indem Sie die Methode [formatTemplateFiles |api:Nette\Application\UI\Presenter::formatTemplateFiles()] überschreiben, die ein Array mit möglichen Dateipfaden zurückgibt.
+Dateien, in denen Vorlagen gesucht werden, können durch Überschreiben der Methode [formatTemplateFiles() |api:Nette\Application\UI\Presenter::formatTemplateFiles()] geändert werden, die ein Array mit möglichen Dateinamen zurückgibt.
+
+
+Layout-Vorlagen-Suche .[#toc-layout-template-lookup]
+----------------------------------------------------
+
+Nette sucht auch automatisch nach der Layout-Datei.
+
+Wenn Sie eine Verzeichnisstruktur verwenden, in der jeder Präsentator sein eigenes Verzeichnis hat, legen Sie das Layout entweder in dem Ordner mit dem Präsentator ab, wenn es nur für diesen spezifisch ist, oder eine Ebene höher, wenn es für mehrere Präsentatoren gemeinsam ist:
+
+/--pre
+app/
+└── Presentation/
+ ├── @layout.latte ← common layout
+ └── Home/
+ ├── @layout.latte ← only for Home presenter
+ ├── HomePresenter.php
+ └── default.latte
+\--
+
+Wenn Sie eine Struktur verwenden, bei der die Vortragenden in einem Verzeichnis zusammengefasst sind und sich die Vorlagen in einem Ordner `templates` befinden, wird das Layout an den folgenden Stellen erwartet:
-Das Layout wird in den folgenden Dateien erwartet:
+/--pre
+app/
+└── Presenters/
+ ├── HomePresenter.php
+ └── templates/
+ ├── @layout.latte ← common layout
+ ├── Home.@layout.latte ← only for Home, 1st variant
+ └── Home/
+ └── @layout.latte ← only for Home, 2nd variant
+\--
-- `templates//@.latte`
-- `templates/.@.latte`
-- `templates/@.latte` gemeinsames Layout für mehrere Präsentatoren
+Befindet sich der Präsentator in einem Modul, wird er auch weiter oben im Verzeichnisbaum entsprechend der Verschachtelung des Moduls gesucht.
-`` ist der Name des aktuellen Präsentators und `` ist der Name des Layouts, der standardmäßig `'layout'` lautet. Der Name kann mit `$this->setLayout('otherLayout')` geändert werden, so dass `@otherLayout.latte` Dateien ausprobiert werden.
+Der Name des Layouts kann mit `$this->setLayout('layoutAdmin')` geändert werden und wird dann in der Datei `@layoutAdmin.latte` erwartet. Sie können die Layout-Vorlagendatei auch direkt mit `$this->setLayout('/path/to/template.latte')` angeben.
-Sie können auch direkt den Dateinamen der Layoutvorlage mit `$this->setLayout('/path/to/template.latte')` angeben. Durch die Verwendung von `$this->setLayout(false)` wird die Layout-Suche deaktiviert.
+Die Verwendung von `$this->setLayout(false)` oder des Tags `{layout none}` innerhalb der Vorlage deaktiviert die Layout-Suche.
.[note]
-Sie können die Pfade, in denen Vorlagen gesucht werden, ändern, indem Sie die Methode [formatLayoutTemplateFiles |api:Nette\Application\UI\Presenter::formatLayoutTemplateFiles()] überschreiben, die ein Array mit möglichen Dateipfaden zurückgibt.
+Die Dateien, in denen Layoutvorlagen gesucht werden, können durch Überschreiben der Methode [formatLayoutTemplateFiles() |api:Nette\Application\UI\Presenter::formatLayoutTemplateFiles()] geändert werden, die ein Array mit möglichen Dateinamen zurückgibt.
Variablen in der Vorlage .[#toc-variables-in-the-template]
@@ -104,7 +150,7 @@ Die `@property-read` Annotation ist für die IDE und die statische Analyse, sie
Sie können sich auch den Luxus gönnen, in Vorlagen zu flüstern. Installieren Sie einfach das Latte-Plugin in PhpStorm und geben Sie den Klassennamen am Anfang der Vorlage an, siehe den Artikel "Latte: how to type system":https://blog.nette.org/de/latte-wie-benutzt-man-das-typensystem:
```latte
-{templateType App\Presenters\ArticleTemplate}
+{templateType App\Presentation\Article\ArticleTemplate}
...
```
@@ -176,7 +222,7 @@ public function beforeRender(): void
Latte Version 3 bietet einen fortgeschritteneren Weg, indem es eine [Erweiterung |latte:creating-extension] für jedes Webprojekt erstellt. Hier ist ein grobes Beispiel für eine solche Klasse:
```php
-namespace App\Templating;
+namespace App\Presentation\Accessory;
final class LatteExtension extends Latte\Extension
{
@@ -214,7 +260,7 @@ Wir registrieren sie mit [configuration |configuration#Latte]:
```neon
latte:
extensions:
- - App\Templating\LatteExtension
+ - App\Presentation\Accessory\LatteExtension
```
@@ -239,7 +285,7 @@ Alternativ kann der Übersetzer auch über die [Konfiguration |configuration#Lat
```neon
latte:
extensions:
- - Latte\Essential\TranslatorExtension
+ - Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)
```
Der Übersetzer kann dann z.B. als Filter `|translate` verwendet werden, wobei zusätzliche Parameter an die Methode `translate()` übergeben werden (siehe `foo, bar`):
diff --git a/application/el/@home.texy b/application/el/@home.texy
index 8020e59b5f..5d89df5402 100644
--- a/application/el/@home.texy
+++ b/application/el/@home.texy
@@ -2,35 +2,84 @@
**************
.[perex]
-Το πακέτο `nette/application` αποτελεί τη βάση για τη δημιουργία διαδραστικών εφαρμογών ιστού.
+Το Nette Application είναι ο πυρήνας του πλαισίου Nette που προσφέρει ισχυρά εργαλεία για τη δημιουργία σύγχρονων εφαρμογών ιστού. Προσφέρει πολυάριθμα εξαιρετικά χαρακτηριστικά που απλοποιούν σημαντικά την ανάπτυξη και βελτιώνουν την ασφάλεια και τη συντηρησιμότητα του κώδικα.
-- [Πώς λειτουργούν οι εφαρμογές; |how-it-works]
-- [Bootstrap |Bootstrap]
-- [Παρουσιαστές |Presenters]
-- [Πρότυπα |Templates]
-- [Ενότητες |Modules]
-- [Δρομολόγηση |Routing]
-- [Δημιουργία συνδέσμων URL |creating-links]
-- [Διαδραστικά στοιχεία |components]
-- [AJAX & Snippets |ajax]
-- [Πολλαπλασιαστής |multiplier]
-- [Διαμόρφωση |Configuration]
+Εγκατάσταση .[#toc-installation]
+--------------------------------
-Εγκατάσταση
------------
-
-Κατεβάστε και εγκαταστήστε το πακέτο χρησιμοποιώντας το [Composer |best-practices:composer]:
+Κατεβάστε και εγκαταστήστε τη βιβλιοθήκη χρησιμοποιώντας [το Composer |best-practices:composer]:
```shell
composer require nette/application
```
-| έκδοση | συμβατό με PHP
+
+Γιατί να επιλέξετε την εφαρμογή Nette; .[#toc-why-choose-nette-application]
+---------------------------------------------------------------------------
+
+Η Nette ήταν πάντα πρωτοπόρος στις τεχνολογίες ιστού.
+
+**Αμφίδρομος δρομολογητής:** Η Nette διαθέτει ένα προηγμένο σύστημα δρομολόγησης μοναδικό στην αμφίδρομη λειτουργία του - δεν μεταφράζει μόνο τις διευθύνσεις URL σε ενέργειες της εφαρμογής, αλλά μπορεί επίσης να παράγει διευθύνσεις URL αντίστροφα. Αυτό σημαίνει ότι:
+- Μπορείτε να τροποποιήσετε τη δομή των διευθύνσεων URL ολόκληρης της εφαρμογής ανά πάσα στιγμή χωρίς να τροποποιήσετε τα αρχεία προτύπων
+- Οι διευθύνσεις URL κανονικοποιούνται αυτόματα, βελτιώνοντας το SEO
+- Η δρομολόγηση ορίζεται σε ένα μέρος, όχι διάσπαρτα σε σημειώσεις
+
+**Συστατικά και σήματα:** Το ενσωματωμένο σύστημα συστατικών εμπνευσμένο από τους Delphi και το React.js είναι μοναδικό μεταξύ των πλαισίων PHP:
+- Επιτρέπει τη δημιουργία επαναχρησιμοποιήσιμων στοιχείων UI
+- Υποστηρίζει ιεραρχική σύνθεση συστατικών
+- Προσφέρει κομψό χειρισμό αιτήσεων AJAX με χρήση σημάτων
+- Πλούσια βιβλιοθήκη έτοιμων στοιχείων στο [Componette](https://componette.org)
+
+**AJAX και Snippets:** Η Nette εισήγαγε έναν επαναστατικό τρόπο εργασίας με AJAX το 2009, πριν από λύσεις όπως το Hotwire για Ruby on Rails ή το Symfony UX Turbo:
+- Τα Snippets επιτρέπουν την ενημέρωση μόνο τμημάτων της σελίδας χωρίς τη συγγραφή JavaScript
+- Αυτόματη ενσωμάτωση με το σύστημα συστατικών
+- Έξυπνη ακύρωση τμημάτων της σελίδας
+- Ελάχιστη μεταφορά δεδομένων
+
+**Εξυπηρέτηση [με Latte |latte:] Templates:** Το πιο ασφαλές σύστημα template για PHP με προηγμένα χαρακτηριστικά:
+- Αυτόματη προστασία XSS με διαφυγή με ευαισθησία περιβάλλοντος
+- Επεκτάσιμο με προσαρμοσμένα φίλτρα, συναρτήσεις και ετικέτες
+- Κληρονομικότητα προτύπων και αποσπάσματα για AJAX
+- Εξαιρετική υποστήριξη της PHP 8.x με σύστημα τύπων
+
+**Έγχυση Εξαρτήσεων:** Η Nette χρησιμοποιεί πλήρως την έγχυση εξαρτήσεων:
+- Αυτόματο πέρασμα εξαρτήσεων (autowiring)
+- Ρύθμιση παραμέτρων με τη χρήση σαφούς μορφής NEON
+- Υποστήριξη για εργοστάσια συστατικών
+
+
+Κύρια οφέλη .[#toc-main-benefits]
+---------------------------------
+
+- **Ασφάλεια**: XSS, CSRF κ.λπ.
+- **Παραγωγικότητα**: Λιγότερο γράψιμο, περισσότερες δυνατότητες χάρη στον έξυπνο σχεδιασμό
+- **Αποσφαλμάτωση**: [Αποσφαλματωτής Tracy |tracy:] με πίνακα δρομολόγησης
+- **Απόδοση**: Ευφυές σύστημα προσωρινής αποθήκευσης, χαλαρή φόρτωση των στοιχείων
+- **Ευελιξία**: Εύκολη τροποποίηση της διεύθυνσης URL ακόμη και μετά την ολοκλήρωση της εφαρμογής
+- **Συστατικά**: Μοναδικό σύστημα επαναχρησιμοποιήσιμων στοιχείων UI
+- **Σύγχρονο**: Πλήρης υποστήριξη για PHP 8.4+ και σύστημα τύπων
+
+
+Ξεκινώντας .[#toc-getting-started]
+----------------------------------
+
+1. [Κατανόηση των εφαρμογών |how-it-works] - Κατανόηση της βασικής αρχιτεκτονικής
+2. [Παρουσιάστριες |presenters] - Εργασία με παρουσιάστριες και ενέργειες
+3. [Πρότυπα |templates] - Δημιουργία προτύπων στο Latte
+4. [Δρομολόγηση |routing] - Διαμόρφωση URL
+5. [Διαδραστικά συστατικά |components] - Χρήση του συστήματος συστατικών
+
+
+Συμβατότητα PHP .[#toc-php-compatibility]
+-----------------------------------------
+
+| έκδοση | συμβατή με PHP
|-----------|-------------------
-| Εφαρμογή Nette 4.0 | PHP 8.0 - 8.2
-| Nette Application 3.1 | PHP 7.2 - 8.2
+| Nette Application 4.0 | PHP 8.1 - 8.4
+| Nette Application 3.2 | PHP 8.1 - 8.4
+| Nette Application 3.1 | PHP 7.2 - 8.3
| Nette Application 3.0 | PHP 7.1 - 8.0
| Nette Application 2.4 | PHP 5.6 - 8.0
-Ισχύει για τις τελευταίες εκδόσεις διορθώσεων.
+Ισχύει για τις τελευταίες εκδόσεις διορθώσεων.
\ No newline at end of file
diff --git a/application/el/@left-menu.texy b/application/el/@left-menu.texy
index b704f8e06d..6406189155 100644
--- a/application/el/@left-menu.texy
+++ b/application/el/@left-menu.texy
@@ -4,7 +4,7 @@
- [Bootstrap |Bootstrap]
- [Παρουσιαστές |Presenters]
- [Πρότυπα |Templates]
-- [Ενότητες |Modules]
+- [Δομή καταλόγου |directory-structure]
- [Δρομολόγηση |Routing]
- [Δημιουργία συνδέσμων URL |creating-links]
- [Διαδραστικά στοιχεία |components]
diff --git a/application/el/ajax.texy b/application/el/ajax.texy
index 8ca0741a48..ab0e2f48f5 100644
--- a/application/el/ajax.texy
+++ b/application/el/ajax.texy
@@ -3,10 +3,10 @@ AJAX & Snippets
-Οι σύγχρονες διαδικτυακές εφαρμογές τρέχουν σήμερα κατά το ήμισυ σε έναν διακομιστή και κατά το ήμισυ σε ένα πρόγραμμα περιήγησης. Το AJAX είναι ένας ζωτικής σημασίας ενωτικός παράγοντας. Τι υποστήριξη προσφέρει το Nette Framework;
-- αποστολή τμημάτων προτύπου (τα λεγόμενα *snippets*)
-- μεταβίβαση μεταβλητών μεταξύ PHP και JavaScript
-- αποσφαλμάτωση εφαρμογών AJAX
+Στην εποχή των σύγχρονων διαδικτυακών εφαρμογών, όπου η λειτουργικότητα συχνά εκτείνεται μεταξύ του διακομιστή και του προγράμματος περιήγησης, το AJAX είναι ένα απαραίτητο συνδετικό στοιχείο. Ποιες επιλογές προσφέρει το Nette Framework σε αυτόν τον τομέα;
+- αποστολή τμημάτων του προτύπου, των λεγόμενων αποσπασμάτων
+- διαβίβαση μεταβλητών μεταξύ PHP και JavaScript
+- εργαλεία για την αποσφαλμάτωση αιτημάτων AJAX
@@ -14,29 +14,32 @@ AJAX & Snippets
Αίτηση AJAX .[#toc-ajax-request]
================================
-Ένα αίτημα AJAX δεν διαφέρει από ένα κλασικό αίτημα - ο παρουσιαστής καλείται με μια συγκεκριμένη προβολή και παραμέτρους. Εξαρτάται επίσης από τον παρουσιαστή πώς θα απαντήσει σε αυτό: μπορεί να χρησιμοποιήσει τη δική του ρουτίνα, η οποία επιστρέφει ένα τμήμα κώδικα HTML (απόσπασμα HTML), ένα έγγραφο XML, ένα αντικείμενο JSON ή κώδικα JavaScript.
+Ένα αίτημα AJAX δεν διαφέρει ουσιαστικά από ένα κλασικό αίτημα HTTP. Ένας παρουσιαστής καλείται με συγκεκριμένες παραμέτρους. Από τον παρουσιαστή εξαρτάται πώς θα απαντήσει στο αίτημα - μπορεί να επιστρέψει δεδομένα σε μορφή JSON, να στείλει ένα τμήμα κώδικα HTML, ένα έγγραφο XML κ.λπ.
-Από την πλευρά του διακομιστή, ένα αίτημα AJAX μπορεί να ανιχνευθεί χρησιμοποιώντας τη μέθοδο service που [ενθυλακώνει το αίτημα HTTP |http:request] `$httpRequest->isAjax()` (ανιχνεύει με βάση την επικεφαλίδα HTTP `X-Requested-With`). Στο εσωτερικό του παρουσιαστή, είναι διαθέσιμη μια συντόμευση με τη μορφή της μεθόδου `$this->isAjax()`.
+Από την πλευρά του προγράμματος περιήγησης, ξεκινάμε ένα αίτημα AJAX χρησιμοποιώντας τη συνάρτηση `fetch()`:
-Υπάρχει ένα προεπεξεργασμένο αντικείμενο που ονομάζεται `payload` και είναι αφιερωμένο στην αποστολή δεδομένων στο πρόγραμμα περιήγησης σε JSON.
-
-```php
-public function actionDelete(int $id): void
-{
- if ($this->isAjax()) {
- $this->payload->message = 'Success';
- }
- // ...
-}
+```js
+fetch(url, {
+ headers: {'X-Requested-With': 'XMLHttpRequest'},
+})
+.then(response => response.json())
+.then(payload => {
+ // επεξεργασία της απάντησης
+});
```
-Για πλήρη έλεγχο της εξόδου JSON χρησιμοποιήστε τη μέθοδο `sendJson` στον παρουσιαστή σας. Τερματίζει αμέσως τον presenter και θα κάνετε χωρίς πρότυπο:
+Στην πλευρά του διακομιστή, ένα αίτημα AJAX αναγνωρίζεται από τη μέθοδο `$httpRequest->isAjax()` της υπηρεσίας που [ενθυλακώνει το αίτημα HTTP |http:request]. Χρησιμοποιεί την επικεφαλίδα HTTP `X-Requested-With`, οπότε είναι απαραίτητη η αποστολή της. Μέσα στον παρουσιαστή, μπορείτε να χρησιμοποιήσετε τη μέθοδο `$this->isAjax()`.
+
+Εάν θέλετε να στείλετε δεδομένα σε μορφή JSON, χρησιμοποιήστε τη μέθοδο [`sendJson()` |presenters#Sending a response] μέθοδο. Η μέθοδος τερματίζει επίσης τη δραστηριότητα του παρουσιαστή.
```php
-$this->sendJson(['key' => 'value', /* ... */]);
+public function actionExport(): void
+{
+ $this->sendJson($this->model->getData);
+}
```
-Εάν θέλουμε να στείλουμε HTML, μπορούμε είτε να ορίσουμε ένα ειδικό πρότυπο για αιτήσεις AJAX:
+Αν σκοπεύετε να απαντήσετε με ένα ειδικό πρότυπο σχεδιασμένο για AJAX, μπορείτε να το κάνετε ως εξής:
```php
public function handleClick($param): void
@@ -44,27 +47,43 @@ public function handleClick($param): void
if ($this->isAjax()) {
$this->template->setFile('path/to/ajax.latte');
}
- // ...
+ //...
}
```
+Αποσπάσματα .[#toc-snippets]
+============================
+
+Το πιο ισχυρό εργαλείο που προσφέρει η Nette για τη σύνδεση του διακομιστή με τον πελάτη είναι τα snippets. Με αυτά μπορείτε να μετατρέψετε μια συνηθισμένη εφαρμογή σε AJAX με ελάχιστη προσπάθεια και λίγες γραμμές κώδικα. Το παράδειγμα Fifteen δείχνει πώς λειτουργούν όλα αυτά, και ο κώδικάς του μπορεί να βρεθεί στο [GitHub |https://github.com/nette-examples/fifteen].
+
+Τα αποσπάσματα, ή αποκόμματα, σας επιτρέπουν να ενημερώνετε μόνο τμήματα της σελίδας, αντί να επαναφορτώνετε ολόκληρη τη σελίδα. Αυτό είναι πιο γρήγορο και αποτελεσματικό, και παρέχει επίσης μια πιο άνετη εμπειρία χρήσης. Τα αποσπάσματα μπορεί να σας θυμίζουν το Hotwire για το Ruby on Rails ή το Symfony UX Turbo. Είναι ενδιαφέρον ότι η Nette εισήγαγε τα snippets 14 χρόνια νωρίτερα.
+
+Πώς λειτουργούν τα snippets; Όταν φορτώνεται για πρώτη φορά η σελίδα (μια αίτηση χωρίς-AJAX), φορτώνεται ολόκληρη η σελίδα, συμπεριλαμβανομένων όλων των snippets. Όταν ο χρήστης αλληλεπιδρά με τη σελίδα (π.χ. κάνει κλικ σε ένα κουμπί, υποβάλλει μια φόρμα κ.λπ.), αντί να φορτωθεί ολόκληρη η σελίδα, γίνεται ένα αίτημα AJAX. Ο κώδικας στον παρουσιαστή εκτελεί την ενέργεια και αποφασίζει ποια αποσπάσματα χρειάζονται ενημέρωση. Η Nette αποδίδει αυτά τα αποσπάσματα και τα αποστέλλει με τη μορφή ενός πίνακα JSON. Ο κώδικας χειρισμού στο πρόγραμμα περιήγησης εισάγει στη συνέχεια τα ληφθέντα αποσπάσματα πίσω στη σελίδα. Επομένως, μεταφέρεται μόνο ο κώδικας των αλλαγμένων αποσπασμάτων, εξοικονομώντας εύρος ζώνης και επιταχύνοντας τη φόρτωση σε σύγκριση με τη μεταφορά ολόκληρου του περιεχομένου της σελίδας.
+
+
Naja .[#toc-naja]
-=================
+-----------------
-Η [βιβλιοθήκη Naja |https://naja.js.org] χρησιμοποιείται για το χειρισμό αιτημάτων AJAX στην πλευρά του προγράμματος περιήγησης. [Εγκαταστήστε |https://naja.js.org/#/guide/01-install-setup-naja] την ως πακέτο node.js (για χρήση με Webpack, Rollup, Vite, Parcel και άλλα):
+Για το χειρισμό των αποσπασμάτων στην πλευρά του προγράμματος περιήγησης, χρησιμοποιείται η [βιβλιοθήκη Naja |https://naja.js.org]. [Εγκαταστήστε την |https://naja.js.org/#/guide/01-install-setup-naja] ως πακέτο node.js (για χρήση με εφαρμογές όπως Webpack, Rollup, Vite, Parcel και άλλες):
```shell
npm install naja
```
-...ή να την εισαγάγετε απευθείας στο πρότυπο της σελίδας:
+... ή να την εισαγάγετε απευθείας στο πρότυπο της σελίδας:
```html
```
-Για να δημιουργήσετε μια αίτηση AJAX από έναν κανονικό σύνδεσμο (σήμα) ή μια υποβολή φόρμας, απλά επισημάνετε τον σχετικό σύνδεσμο, τη φόρμα ή το κουμπί με την κλάση `ajax`:
+Πρώτα πρέπει να [αρχικοποιήσετε |https://naja.js.org/#/guide/01-install-setup-naja?id=initialization] τη βιβλιοθήκη:
+
+```js
+naja.initialize();
+```
+
+Για να μετατρέψετε έναν συνηθισμένο σύνδεσμο (σήμα) ή την υποβολή φόρμας σε αίτηση AJAX, απλά σημειώστε τον αντίστοιχο σύνδεσμο, φόρμα ή κουμπί με την κλάση `ajax`:
```html
Go
@@ -74,64 +93,39 @@ npm install naja
or
+
```
-Snippets .[#toc-snippets]
-=========================
-
-Υπάρχει ένα πολύ πιο ισχυρό εργαλείο ενσωματωμένης υποστήριξης AJAX - τα αποσπάσματα. Η χρήση τους καθιστά δυνατή τη μετατροπή μιας κανονικής εφαρμογής σε εφαρμογή AJAX χρησιμοποιώντας μόνο μερικές γραμμές κώδικα. Το πώς λειτουργούν όλα αυτά παρουσιάζεται στο παράδειγμα Fifteen του οποίου ο κώδικας είναι επίσης προσβάσιμος στο build ή στο [GitHub |https://github.com/nette-examples/fifteen].
-
-Ο τρόπος που λειτουργούν τα snippets είναι ότι ολόκληρη η σελίδα μεταφέρεται κατά το αρχικό (δηλαδή μη-AJAX) αίτημα και στη συνέχεια με κάθε AJAX [υποερώτημα |components#signal] (αίτημα της ίδιας προβολής του ίδιου παρουσιαστή) μεταφέρεται μόνο ο κώδικας των αλλαγμένων τμημάτων στο αποθετήριο `payload` που αναφέρθηκε προηγουμένως.
-
-Τα Snippets μπορεί να σας θυμίζουν το Hotwire για το Ruby on Rails ή το Symfony UX Turbo, αλλά η Nette τα επινόησε δεκατέσσερα χρόνια νωρίτερα.
-
+Επανασχεδίαση αποσπασμάτων .[#toc-redrawing-snippets]
+-----------------------------------------------------
-Ακύρωση των Snippets .[#toc-invalidation-of-snippets]
-=====================================================
-
-Κάθε απόγονος της κλάσης [Control |components] (που είναι και ένας Παρουσιαστής) είναι σε θέση να θυμάται αν υπήρξαν αλλαγές κατά τη διάρκεια μιας αίτησης που απαιτούν την εκ νέου εμφάνιση. Υπάρχει ένα ζευγάρι μεθόδων για το χειρισμό αυτό: `redrawControl()` και `isControlInvalid()`. Ένα παράδειγμα:
+Κάθε αντικείμενο της κλάσης [Control |components] (συμπεριλαμβανομένου και του ίδιου του Presenter) διατηρεί αρχείο για το αν έχουν συμβεί αλλαγές που καθιστούν αναγκαία την επανασχεδίασή του. Για το σκοπό αυτό χρησιμοποιείται η μέθοδος `redrawControl()`.
```php
public function handleLogin(string $user): void
{
- // Το αντικείμενο πρέπει να αναδημιουργηθεί εκ νέου μετά τη σύνδεση του χρήστη.
+ // μετά τη σύνδεση, είναι απαραίτητο να σχεδιάσετε εκ νέου το σχετικό τμήμα
$this->redrawControl();
- // ...
+ //...
}
```
-Η Nette ωστόσο προσφέρει μια ακόμη πιο λεπτή ανάλυση από ολόκληρα στοιχεία. Οι αναφερόμενες μέθοδοι δέχονται το όνομα ενός λεγόμενου "αποσπάσματος" ως προαιρετική παράμετρο. Ένα "απόσπασμα" είναι ουσιαστικά ένα στοιχείο στο πρότυπό σας που επισημαίνεται για το σκοπό αυτό με μια ετικέτα Latte, περισσότερα γι' αυτό αργότερα. Έτσι είναι δυνατόν να ζητήσετε από ένα στοιχείο να ξανασχεδιάσει μόνο *μέρη* του προτύπου του. Εάν ακυρωθεί ολόκληρο το συστατικό, τότε όλα τα αποσπάσματά του αναδημιουργούνται εκ νέου. Ένα συστατικό είναι "άκυρο" επίσης εάν οποιοδήποτε από τα υποσυστήματά του είναι άκυρο.
-
-```php
-$this->isControlInvalid(); // -> false
-$this->redrawControl('header'); // ακυρώνει το απόσπασμα με το όνομα 'header'
-$this->isControlInvalid('header'); // -> true
-$this->isControlInvalid('footer'); // -> false
-$this->isControlInvalid(); // -> true, τουλάχιστον ένα απόσπασμα είναι άκυρο
+Η Nette επιτρέπει επίσης έναν πιο λεπτομερή έλεγχο του τι χρειάζεται επανασχεδίαση. Η προαναφερθείσα μέθοδος μπορεί να λάβει το όνομα του αποσπάσματος ως όρισμα. Έτσι, είναι δυνατή η ακύρωση (που σημαίνει: εξαναγκασμός σε επανασχεδίαση) σε επίπεδο τμήματος προτύπου. Εάν ακυρωθεί ολόκληρο το συστατικό, κάθε απόσπασμα αυτού επανασχεδιάζεται επίσης:
-$this->redrawControl(); // ακυρώνει ολόκληρο το συστατικό, κάθε απόσπασμα
-$this->isControlInvalid('footer'); // -> true
+```php
+// ακυρώνει το απόσπασμα 'header'
+$this->redrawControl('header');
```
-Ένα συστατικό που λαμβάνει σήμα επισημαίνεται αυτόματα για επανασχεδίαση.
-
-Χάρη στην επανασχεδίαση αποσπασμάτων γνωρίζουμε επακριβώς ποια τμήματα ποιων στοιχείων πρέπει να επανασχεδιαστούν.
-
-
-Ετικέτα `{snippet} … {/snippet}` .{toc: Tag snippet}
-====================================================
-
-Η απόδοση της σελίδας εξελίσσεται πολύ παρόμοια με μια κανονική αίτηση: φορτώνονται τα ίδια πρότυπα κ.λπ. Το ζωτικής σημασίας μέρος είναι, ωστόσο, να παραλείπονται τα μέρη που δεν πρέπει να φτάσουν στην έξοδο- τα υπόλοιπα μέρη πρέπει να συσχετίζονται με ένα αναγνωριστικό και να αποστέλλονται στο χρήστη σε κατανοητή μορφή για έναν χειριστή JavaScript.
-
-Σύνταξη .[#toc-syntax]
-----------------------
+Αποσπάσματα σε Latte .[#toc-snippets-in-latte]
+----------------------------------------------
-Εάν υπάρχει ένα στοιχείο ελέγχου ή ένα απόσπασμα στο πρότυπο, πρέπει να το τυλίξουμε χρησιμοποιώντας την ετικέτα `{snippet} ... {/snippet}` pair - θα διασφαλίσει ότι το αποδιδόμενο απόσπασμα θα "αποκοπεί" και θα σταλεί στο πρόγραμμα περιήγησης. Θα το περικλείσει επίσης σε ένα βοηθητικό `` tag (είναι δυνατόν να χρησιμοποιηθεί ένα διαφορετικό). Στο ακόλουθο παράδειγμα ορίζεται ένα απόσπασμα με το όνομα `header`. Μπορεί κάλλιστα να αντιπροσωπεύει το πρότυπο ενός στοιχείου:
+Η χρήση αποσπασμάτων στο Latte είναι εξαιρετικά εύκολη. Για να ορίσετε ένα μέρος του προτύπου ως snippet, απλά τυλίξτε το σε ετικέτες `{snippet}` και `{/snippet}`:
```latte
{snippet header}
@@ -139,7 +133,9 @@ $this->isControlInvalid('footer'); // -> true
{/snippet}
```
-Ένα απόσπασμα άλλου τύπου από το `
` ή ένα απόσπασμα με πρόσθετα χαρακτηριστικά HTML επιτυγχάνεται με τη χρήση της παραλλαγής χαρακτηριστικών:
+Το απόσπασμα δημιουργεί ένα στοιχείο `
` στη σελίδα HTML με ένα ειδικά δημιουργημένο `id`. Κατά την επανασχεδίαση ενός αποσπάσματος, το περιεχόμενο αυτού του στοιχείου ενημερώνεται. Επομένως, κατά την αρχική απόδοση της σελίδας, όλα τα snippets πρέπει επίσης να αποδοθούν, ακόμη και αν μπορεί αρχικά να είναι κενά.
+
+Μπορείτε επίσης να δημιουργήσετε ένα απόσπασμα με ένα στοιχείο διαφορετικό από το `
` χρησιμοποιώντας ένα χαρακτηριστικό n:attribute:
```latte
@@ -148,138 +144,106 @@ $this->isControlInvalid('footer'); // -> true
```
-Δυναμικά αποσπάσματα .[#toc-dynamic-snippets]
-=============================================
+Περιοχές αποσπασμάτων .[#toc-snippet-areas]
+-------------------------------------------
-Στο Nette μπορείτε επίσης να ορίσετε αποσπάσματα με δυναμικό όνομα βάσει μιας παραμέτρου εκτέλεσης. Αυτό είναι πιο κατάλληλο για διάφορες λίστες όπου πρέπει να αλλάξουμε μόνο μια γραμμή αλλά δεν θέλουμε να μεταφέρουμε ολόκληρη τη λίστα μαζί με αυτήν. Ένα τέτοιο παράδειγμα θα ήταν το εξής:
+Τα ονόματα αποσπασμάτων μπορούν επίσης να είναι εκφράσεις:
```latte
-
- {foreach $list as $id => $item}
- - {$item} update
- {/foreach}
-
+{foreach $items as $id => $item}
+ {$item}
+{/foreach}
```
-Υπάρχει ένα στατικό απόσπασμα που ονομάζεται `itemsContainer`, το οποίο περιέχει διάφορα δυναμικά αποσπάσματα: `item-0`, `item-1` κ.ο.κ.
+ `item-0`, `item-1`, κ.λπ. Αν ακυρώναμε άμεσα ένα δυναμικό απόσπασμα (π.χ. `item-1`), τίποτα δεν θα ξανασχεδιαζόταν. Ο λόγος είναι ότι τα αποσπάσματα λειτουργούν ως αληθινά αποσπάσματα και μόνο τα ίδια αποδίδονται άμεσα. Ωστόσο, στο πρότυπο, δεν υπάρχει τεχνικά ένα απόσπασμα με το όνομα `item-1`. Εμφανίζεται μόνο όταν εκτελείται ο περιβάλλων κώδικας του αποσπάσματος, στην προκειμένη περίπτωση, ο βρόχος foreach. Ως εκ τούτου, θα επισημάνουμε το τμήμα του προτύπου που πρέπει να εκτελεστεί με την ετικέτα `{snippetArea}`:
-Δεν μπορείτε να ξανασχεδιάσετε άμεσα ένα δυναμικό απόσπασμα (η επανασχεδίαση του `item-1` δεν έχει κανένα αποτέλεσμα), πρέπει να ξανασχεδιάσετε το γονικό του απόσπασμα (σε αυτό το παράδειγμα `itemsContainer`). Αυτό προκαλεί την εκτέλεση του κώδικα του γονικού αποσπάσματος, αλλά στη συνέχεια αποστέλλονται στο πρόγραμμα περιήγησης μόνο τα επιμέρους αποσπάσματά του. Αν θέλετε να στείλετε μόνο ένα από τα υπο-στοιχεία, πρέπει να τροποποιήσετε την είσοδο για το γονικό απόσπασμα ώστε να μην παράγει τα άλλα υπο-στοιχεία.
+```latte
+
+ {foreach $items as $id => $item}
+ - {$item}
+ {/foreach}
+
+```
-Στο παραπάνω παράδειγμα πρέπει να βεβαιωθείτε ότι για μια αίτηση AJAX θα προστεθεί μόνο ένα στοιχείο στον πίνακα `$list`, επομένως ο βρόχος `foreach` θα εκτυπώσει μόνο ένα δυναμικό απόσπασμα.
+Και θα ξανασχεδιάσουμε τόσο το μεμονωμένο απόσπασμα όσο και ολόκληρη την υπερκείμενη περιοχή:
```php
-class HomePresenter extends Nette\Application\UI\Presenter
-{
- /**
- * This method returns data for the list.
- * Usually this would just request the data from a model.
- * For the purpose of this example, the data is hard-coded.
- */
- private function getTheWholeList(): array
- {
- return [
- 'First',
- 'Second',
- 'Third',
- ];
- }
-
- public function renderDefault(): void
- {
- if (!isset($this->template->list)) {
- $this->template->list = $this->getTheWholeList();
- }
- }
-
- public function handleUpdate(int $id): void
- {
- $this->template->list = $this->isAjax()
- ? []
- : $this->getTheWholeList();
- $this->template->list[$id] = 'Updated item';
- $this->redrawControl('itemsContainer');
- }
-}
+$this->redrawControl('itemsContainer');
+$this->redrawControl('item-1');
```
+Είναι επίσης σημαντικό να διασφαλίσουμε ότι ο πίνακας `$items` περιέχει μόνο τα στοιχεία που πρέπει να επανασχεδιαστούν.
-Αποσπάσματα σε συμπεριλαμβανόμενο πρότυπο .[#toc-snippets-in-an-included-template]
-==================================================================================
-
-Μπορεί να συμβεί το απόσπασμα να βρίσκεται σε ένα πρότυπο το οποίο συμπεριλαμβάνεται από ένα διαφορετικό πρότυπο. Σε αυτή την περίπτωση πρέπει να τυλίξουμε τον κώδικα συμπερίληψης στο δεύτερο πρότυπο με την ετικέτα `snippetArea`, και στη συνέχεια να ξανασχεδιάσουμε τόσο το snippetArea όσο και το πραγματικό απόσπασμα.
-
-Η ετικέτα `snippetArea` διασφαλίζει ότι ο κώδικας στο εσωτερικό της εκτελείται, αλλά μόνο το πραγματικό απόσπασμα στο συμπεριλαμβανόμενο πρότυπο αποστέλλεται στο πρόγραμμα περιήγησης.
+Κατά την εισαγωγή ενός άλλου προτύπου στο κύριο με τη χρήση της ετικέτας `{include}`, το οποίο έχει αποσπάσματα, είναι απαραίτητο να τυλίξετε και πάλι το συμπεριλαμβανόμενο πρότυπο σε ένα `snippetArea` και να ακυρώσετε τόσο το απόσπασμα όσο και την περιοχή μαζί:
```latte
-{* parent.latte *}
-{snippetArea wrapper}
- {include 'child.latte'}
+{snippetArea include}
+ {include 'included.latte'}
{/snippetArea}
```
+
```latte
-{* child.latte *}
+{* included.latte *}
{snippet item}
-...
+ ...
{/snippet}
```
+
```php
-$this->redrawControl('wrapper');
+$this->redrawControl('include');
$this->redrawControl('item');
```
-Μπορείτε επίσης να το συνδυάσετε με δυναμικά αποσπάσματα.
+Αποσπάσματα σε στοιχεία .[#toc-snippets-in-components]
+------------------------------------------------------
-Προσθήκη και διαγραφή .[#toc-adding-and-deleting]
-=================================================
-
-Εάν προσθέσετε ένα νέο στοιχείο στη λίστα και ακυρώσετε το `itemsContainer`, η αίτηση AJAX επιστρέφει αποσπάσματα που περιλαμβάνουν το νέο στοιχείο, αλλά ο χειριστής javascript δεν θα είναι σε θέση να το αποδώσει. Αυτό συμβαίνει επειδή δεν υπάρχει κανένα στοιχείο HTML με το νεοδημιουργηθέν ID.
-
-Σε αυτή την περίπτωση, ο απλούστερος τρόπος είναι να τυλίξετε ολόκληρη τη λίστα σε ένα ακόμη απόσπασμα και να τα ακυρώσετε όλα:
+Μπορείτε να δημιουργήσετε αποσπάσματα μέσα σε [στοιχεία |components] και η Nette θα τα ανασχεδιάσει αυτόματα. Ωστόσο, υπάρχει ένας συγκεκριμένος περιορισμός: για να ξανασχεδιάσει αποσπάσματα, καλεί τη μέθοδο `render()` χωρίς καμία παράμετρο. Έτσι, η μετάδοση παραμέτρων στο πρότυπο δεν θα λειτουργήσει:
```latte
-{snippet wholeList}
-
- {foreach $list as $id => $item}
- - {$item} update
- {/foreach}
-
-{/snippet}
-Add
+OK
+{control productGrid}
+
+will not work:
+{control productGrid $arg, $arg}
+{control productGrid:paginator}
```
+
+Αποστολή δεδομένων χρήστη .[#toc-sending-user-data]
+---------------------------------------------------
+
+Μαζί με τα αποσπάσματα, μπορείτε να στείλετε οποιαδήποτε πρόσθετα δεδομένα στον πελάτη. Απλά γράψτε τα στο αντικείμενο `payload`:
+
```php
-public function handleAdd(): void
+public function actionDelete(int $id): void
{
- $this->template->list = $this->getTheWholeList();
- $this->template->list[] = 'New one';
- $this->redrawControl('wholeList');
+ //...
+ if ($this->isAjax()) {
+ $this->payload->message = 'Success';
+ }
}
```
-Το ίδιο ισχύει και για τη διαγραφή ενός στοιχείου. Θα ήταν δυνατό να στείλετε κενό snippet, αλλά συνήθως οι λίστες μπορούν να είναι σελιδοποιημένες και θα ήταν περίπλοκο να υλοποιήσετε τη διαγραφή ενός στοιχείου και τη φόρτωση ενός άλλου (το οποίο βρισκόταν σε διαφορετική σελίδα της σελιδοποιημένης λίστας).
-
-Αποστολή παραμέτρων στο συστατικό .[#toc-sending-parameters-to-component]
-=========================================================================
+Παράμετροι αποστολής .[#toc-sending-parameters]
+===============================================
Όταν στέλνουμε παραμέτρους στο στοιχείο μέσω αίτησης AJAX, είτε πρόκειται για παραμέτρους σήματος είτε για μόνιμες παραμέτρους, πρέπει να παρέχουμε το συνολικό τους όνομα, το οποίο περιέχει επίσης το όνομα του στοιχείου. Το πλήρες όνομα της παραμέτρου επιστρέφει η μέθοδος `getParameterId()`.
```js
-$.getJSON(
- {link changeCountBasket!},
- {
- {$control->getParameterId('id')}: id,
- {$control->getParameterId('count')}: count
- }
-});
+let url = new URL({link //foo!});
+url.searchParams.set({$control->getParameterId('bar')}, bar);
+
+fetch(url, {
+ headers: {'X-Requested-With': 'XMLHttpRequest'},
+})
```
-Και χειρίζεται τη μέθοδο με s αντίστοιχες παραμέτρους στο συστατικό.
+Μια μέθοδος χειρισμού με τις αντίστοιχες παραμέτρους στο συστατικό:
```php
-public function handleChangeCountBasket(int $id, int $count): void
+public function handleFoo(int $bar): void
{
-
}
```
diff --git a/application/el/bootstrap.texy b/application/el/bootstrap.texy
index 71f6028048..3f5515fbd3 100644
--- a/application/el/bootstrap.texy
+++ b/application/el/bootstrap.texy
@@ -20,18 +20,44 @@ use Nette\Bootstrap\Configurator;
class Bootstrap
{
- public static function boot(): Configurator
+ private Configurator $configurator;
+ private string $rootDir;
+
+ public function __construct()
+ {
+ $this->rootDir = dirname(__DIR__);
+ // Ο διαμορφωτής είναι υπεύθυνος για τη ρύθμιση του περιβάλλοντος και των υπηρεσιών της εφαρμογής.
+ $this->configurator = new Configurator;
+ // Ορίστε τον κατάλογο για τα προσωρινά αρχεία που παράγονται από τη Nette (π.χ. μεταγλωττισμένα πρότυπα)
+ $this->configurator->setTempDirectory($this->rootDir . '/temp');
+ }
+
+ public function bootWebApplication(): Nette\DI\Container
+ {
+ $this->initializeEnvironment();
+ $this->setupContainer();
+ return $this->configurator->createContainer();
+ }
+
+ private function initializeEnvironment(): void
{
- $appDir = dirname(__DIR__);
- $configurator = new Configurator;
- //$configurator->setDebugMode('secret@23.75.345.200');
- $configurator->enableTracy($appDir . '/log');
- $configurator->setTempDirectory($appDir . '/temp');
- $configurator->createRobotLoader()
+ // Η Nette είναι έξυπνη και η λειτουργία ανάπτυξης ενεργοποιείται αυτόματα,
+ // ή μπορείτε να την ενεργοποιήσετε για μια συγκεκριμένη διεύθυνση IP ξεσχολιάζοντας την ακόλουθη γραμμή:
+ // $this->configurator->setDebugMode('secret@23.75.345.200'),
+
+ // Ενεργοποιεί το Tracy: το απόλυτο εργαλείο αποσφαλμάτωσης "ελβετικό μαχαίρι του στρατού".
+ $this->configurator->enableTracy($this->rootDir . '/log');
+
+ // RobotLoader: αυτόματη φόρτωση όλων των κλάσεων στον δεδομένο κατάλογο
+ $this->configurator->createRobotLoader()
->addDirectory(__DIR__)
->register();
- $configurator->addConfig($appDir . '/config/common.neon');
- return $configurator;
+ }
+
+ private function setupContainer(): void
+ {
+ // Φόρτωση αρχείων διαμόρφωσης
+ $this->configurator->addConfig($this->rootDir . '/config/common.neon');
}
}
```
@@ -40,16 +66,15 @@ class Bootstrap
index.php .[#toc-index-php]
===========================
-Στην περίπτωση των διαδικτυακών εφαρμογών, το αρχικό αρχείο είναι το `index.php`, το οποίο βρίσκεται στον δημόσιο κατάλογο `www/`. Επιτρέπει στην κλάση `Bootstrap` να αρχικοποιήσει το περιβάλλον και να επιστρέψει το `$configurator` που δημιουργεί το DI container. Στη συνέχεια αποκτά την υπηρεσία `Application`, η οποία εκτελεί την εφαρμογή ιστού:
+Στην περίπτωση των διαδικτυακών εφαρμογών, το πρωτεύον αρχείο είναι το `index.php`, το οποίο βρίσκεται στον [δημόσιο κατάλογο |directory-structure#public-directory-www] `www/`. Αυτό θα έχει την κλάση Bootstrap να αρχικοποιεί το περιβάλλον και να παράγει ένα δοχείο DI. Στη συνέχεια, παίρνει από αυτό την υπηρεσία `Application`, η οποία εκκινεί την εφαρμογή ιστού:
```php
-// αρχικοποίηση του περιβάλλοντος + λήψη του αντικειμένου Configurator
-$configurator = App\Bootstrap::boot();
-// Δημιουργία ενός δοχείου DI
-$container = $configurator->createContainer();
+$bootstrap = new App\Bootstrap;
+// Αρχικοποίηση του περιβάλλοντος + δημιουργία ενός δοχείου DI
+$container = $bootstrap->bootWebApplication();
// Το δοχείο DI δημιουργεί ένα αντικείμενο Nette\Application\Application
$application = $container->getByType(Nette\Application\Application::class);
-// έναρξη της εφαρμογής Nette
+// Εκκίνηση της εφαρμογής Nette και χειρισμός της εισερχόμενης αίτησης
$application->run();
```
@@ -59,26 +84,42 @@ $application->run();
Λειτουργία ανάπτυξης έναντι λειτουργίας παραγωγής .[#toc-development-vs-production-mode]
========================================================================================
-Η Nette διακρίνει μεταξύ δύο βασικών τρόπων εκτέλεσης μιας αίτησης: ανάπτυξη και παραγωγή. Η λειτουργία ανάπτυξης επικεντρώνεται στη μέγιστη άνεση του προγραμματιστή, εμφανίζεται το Tracy, η προσωρινή μνήμη ενημερώνεται αυτόματα όταν αλλάζουν τα πρότυπα ή η διαμόρφωση του DI container, κ.λπ. Η λειτουργία παραγωγής επικεντρώνεται στην απόδοση, το Tracy καταγράφει μόνο τα σφάλματα και δεν ελέγχονται οι αλλαγές των προτύπων και άλλων αρχείων.
+Η Nette συμπεριφέρεται διαφορετικά ανάλογα με το αν εκτελείται σε διακομιστή ανάπτυξης ή παραγωγής:
+
+🛠️ Λειτουργία ανάπτυξης:
+ - Εμφανίζει τη γραμμή εντοπισμού σφαλμάτων του Tracy με χρήσιμες πληροφορίες (π.χ. ερωτήματα SQL, χρόνος εκτέλεσης, χρήση μνήμης).
+ - Εμφανίζει μια λεπτομερή σελίδα σφαλμάτων με ίχνη κλήσεων συναρτήσεων και περιεχόμενα μεταβλητών όταν εμφανίζεται σφάλμα.
+ - Ανανεώνει αυτόματα την προσωρινή μνήμη όταν τροποποιούνται πρότυπα Latte, αρχεία ρυθμίσεων κ.λπ.
+
+
+🚀 Λειτουργία παραγωγής:
+ - Δεν εμφανίζει καμία πληροφορία εντοπισμού σφαλμάτων- όλα τα σφάλματα καταγράφονται.
+ - Εμφανίζει μια σελίδα `ErrorPresenter` ή μια γενική σελίδα "Σφάλμα διακομιστή" όταν προκύψει σφάλμα.
+ - Η προσωρινή μνήμη δεν ανανεώνεται ποτέ αυτόματα!
+ - Βελτιστοποιημένη για ταχύτητα και ασφάλεια.
-Η επιλογή της λειτουργίας γίνεται με αυτόματη ανίχνευση, οπότε συνήθως δεν χρειάζεται να ρυθμίσετε ή να αλλάξετε κάτι χειροκίνητα. Η κατάσταση λειτουργίας είναι development εάν η εφαρμογή εκτελείται στο localhost (δηλαδή στη διεύθυνση IP `127.0.0.1` ή `::1`) και δεν υπάρχει proxy (δηλαδή η επικεφαλίδα HTTP του). Διαφορετικά, εκτελείται σε κατάσταση παραγωγής.
+
+Η λειτουργία καθορίζεται αυτόματα, οπότε στις περισσότερες περιπτώσεις δεν χρειάζεται να τη ρυθμίσετε ή να την αλλάξετε χειροκίνητα:
+
+- Λειτουργία ανάπτυξης: `127.0.0.1` ή `::1`), εκτός εάν χρησιμοποιείται ένας διακομιστής μεσολάβησης (δηλ. με βάση τις επικεφαλίδες HTTP).
+- Λειτουργία παραγωγής: Ενεργός παντού αλλού.
Αν θέλετε να ενεργοποιήσετε τη λειτουργία ανάπτυξης σε άλλες περιπτώσεις, για παράδειγμα, για προγραμματιστές που έχουν πρόσβαση από μια συγκεκριμένη διεύθυνση IP, μπορείτε να χρησιμοποιήσετε τη διεύθυνση `setDebugMode()`:
```php
-$configurator->setDebugMode('23.75.345.200'); // μία ή περισσότερες διευθύνσεις IP
+$this->configurator->setDebugMode('23.75.345.200'); // μία ή περισσότερες διευθύνσεις IP
```
Συνιστούμε οπωσδήποτε τον συνδυασμό μιας διεύθυνσης IP με ένα cookie. Θα αποθηκεύσουμε ένα μυστικό token στο cookie `nette-debug`, π.χ. `secret1234`, και η λειτουργία ανάπτυξης θα ενεργοποιηθεί για τους προγραμματιστές με αυτόν τον συνδυασμό IP και cookie.
```php
-$configurator->setDebugMode('secret1234@23.75.345.200');
+$this->configurator->setDebugMode('secret1234@23.75.345.200');
```
Μπορούμε επίσης να απενεργοποιήσουμε εντελώς τη λειτουργία προγραμματιστή, ακόμη και για το localhost:
```php
-$configurator->setDebugMode(false);
+$this->configurator->setDebugMode(false);
```
Σημειώστε ότι η τιμή `true` ενεργοποιεί τη λειτουργία προγραμματιστή με σκληρό τρόπο, κάτι που δεν πρέπει ποτέ να συμβαίνει σε έναν διακομιστή παραγωγής.
@@ -90,7 +131,7 @@ $configurator->setDebugMode(false);
Για εύκολη αποσφαλμάτωση, θα ενεργοποιήσουμε το σπουδαίο εργαλείο [Tracy |tracy:]. Στη λειτουργία προγραμματιστή απεικονίζει τα σφάλματα και στη λειτουργία παραγωγής καταγράφει τα σφάλματα στον καθορισμένο κατάλογο:
```php
-$configurator->enableTracy($appDir . '/log');
+$this->configurator->enableTracy($this->rootDir . '/log');
```
@@ -100,7 +141,7 @@ $configurator->enableTracy($appDir . '/log');
Η Nette χρησιμοποιεί την κρυφή μνήμη για το DI container, το RobotLoader, τα πρότυπα κ.λπ. Ως εκ τούτου, είναι απαραίτητο να ορίσετε τη διαδρομή προς τον κατάλογο όπου θα αποθηκεύεται η προσωρινή μνήμη:
```php
-$configurator->setTempDirectory($appDir . '/temp');
+$this->configurator->setTempDirectory($this->rootDir . '/temp');
```
Σε Linux ή macOS, ορίστε τα [δικαιώματα εγγραφής |nette:troubleshooting#Setting directory permissions] για τους καταλόγους `log/` και `temp/`.
@@ -112,7 +153,7 @@ RobotLoader .[#toc-robotloader]
Συνήθως, θα θέλουμε να φορτώνουμε αυτόματα τις κλάσεις χρησιμοποιώντας [τον RobotLoader |robot-loader:], οπότε πρέπει να τον εκκινήσουμε και να τον αφήσουμε να φορτώσει κλάσεις από τον κατάλογο όπου βρίσκεται το `Bootstrap.php` (δηλαδή το `__DIR__`) και όλους τους υποκαταλόγους του:
```php
-$configurator->createRobotLoader()
+$this->configurator->createRobotLoader()
->addDirectory(__DIR__)
->register();
```
@@ -126,7 +167,7 @@ $configurator->createRobotLoader()
Το Configurator σας επιτρέπει να καθορίσετε μια ζώνη ώρας για την εφαρμογή σας.
```php
-$configurator->setTimeZone('Europe/Prague');
+$this->configurator->setTimeZone('Europe/Prague');
```
@@ -143,16 +184,17 @@ $configurator->setTimeZone('Europe/Prague');
Τα αρχεία διαμόρφωσης φορτώνονται με τη χρήση του `addConfig()`:
```php
-$configurator->addConfig($appDir . '/config/common.neon');
+$this->configurator->addConfig($this->rootDir . '/config/common.neon');
```
Η μέθοδος `addConfig()` μπορεί να κληθεί πολλές φορές για την προσθήκη πολλών αρχείων.
```php
-$configurator->addConfig($appDir . '/config/common.neon');
-$configurator->addConfig($appDir . '/config/local.neon');
+$configDir = $this->rootDir . '/config';
+$this->configurator->addConfig($configDir . '/common.neon');
+$this->configurator->addConfig($configDir . '/services.neon');
if (PHP_SAPI === 'cli') {
- $configurator->addConfig($appDir . '/config/cli.php');
+ $this->configurator->addConfig($configDir . '/cli.php');
}
```
@@ -169,7 +211,7 @@ if (PHP_SAPI === 'cli') {
Οι παράμετροι που χρησιμοποιούνται σε αρχεία ρυθμίσεων μπορούν να οριστούν [στην ενότητα `parameters` |dependency-injection:configuration#parameters] και επίσης να μεταβιβαστούν (ή να αντικατασταθούν) από τη μέθοδο `addStaticParameters()` (έχει το ψευδώνυμο `addParameters()`). Είναι σημαντικό ότι διαφορετικές τιμές παραμέτρων προκαλούν τη δημιουργία πρόσθετων δοχείων DI, δηλαδή πρόσθετων κλάσεων.
```php
-$configurator->addStaticParameters([
+$this->configurator->addStaticParameters([
'projectId' => 23,
]);
```
@@ -183,7 +225,7 @@ $configurator->addStaticParameters([
Μπορούμε επίσης να προσθέσουμε δυναμικές παραμέτρους στο δοχείο, οι διαφορετικές τιμές τους, σε αντίθεση με τις στατικές παραμέτρους, δεν θα προκαλέσουν τη δημιουργία νέων δοχείων DI.
```php
-$configurator->addDynamicParameters([
+$this->configurator->addDynamicParameters([
'remoteIp' => $_SERVER['REMOTE_ADDR'],
]);
```
@@ -191,7 +233,7 @@ $configurator->addDynamicParameters([
Οι μεταβλητές περιβάλλοντος θα μπορούσαν εύκολα να γίνουν διαθέσιμες με τη χρήση δυναμικών παραμέτρων. Μπορούμε να έχουμε πρόσβαση σε αυτές μέσω της διεύθυνσης `%env.variable%` στα αρχεία ρυθμίσεων.
```php
-$configurator->addDynamicParameters([
+$this->configurator->addDynamicParameters([
'env' => getenv(),
]);
```
@@ -206,6 +248,7 @@ $configurator->addDynamicParameters([
- `%wwwDir%` είναι η απόλυτη διαδρομή προς τον κατάλογο που περιέχει το αρχείο καταχώρησης `index.php`
- `%tempDir%` είναι η απόλυτη διαδρομή προς τον κατάλογο για τα προσωρινά αρχεία
- `%vendorDir%` είναι η απόλυτη διαδρομή προς τον κατάλογο όπου ο Composer εγκαθιστά τις βιβλιοθήκες
+- `%rootDir%` είναι η απόλυτη διαδρομή προς τον ριζικό κατάλογο του έργου
- Το `%debugMode%` δηλώνει αν η εφαρμογή βρίσκεται σε κατάσταση αποσφαλμάτωσης.
- Το `%consoleMode%` δηλώνει αν η αίτηση υποβλήθηκε μέσω της γραμμής εντολών.
@@ -225,7 +268,7 @@ services:
Δημιουργούμε μια νέα περίπτωση και την εισάγουμε στο bootstrap:
```php
-$configurator->addServices([
+$this->configurator->addServices([
'myservice' => new App\Model\MyCustomService('foobar'),
]);
```
@@ -234,13 +277,21 @@ $configurator->addServices([
Διαφορετικά περιβάλλοντα .[#toc-different-environments]
=======================================================
-Μπορείτε να προσαρμόσετε την τάξη `Bootstrap` ανάλογα με τις ανάγκες σας. Μπορείτε να προσθέσετε παραμέτρους στη μέθοδο `boot()` για να διαφοροποιήσετε τα έργα ιστού ή να προσθέσετε άλλες μεθόδους, όπως η `bootForTests()`, η οποία αρχικοποιεί το περιβάλλον για δοκιμές μονάδας, η `bootForCli()` για σενάρια που καλούνται από τη γραμμή εντολών κ.ο.κ.
+Μη διστάσετε να προσαρμόσετε την τάξη `Bootstrap` σύμφωνα με τις ανάγκες σας. Μπορείτε να προσθέσετε παραμέτρους στη μέθοδο `bootWebApplication()` για να διαφοροποιήσετε τα διάφορα web projects. Εναλλακτικά, μπορείτε να προσθέσετε άλλες μεθόδους, όπως `bootTestEnvironment()` για την αρχικοποίηση του περιβάλλοντος για δοκιμές μονάδας, `bootConsoleApplication()` για σενάρια που καλούνται από τη γραμμή εντολών κ.ο.κ.
```php
-public static function bootForTests(): Configurator
+public function bootTestEnvironment(): Nette\DI\Container
{
- $configurator = self::boot();
Tester\Environment::setup(); // Αρχικοποίηση Nette Tester
- return $configurator;
+ $this->setupContainer();
+ return $this->configurator->createContainer();
+}
+
+public function bootConsoleApplication(): Nette\DI\Container
+{
+ $this->configurator->setDebugMode(false);
+ $this->initializeEnvironment();
+ $this->setupContainer();
+ return $this->configurator->createContainer();
}
```
diff --git a/application/el/components.texy b/application/el/components.texy
index ae5fc64c2f..06fafcf4d4 100644
--- a/application/el/components.texy
+++ b/application/el/components.texy
@@ -230,6 +230,28 @@ $this->redirect(/* ... */); // και ανακατεύθυνση
```
+Επανακατεύθυνση μετά από ένα σήμα .[#toc-redirection-after-a-signal]
+====================================================================
+
+Μετά την επεξεργασία ενός σήματος συνιστωσών, ακολουθεί συχνά ανακατεύθυνση. Αυτή η κατάσταση είναι παρόμοια με τις φόρμες - μετά την υποβολή μιας φόρμας, κάνουμε επίσης ανακατεύθυνση για να αποτρέψουμε την εκ νέου υποβολή δεδομένων όταν η σελίδα ανανεώνεται στο πρόγραμμα περιήγησης.
+
+```php
+$this->redirect('this') // redirects to the current presenter and action
+```
+
+Δεδομένου ότι ένα συστατικό είναι ένα επαναχρησιμοποιήσιμο στοιχείο και συνήθως δεν πρέπει να έχει άμεση εξάρτηση από συγκεκριμένους παρουσιαστές, οι μέθοδοι `redirect()` και `link()` ερμηνεύουν αυτόματα την παράμετρο ως σήμα συστατικού:
+
+```php
+$this->redirect('click') // redirects to the 'click' signal of the same component
+```
+
+Εάν χρειάζεται να ανακατευθύνετε σε διαφορετικό παρουσιαστή ή ενέργεια, μπορείτε να το κάνετε μέσω του παρουσιαστή:
+
+```php
+$this->getPresenter()->redirect('Product:show'); // redirects to a different presenter/action
+```
+
+
Μόνιμες παράμετροι .[#toc-persistent-parameters]
================================================
@@ -347,7 +369,7 @@ services:
Τέλος, θα χρησιμοποιήσουμε αυτό το εργοστάσιο στον παρουσιαστή μας:
```php
-class PollPresenter extends Nette\UI\Application\Presenter
+class PollPresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private PollControlFactory $pollControlFactory,
@@ -380,7 +402,7 @@ interface PollControlFactory
Τα συστατικά σε μια εφαρμογή Nette είναι τα επαναχρησιμοποιήσιμα μέρη μιας διαδικτυακής εφαρμογής που ενσωματώνουμε σε σελίδες, τα οποία αποτελούν το αντικείμενο αυτού του κεφαλαίου. Ποιες ακριβώς είναι οι δυνατότητες ενός τέτοιου συστατικού;
1) είναι δυνατό να αποδοθεί σε ένα πρότυπο
-2) γνωρίζει ποιο μέρος του εαυτού του να αποδώσει κατά τη διάρκεια μιας [αίτησης AJAX |ajax#invalidation] (αποσπάσματα)
+2) γνωρίζει [ποιο μέρος του εαυτού του |ajax#snippets] να αποδώσει κατά τη διάρκεια μιας αίτησης AJAX (αποσπάσματα)
3) έχει τη δυνατότητα να αποθηκεύει την κατάστασή του σε μια διεύθυνση URL (μόνιμες παράμετροι)
4) έχει τη δυνατότητα να ανταποκρίνεται σε ενέργειες του χρήστη (σήματα)
5) δημιουργεί μια ιεραρχική δομή (όπου η ρίζα είναι ο παρουσιαστής)
@@ -430,7 +452,7 @@ class PaginatingControl extends Control
}
```
-Η αντίθετη διαδικασία, δηλαδή η συλλογή τιμών από persistent properites, αντιμετωπίζεται από τη μέθοδο `saveState()`.
+Η αντίθετη διαδικασία, δηλαδή η συλλογή τιμών από persistent properties, αντιμετωπίζεται από τη μέθοδο `saveState()`.
Σήματα σε βάθος .[#toc-signals-in-depth]
diff --git a/application/el/configuration.texy b/application/el/configuration.texy
index 01b036704b..5ac500455c 100644
--- a/application/el/configuration.texy
+++ b/application/el/configuration.texy
@@ -13,11 +13,15 @@ application:
# δείχνει τον πίνακα "Nette Application" στο Tracy BlueScreen?
debugger: ... # (bool) προεπιλογή true
- # θα καλείται ο παρουσιαστής σφαλμάτων σε περίπτωση σφάλματος;
- catchExceptions: ... # (bool) προεπιλεγμένη τιμή true σε κατάσταση παραγωγής
+ # θα κληθεί ο παρουσιαστής σφαλμάτων στο σφάλμα;
+ # έχει αποτέλεσμα μόνο σε λειτουργία προγραμματιστή
+ catchExceptions: ... # (bool) προεπιλογή true
# όνομα του error-presenter
- errorPresenter: Error # (string) προεπιλογή 'Nette:Error'
+ errorPresenter: Error # (string|array) προεπιλογή 'Nette:Error'
+
+ # ορίζει ψευδώνυμα για παρουσιαστές και εκδηλώσεις
+ aliases: ...
# ορίζει τους κανόνες για την επίλυση του ονόματος του παρουσιαστή σε μια κλάση
mapping: ...
@@ -27,11 +31,20 @@ application:
silentLinks: ... # (bool) προεπιλογή σε false
```
-Επειδή οι παρουσιαστές σφαλμάτων δεν καλούνται εξ ορισμού σε κατάσταση ανάπτυξης και τα σφάλματα εμφανίζονται από το Tracy, η αλλαγή της τιμής `catchExceptions` σε `true` βοηθάει στην επαλήθευση της σωστής λειτουργίας των παρουσιαστών σφαλμάτων κατά την ανάπτυξη.
+Από την έκδοση 3.2 του `nette/application` είναι δυνατό να ορίσετε ένα ζεύγος παρουσιαστών σφαλμάτων:
+
+```neon
+application:
+ errorPresenter:
+ 4xx: Error4xx # για την εξαίρεση Nette\Application\BadRequestException
+ 5xx: Error5xx # για άλλες εξαιρέσεις
+```
Η επιλογή `silentLinks` καθορίζει τον τρόπο με τον οποίο η Nette συμπεριφέρεται στη λειτουργία ανάπτυξης όταν η δημιουργία συνδέσμων αποτυγχάνει (για παράδειγμα, επειδή δεν υπάρχει παρουσιαστής κ.λπ.). Η προεπιλεγμένη τιμή `false` σημαίνει ότι η Nette ενεργοποιεί το `E_USER_WARNING`. Η ρύθμιση σε `true` καταστέλλει αυτό το μήνυμα σφάλματος. Σε περιβάλλον παραγωγής, το `E_USER_WARNING` ενεργοποιείται πάντα. Μπορούμε επίσης να επηρεάσουμε αυτή τη συμπεριφορά θέτοντας τη μεταβλητή του παρουσιαστή [$invalidLinkMode |creating-links#Invalid Links].
-Η [αντιστοίχιση ορίζει τους κανόνες |modules#mapping] με τους οποίους το όνομα της κλάσης προκύπτει από το όνομα του παρουσιαστή.
+Τα [ψευδώνυμα απλοποιούν την αναφορά σε |creating-links#aliases] συχνά χρησιμοποιούμενους παρουσιαστές.
+
+Η [αντιστοίχιση ορίζει τους κανόνες |directory-structure#Presenter Mapping] με τους οποίους το όνομα της κλάσης προκύπτει από το όνομα του παρουσιαστή.
Αυτόματη εγγραφή παρουσιαστών .[#toc-automatic-registration-of-presenters]
@@ -82,6 +95,9 @@ latte:
# ενεργοποιεί τον [έλεγχο του παραγόμενου κώδικα |latte:develop#Checking Generated Code]
phpLinter: ... # (string) η προεπιλογή είναι null
+ # ορίζει την τοπική γλώσσα
+ locale: cs_CZ # (string) η προεπιλογή είναι null
+
# κλάση του $this->template
templateClass: App\MyTemplateClass # προεπιλογή σε Nette\Bridges\ApplicationLatte\DefaultTemplate
```
@@ -91,7 +107,7 @@ latte:
```neon
latte:
extensions:
- - Latte\Essential\TranslatorExtension
+ - Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)
```
/--comment
diff --git a/application/el/creating-links.texy b/application/el/creating-links.texy
index e36d982225..19ec14635d 100644
--- a/application/el/creating-links.texy
+++ b/application/el/creating-links.texy
@@ -38,7 +38,7 @@
detail
```
-Εάν η μέθοδος `ProductPresenter::renderShow()` δεν έχει στην υπογραφή της την `$lang`, μπορεί να διαβάσει την τιμή της παραμέτρου χρησιμοποιώντας την `$lang = $this->getParameter('lang')`.
+Εάν η μέθοδος `ProductPresenter::renderShow()` δεν έχει στην υπογραφή της την `$lang`, μπορεί να ανακτήσει την τιμή της παραμέτρου χρησιμοποιώντας την `$lang = $this->getParameter('lang')` ή από την [ιδιότητα |presenters#Request Parameters].
Εάν οι παράμετροι είναι αποθηκευμένες σε πίνακα, μπορούν να επεκταθούν με τον τελεστή `...` (ή `(expand)` στο Latte 2.x):
@@ -103,7 +103,7 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']);
home
```
-Οι σύνδεσμοι μπορούν επίσης να παραπέμπουν σε άλλες [ενότητες |modules]. Εδώ, οι σύνδεσμοι διακρίνονται σε σχετικούς με τις υποενότητες ή απόλυτους. Η αρχή είναι ανάλογη με τις διαδρομές δίσκου, μόνο που αντί για κάθετους υπάρχουν άνω και κάτω τελεία. Ας υποθέσουμε ότι ο πραγματικός παρουσιαστής είναι μέρος της ενότητας `Front`, τότε θα γράψουμε:
+Οι σύνδεσμοι μπορούν επίσης να παραπέμπουν σε άλλες [ενότητες |directory-structure#Presenters and Templates]. Εδώ, οι σύνδεσμοι διακρίνονται σε σχετικούς με τις υποενότητες ή απόλυτους. Η αρχή είναι ανάλογη με τις διαδρομές δίσκου, μόνο που αντί για κάθετους υπάρχουν άνω και κάτω τελεία. Ας υποθέσουμε ότι ο πραγματικός παρουσιαστής είναι μέρος της ενότητας `Front`, τότε θα γράψουμε:
```latte
link to Front:Shop:Product:show
@@ -140,7 +140,7 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']);
refresh
```
-Ταυτόχρονα, όλες οι παράμετροι που καθορίζονται στην υπογραφή της εντολής `render()` ή `action()` μεταφέρονται. Έτσι, αν βρισκόμαστε στις σελίδες `Product:show` και `id:123`, ο σύνδεσμος προς την `this` θα μεταφέρει και αυτή την παράμετρο.
+Ταυτόχρονα, όλες οι παράμετροι που καθορίζονται στην υπογραφή της `action()` ή `render()` μεθόδου, εάν η `action()` δεν έχει οριστεί, μεταφέρονται. Έτσι, αν βρισκόμαστε στις σελίδες `Product:show` και `id:123`, ο σύνδεσμος προς την `this` θα μεταφέρει και αυτή την παράμετρο.
Φυσικά, είναι δυνατόν να καθορίσετε τις παραμέτρους απευθείας:
@@ -213,7 +213,7 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']);
Αν θέλουμε να συνδέσουμε με παρουσιαστές στο πρότυπο συστατικού, χρησιμοποιούμε την ετικέτα `{plink}`:
```latte
-home
+home
```
ή στον κώδικα
@@ -223,6 +223,30 @@ $this->getPresenter()->link('Home:default')
```
+Ψευδώνυμα .[#toc-aliases]{data-version:v3.2.2}
+==============================================
+
+Μερικές φορές είναι χρήσιμο να αντιστοιχίσετε ένα εύκολα απομνημονεύσιμο ψευδώνυμο σε ένα ζεύγος Παρουσιαστής:ενέργεια. Για παράδειγμα, θα μπορούσατε να ονομάσετε την αρχική σελίδα `Front:Home:default` απλά ως `home` ή `Admin:Dashboard:default` ως `admin`.
+
+Τα ψευδώνυμα ορίζονται στη [ρύθμιση παρα |configuration] μέτρων κάτω από το κλειδί `application › aliases`:
+
+```neon
+application:
+ aliases:
+ home: Front:Home:default
+ admin: Admin:Dashboard:default
+ sign: Front:Sign:in
+```
+
+Στους συνδέσμους, γράφονται χρησιμοποιώντας το σύμβολο at, για παράδειγμα:
+
+```latte
+administration
+```
+
+Υποστηρίζονται σε όλες τις μεθόδους που λειτουργούν με συνδέσμους, όπως το `redirect()` και παρόμοια.
+
+
Άκυροι σύνδεσμοι .[#toc-invalid-links]
======================================
@@ -257,6 +281,6 @@ LinkGenerator .[#toc-linkgenerator]
LinkGenerator είναι μια υπηρεσία που μπορείτε να έχετε περάσει μέσω του κατασκευαστή και στη συνέχεια να δημιουργήσετε συνδέσμους χρησιμοποιώντας τη μέθοδό του `link()`.
-Υπάρχει μια διαφορά σε σχέση με τους παρουσιαστές. Το LinkGenerator δημιουργεί όλους τους συνδέσμους ως απόλυτες διευθύνσεις URL. Επιπλέον, δεν υπάρχει "τρέχων παρουσιαστής", οπότε δεν είναι δυνατόν να καθορίσετε μόνο το όνομα της ενέργειας `link('default')` ή τις σχετικές διαδρομές προς τις [ενότητες |modules].
+Σε σύγκριση με τους παρουσιαστές, υπάρχει διαφορά. Το LinkGenerator δημιουργεί όλους τους συνδέσμους απευθείας ως απόλυτες διευθύνσεις URL. Επίσης, δεν υπάρχει "πραγματικός παρουσιαστής", οπότε δεν μπορείτε απλώς να αναφέρετε το όνομα της ενέργειας `link('default')` ως στόχο ή να αναφέρετε σχετικές διαδρομές σε ενότητες.
Οι άκυροι σύνδεσμοι πάντα προκαλούν `Nette\Application\UI\InvalidLinkException`.
diff --git a/application/el/directory-structure.texy b/application/el/directory-structure.texy
new file mode 100644
index 0000000000..d7fba83719
--- /dev/null
+++ b/application/el/directory-structure.texy
@@ -0,0 +1,526 @@
+Δομή καταλόγου της εφαρμογής
+****************************
+
+
+
+Πώς να σχεδιάσετε μια σαφή και κλιμακούμενη δομή καταλόγου για έργα στο Nette Framework; Θα σας δείξουμε δοκιμασμένες πρακτικές που θα σας βοηθήσουν να οργανώσετε τον κώδικά σας. Θα μάθετε:
+
+- πώς να **δομήσετε λογικά** την εφαρμογή σε καταλόγους
+- πώς να σχεδιάζετε τη δομή ώστε να **κλιμακώνεται καλά** καθώς το έργο μεγαλώνει
+- ποιες είναι οι **πιθανές εναλλακτικές λύσεις** και τα πλεονεκτήματα ή μειονεκτήματά τους
+
+
+
+
+Είναι σημαντικό να αναφέρουμε ότι το ίδιο το Nette Framework δεν επιμένει σε κάποια συγκεκριμένη δομή. Είναι σχεδιασμένο ώστε να προσαρμόζεται εύκολα σε οποιεσδήποτε ανάγκες και προτιμήσεις.
+
+
+Βασική δομή έργου .[#toc-basic-project-structure]
+=================================================
+
+Αν και το Nette Framework δεν υπαγορεύει κάποια σταθερή δομή καταλόγων, υπάρχει μια αποδεδειγμένη προεπιλεγμένη διάταξη με τη μορφή [Web Project |https://github.com/nette/web-project]:
+
+/--pre
+web-project/
+├── app/ ← κατάλογος εφαρμογών
+├── assets/ ← SCSS, αρχεία JS, εικόνες..., εναλλακτικά resources/
+├── bin/ ← σενάρια γραμμής εντολών
+├── config/ ← διαμόρφωση
+├── log/ ← καταγεγραμμένα σφάλματα
+├── temp/ ← προσωρινά αρχεία, κρυφή μνήμη
+├── tests/ ← δοκιμές
+├── vendor/ ← βιβλιοθήκες που εγκαθίστανται από τον Composer
+└── www/ ← δημόσιος κατάλογος (document-root)
+\--
+
+Μπορείτε να τροποποιήσετε ελεύθερα αυτή τη δομή σύμφωνα με τις ανάγκες σας - να μετονομάσετε ή να μετακινήσετε φακέλους. Στη συνέχεια, πρέπει απλώς να προσαρμόσετε τις σχετικές διαδρομές προς τους καταλόγους στο `Bootstrap.php` και ενδεχομένως στο `composer.json`. Δεν χρειάζεται τίποτα άλλο, ούτε πολύπλοκη αναδιαμόρφωση, ούτε συνεχείς αλλαγές. Το Nette διαθέτει έξυπνη αυτόματη ανίχνευση και αναγνωρίζει αυτόματα τη θέση της εφαρμογής, συμπεριλαμβανομένης της βάσης URL της.
+
+
+Αρχές οργάνωσης κώδικα .[#toc-code-organization-principles]
+===========================================================
+
+Όταν εξερευνάτε για πρώτη φορά ένα νέο έργο, θα πρέπει να είστε σε θέση να προσανατολιστείτε γρήγορα. Φανταστείτε να κάνετε κλικ στον κατάλογο `app/Model/` και να δείτε αυτή τη δομή:
+
+/--pre
+app/Model/
+├── Services/
+├── Repositories/
+└── Entities/
+\--
+
+Από αυτήν, θα μάθετε μόνο ότι το έργο χρησιμοποιεί κάποιες υπηρεσίες, αποθετήρια και οντότητες. Δεν θα μάθετε τίποτα για τον πραγματικό σκοπό της εφαρμογής.
+
+Ας δούμε μια διαφορετική προσέγγιση - **οργάνωση με βάση τους τομείς**:
+
+/--pre
+app/Model/
+├── Cart/
+├── Payment/
+├── Order/
+└── Product/
+\--
+
+Αυτό είναι διαφορετικό - με την πρώτη ματιά είναι σαφές ότι πρόκειται για ιστότοπο ηλεκτρονικού εμπορίου. Τα ίδια τα ονόματα των καταλόγων αποκαλύπτουν τι μπορεί να κάνει η εφαρμογή - λειτουργεί με πληρωμές, παραγγελίες και προϊόντα.
+
+Η πρώτη προσέγγιση (οργάνωση με βάση τον τύπο της κλάσης) φέρνει αρκετά προβλήματα στην πράξη: ο κώδικας που σχετίζεται λογικά είναι διασκορπισμένος σε διαφορετικούς φακέλους και πρέπει να μεταπηδήσετε μεταξύ τους. Ως εκ τούτου, θα οργανώσουμε κατά τομείς.
+
+
+Χώροι ονομάτων .[#toc-namespaces]
+---------------------------------
+
+Είναι συμβατικό η δομή καταλόγου να αντιστοιχεί σε χώρους ονομάτων στην εφαρμογή. Αυτό σημαίνει ότι η φυσική θέση των αρχείων αντιστοιχεί στο χώρο ονομάτων τους. Για παράδειγμα, μια κλάση που βρίσκεται στο `app/Model/Product/ProductRepository.php` θα πρέπει να έχει χώρο ονομάτων `App\Model\Product`. Αυτή η αρχή βοηθά στον προσανατολισμό του κώδικα και απλοποιεί την αυτόματη φόρτωση.
+
+
+Ενικός και πληθυντικός αριθμός στα ονόματα .[#toc-singular-vs-plural-in-names]
+------------------------------------------------------------------------------
+
+Παρατηρήστε ότι χρησιμοποιούμε ενικό για τους κύριους καταλόγους εφαρμογών: `app`, `config`, `log`, `temp`, `www`. Το ίδιο ισχύει και στο εσωτερικό της εφαρμογής: `Model`, `Core`, `Presentation`. Αυτό συμβαίνει επειδή το καθένα αντιπροσωπεύει μια ενιαία έννοια.
+
+Ομοίως, το `app/Model/Product` αντιπροσωπεύει τα πάντα σχετικά με τα προϊόντα. Δεν το ονομάζουμε `Products` επειδή δεν είναι ένας φάκελος γεμάτος προϊόντα (που θα περιείχε αρχεία όπως `iphone.php`, `samsung.php`). Είναι ένας χώρος ονομάτων που περιέχει κλάσεις για την εργασία με προϊόντα - `ProductRepository.php`, `ProductService.php`.
+
+Ο φάκελος `app/Tasks` είναι στον πληθυντικό επειδή περιέχει ένα σύνολο αυτόνομων εκτελέσιμων σεναρίων - `CleanupTask.php`, `ImportTask.php`. Κάθε ένα από αυτά αποτελεί μια ανεξάρτητη μονάδα.
+
+Για λόγους συνέπειας, συνιστούμε να χρησιμοποιείτε:
+- Ενικός για χώρους ονομάτων που αντιπροσωπεύουν μια λειτουργική μονάδα (ακόμη και αν εργάζεστε με πολλαπλές οντότητες)
+- Πληθυντικός για συλλογές ανεξάρτητων μονάδων
+- Σε περίπτωση αβεβαιότητας ή αν δεν θέλετε να το σκεφτείτε, επιλέξτε τον ενικό
+
+
+Δημόσιος κατάλογος `www/` .[#toc-public-directory-www]
+======================================================
+
+Αυτός ο κατάλογος είναι ο μόνος προσβάσιμος από το διαδίκτυο (το λεγόμενο document-root). Συχνά μπορεί να συναντήσετε το όνομα `public/` αντί για `www/` - είναι απλώς θέμα σύμβασης και δεν επηρεάζει τη λειτουργικότητα. Ο κατάλογος περιέχει:
+- [Σημείο εισόδου |bootstrap#index.php] της εφαρμογής `index.php`
+- αρχείο `.htaccess` με κανόνες mod_rewrite (για τον Apache)
+- Στατικά αρχεία (CSS, JavaScript, εικόνες)
+- Ανεβασμένα αρχεία
+
+Για τη σωστή ασφάλεια της εφαρμογής, είναι ζωτικής σημασίας να έχετε [ρυθμίσει |nette:troubleshooting#how-to-change-or-remove-www-directory-from-url] σωστά [το document-root |nette:troubleshooting#how-to-change-or-remove-www-directory-from-url].
+
+.[note]
+Ποτέ μην τοποθετείτε το φάκελο `node_modules/` σε αυτόν τον κατάλογο - περιέχει χιλιάδες αρχεία που μπορεί να είναι εκτελέσιμα και δεν πρέπει να είναι προσβάσιμα από το κοινό.
+
+
+Κατάλογος εφαρμογών `app/` .[#toc-application-directory-app]
+============================================================
+
+Αυτός είναι ο κύριος κατάλογος με τον κώδικα της εφαρμογής. Βασική δομή:
+
+/--pre
+app/
+├── Core/ ← θέματα υποδομής
+├── Model/ ← επιχειρησιακή λογική
+├── Presentation/ ← παρουσιαστές και πρότυπα
+├── Tasks/ ← σενάρια εντολών
+└── Bootstrap.php ← κλάση bootstrap της εφαρμογής
+\--
+
+`Bootstrap.php` είναι η [κλάση εκκίνησης της εφαρμογής |bootstrap] που αρχικοποιεί το περιβάλλον, φορτώνει τη διαμόρφωση και δημιουργεί το δοχείο DI.
+
+Ας δούμε τώρα αναλυτικά τους επιμέρους υποκαταλόγους.
+
+
+Παρουσιαστές και πρότυπα .[#toc-presenters-and-templates]
+=========================================================
+
+Έχουμε το τμήμα παρουσίασης της εφαρμογής στον κατάλογο `app/Presentation`. Μια εναλλακτική λύση είναι το σύντομο `app/UI`. Εκεί βρίσκονται όλοι οι παρουσιαστές, τα πρότυπα τους και τυχόν βοηθητικές κλάσεις.
+
+Οργανώνουμε αυτό το επίπεδο ανά τομείς. Σε ένα σύνθετο έργο που συνδυάζει ηλεκτρονικό εμπόριο, blog και API, η δομή θα έμοιαζε ως εξής:
+
+/--pre
+app/Presentation/
+├── Shop/ ← frontend ηλεκτρονικού εμπορίου
+│ ├── Product/
+│ ├── Cart/
+│ └── Order/
+├── Blog/ ← blog
+│ ├── Home/
+│ └── Post/
+├── Admin/ ← διαχείριση
+│ ├── Dashboard/
+│ └── Products/
+└── Api/ ← Τελικά σημεία API
+ └── V1/
+\--
+
+Αντίθετα, για ένα απλό ιστολόγιο θα χρησιμοποιούσαμε αυτή τη δομή:
+
+/--pre
+app/Presentation/
+├── Front/ ← εμπρόσθιο άκρο ιστοσελίδας
+│ ├── Home/
+│ └── Post/
+├── Admin/ ← διαχείριση
+│ ├── Dashboard/
+│ └── Posts/
+├── Error/
+└── Export/ ← RSS, sitemaps κ.λπ.
+\--
+
+Οι φάκελοι όπως `Home/` ή `Dashboard/` περιέχουν παρουσιαστές και πρότυπα. Οι φάκελοι όπως `Front/`, `Admin/` ή `Api/` ονομάζονται **modules**. Τεχνικά, πρόκειται για κανονικούς καταλόγους που χρησιμεύουν για τη λογική οργάνωση της εφαρμογής.
+
+Κάθε φάκελος με έναν παρουσιαστή περιέχει έναν παρουσιαστή με παρόμοιο όνομα και τα πρότυπά του. Για παράδειγμα, ο φάκελος `Dashboard/` περιέχει:
+
+/--pre
+Dashboard/
+├── DashboardPresenter.php ← παρουσιαστής
+└── default.latte ← πρότυπο
+\--
+
+Αυτή η δομή καταλόγων αντικατοπτρίζεται στους χώρους ονομάτων των κλάσεων. Για παράδειγμα, το `DashboardPresenter` βρίσκεται στο χώρο ονομάτων `App\Presentation\Admin\Dashboard` (βλ. [αντιστοίχιση παρουσιαστή |#presenter mapping]):
+
+```php
+namespace App\Presentation\Admin\Dashboard;
+
+class DashboardPresenter extends Nette\Application\UI\Presenter
+{
+ //...
+}
+```
+
+Αναφερόμαστε στον παρουσιαστή `Dashboard` μέσα στην ενότητα `Admin` στην εφαρμογή χρησιμοποιώντας τον συμβολισμό της άνω και κάτω τελείας ως `Admin:Dashboard`. Στη δράση του `default` στη συνέχεια ως `Admin:Dashboard:default`. Για ένθετες ενότητες χρησιμοποιούμε περισσότερες άνω και κάτω τελεία, για παράδειγμα `Shop:Order:Detail:default`.
+
+
+Ανάπτυξη ευέλικτης δομής .[#toc-flexible-structure-development]
+---------------------------------------------------------------
+
+Ένα από τα μεγάλα πλεονεκτήματα αυτής της δομής είναι το πόσο κομψά προσαρμόζεται στις αυξανόμενες ανάγκες του έργου. Ως παράδειγμα, ας πάρουμε το τμήμα που παράγει XML feeds. Αρχικά, έχουμε μια απλή φόρμα:
+
+/--pre
+Export/
+├── ExportPresenter.php ← ένας παρουσιαστής για όλες τις εξαγωγές
+├── sitemap.latte ← πρότυπο για χάρτη σελίδων
+└── feed.latte ← πρότυπο για RSS feed
+\--
+
+Με την πάροδο του χρόνου, προστίθενται περισσότεροι τύποι τροφοδοσίας και χρειαζόμαστε περισσότερη λογική για αυτούς... Κανένα πρόβλημα! Ο φάκελος `Export/` γίνεται απλά μια ενότητα:
+
+/--pre
+Export/
+├── Sitemap/
+│ ├── SitemapPresenter.php
+│ └── sitemap.latte
+└── Feed/
+ ├── FeedPresenter.php
+ ├── amazon.latte ← feed για την Amazon
+ └── ebay.latte ← τροφοδοσία για το eBay
+\--
+
+Απλά δημιουργήστε νέους υποφακέλους, χωρίστε τον κώδικα σε αυτούς και ενημερώστε τους συνδέσμους (π.χ. από `Export:feed` σε `Export:Feed:amazon`). Χάρη σε αυτό, μπορούμε να επεκτείνουμε σταδιακά τη δομή όπως χρειάζεται, το επίπεδο φωλιάσματος δεν περιορίζεται με κανέναν τρόπο.
+
+Για παράδειγμα, αν στη διαχείριση έχετε πολλούς παρουσιαστές που σχετίζονται με τη διαχείριση παραγγελιών, όπως `OrderDetail`, `OrderEdit`, `OrderDispatch` κ.λπ. μπορείτε να δημιουργήσετε μια ενότητα (φάκελο) `Order` για καλύτερη οργάνωση, η οποία θα περιέχει (φακέλους για) τους παρουσιαστές `Detail`, `Edit`, `Dispatch` και άλλους.
+
+
+Τοποθεσία προτύπου .[#toc-template-location]
+--------------------------------------------
+
+Στα προηγούμενα παραδείγματα, είδαμε ότι τα πρότυπα βρίσκονται απευθείας στο φάκελο με τον παρουσιαστή:
+
+/--pre
+Dashboard/
+├── DashboardPresenter.php ← παρουσιαστής
+├── DashboardTemplate.php ← προαιρετική κλάση προτύπου
+└── default.latte ← πρότυπο
+\--
+
+Αυτή η τοποθεσία αποδεικνύεται η πιο βολική στην πράξη - έχετε όλα τα σχετικά αρχεία στη διάθεσή σας.
+
+Εναλλακτικά, μπορείτε να τοποθετήσετε τα πρότυπα σε έναν υποφάκελο `templates/`. Η Nette υποστηρίζει και τις δύο παραλλαγές. Μπορείτε ακόμη και να τοποθετήσετε τα πρότυπα εντελώς εκτός του φακέλου `Presentation/`. Τα πάντα σχετικά με τις επιλογές τοποθέτησης προτύπων μπορείτε να βρείτε στο κεφάλαιο [Αναζήτηση προτύπων |templates#Template Lookup].
+
+
+Βοηθητικές κλάσεις και συστατικά .[#toc-helper-classes-and-components]
+----------------------------------------------------------------------
+
+Οι παρουσιαστές και τα πρότυπα συχνά συνοδεύονται από άλλα βοηθητικά αρχεία. Τα τοποθετούμε λογικά ανάλογα με το πεδίο εφαρμογής τους:
+
+1. **Απευθείας με τον παρουσιαστή** σε περίπτωση που πρόκειται για συγκεκριμένα στοιχεία για τον συγκεκριμένο παρουσιαστή:
+
+/--pre
+Product/
+├── ProductPresenter.php
+├── ProductGrid.php ← συστατικό για την καταχώριση προϊόντων
+└── FilterForm.php ← φόρμα για φιλτράρισμα
+\--
+
+2. **Για την ενότητα** - συνιστούμε τη χρήση του φακέλου `Accessory`, ο οποίος τοποθετείται τακτοποιημένα στην αρχή του αλφαβήτου:
+
+/--pre
+Front/
+├── Accessory/
+│ ├── NavbarControl.php ← συστατικά για frontend
+│ └── TemplateFilters.php
+├── Product/
+└── Cart/
+\--
+
+3. **Για ολόκληρη την εφαρμογή** - στον φάκελο `Presentation/Accessory/`:
+/--pre
+app/Presentation/
+├── Accessory/
+│ ├── LatteExtension.php
+│ └── TemplateFilters.php
+├── Front/
+└── Admin/
+\--
+
+Ή μπορείτε να τοποθετήσετε βοηθητικές κλάσεις όπως `LatteExtension.php` ή `TemplateFilters.php` στο φάκελο υποδομής `app/Core/Latte/`. Και τα συστατικά στον φάκελο `app/Components`. Η επιλογή εξαρτάται από τις συμβάσεις της ομάδας.
+
+
+Μοντέλο - Η καρδιά της εφαρμογής .[#toc-model-heart-of-the-application]
+=======================================================================
+
+Το μοντέλο περιέχει όλη την επιχειρησιακή λογική της εφαρμογής. Για την οργάνωσή του, ισχύει ο ίδιος κανόνας - δομούμε με βάση τους τομείς:
+
+/--pre
+app/Model/
+├── Payment/ ← τα πάντα για τις πληρωμές
+│ ├── PaymentFacade.php ← κύριο σημείο εισόδου
+│ ├── PaymentRepository.php
+│ ├── Payment.php ← οντότητα
+├── Order/ ← τα πάντα για τις παραγγελίες
+│ ├── OrderFacade.php
+│ ├── OrderRepository.php
+│ ├── Order.php
+└── Shipping/ ← τα πάντα για τη ναυτιλία
+\--
+
+Στο μοντέλο, συναντάτε συνήθως αυτούς τους τύπους κλάσεων:
+
+**Προσοψεις**: αντιπροσωπεύουν το κύριο σημείο εισόδου σε ένα συγκεκριμένο τομέα της εφαρμογής. Λειτουργούν ως ενορχηστρωτής που συντονίζει τη συνεργασία μεταξύ διαφορετικών υπηρεσιών για την υλοποίηση ολοκληρωμένων περιπτώσεων χρήσης (όπως "δημιουργία παραγγελίας" ή "επεξεργασία πληρωμής"). Κάτω από το επίπεδο ενορχήστρωσης, η όψη αποκρύπτει τις λεπτομέρειες υλοποίησης από την υπόλοιπη εφαρμογή, παρέχοντας έτσι μια καθαρή διεπαφή για την εργασία με τον συγκεκριμένο τομέα.
+
+```php
+class OrderFacade
+{
+ public function createOrder(Cart $cart): Order
+ {
+ // επικύρωση
+ // δημιουργία παραγγελίας
+ // αποστολή email
+ // εγγραφή σε στατιστικά στοιχεία
+ }
+}
+```
+
+**Υπηρεσίες**: εστιάζουν σε συγκεκριμένες επιχειρηματικές λειτουργίες εντός ενός τομέα. Σε αντίθεση με τις προσόψεις που ενορχηστρώνουν ολόκληρες περιπτώσεις χρήσης, μια υπηρεσία υλοποιεί συγκεκριμένη επιχειρηματική λογική (όπως υπολογισμούς τιμών ή επεξεργασία πληρωμών). Οι υπηρεσίες είναι συνήθως χωρίς κατάσταση και μπορούν να χρησιμοποιηθούν είτε από τις προσόψεις ως δομικά στοιχεία για πιο σύνθετες λειτουργίες, είτε απευθείας από άλλα μέρη της εφαρμογής για απλούστερες εργασίες.
+
+```php
+class PricingService
+{
+ public function calculateTotal(Order $order): Money
+ {
+ // υπολογισμός τιμών
+ }
+}
+```
+
+**Αποθήκες**: χειρίζονται όλη την επικοινωνία με την αποθήκευση δεδομένων, συνήθως μια βάση δεδομένων. Έργο τους είναι να φορτώνουν και να αποθηκεύουν οντότητες και να υλοποιούν μεθόδους για την αναζήτησή τους. Ένα αποθετήριο θωρακίζει την υπόλοιπη εφαρμογή από τις λεπτομέρειες υλοποίησης της βάσης δεδομένων και παρέχει μια αντικειμενοστραφή διεπαφή για την εργασία με τα δεδομένα.
+
+```php
+class OrderRepository
+{
+ public function find(int $id): ?Order
+ {
+ }
+
+ public function findByCustomer(int $customerId): array
+ {
+ }
+}
+```
+
+**Οντότητες**: αντικείμενα που αντιπροσωπεύουν τις κύριες επιχειρηματικές έννοιες της εφαρμογής, οι οποίες έχουν την ταυτότητά τους και αλλάζουν με την πάροδο του χρόνου. Συνήθως πρόκειται για κλάσεις που αντιστοιχίζονται σε πίνακες της βάσης δεδομένων με χρήση ORM (όπως το Nette Database Explorer ή το Doctrine). Οι οντότητες μπορούν να περιέχουν επιχειρηματικούς κανόνες σχετικά με τα δεδομένα τους και τη λογική επικύρωσης.
+
+```php
+// Οντότητα που αντιστοιχίζεται στον πίνακα παραγγελιών της βάσης δεδομένων
+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,
+ ]);
+ }
+}
+```
+
+**Αντικείμενα αξίας**: αμετάβλητα αντικείμενα που αντιπροσωπεύουν τιμές χωρίς δική τους ταυτότητα - για παράδειγμα, ένα χρηματικό ποσό ή μια διεύθυνση ηλεκτρονικού ταχυδρομείου. Δύο περιπτώσεις ενός αντικειμένου αξίας με τις ίδιες τιμές θεωρούνται πανομοιότυπες.
+
+
+Κώδικας υποδομής .[#toc-infrastructure-code]
+============================================
+
+Ο φάκελος `Core/` (ή επίσης `Infrastructure/`) φιλοξενεί τα τεχνικά θεμέλια της εφαρμογής. Ο κώδικας υποδομής συνήθως περιλαμβάνει:
+
+/--pre
+app/Core/
+├── Router/ ← δρομολόγηση και διαχείριση URL
+│ └── RouterFactory.php
+├── Security/ ← αυθεντικοποίηση και εξουσιοδότηση
+│ ├── Authenticator.php
+│ └── Authorizator.php
+├── Logging/ ← καταγραφή και παρακολούθηση
+│ ├── SentryLogger.php
+│ └── FileLogger.php
+├── Cache/ ← επίπεδο προσωρινής αποθήκευσης
+│ └── FullPageCache.php
+└── Integration/ ← ενσωμάτωση με πρόσθετες υπηρεσίες
+ ├── Slack/
+ └── Stripe/
+\--
+
+Για μικρότερα έργα, μια επίπεδη δομή είναι φυσικά επαρκής:
+
+/--pre
+Core/
+├── RouterFactory.php
+├── Authenticator.php
+└── QueueMailer.php
+\--
+
+Αυτός είναι κώδικας που:
+
+- Χειρίζεται την τεχνική υποδομή (δρομολόγηση, καταγραφή, προσωρινή αποθήκευση).
+- Ενσωματώνει εξωτερικές υπηρεσίες (Sentry, Elasticsearch, Redis)
+- Παρέχει βασικές υπηρεσίες για ολόκληρη την εφαρμογή (αλληλογραφία, βάση δεδομένων)
+- Είναι ως επί το πλείστον ανεξάρτητος από συγκεκριμένο τομέα - η κρυφή μνήμη ή ο καταγραφέας λειτουργεί το ίδιο για το ηλεκτρονικό εμπόριο ή το ιστολόγιο.
+
+Αναρωτιέστε αν μια συγκεκριμένη κλάση ανήκει εδώ ή στο μοντέλο; Η βασική διαφορά είναι ότι ο κώδικας στο `Core/`:
+
+- Δεν γνωρίζει τίποτα για τον τομέα (προϊόντα, παραγγελίες, άρθρα).
+- Μπορεί συνήθως να μεταφερθεί σε άλλο έργο
+- Λύνει το "πώς λειτουργεί" (πώς να στείλει το ταχυδρομείο), όχι το "τι κάνει" (τι ταχυδρομείο να στείλει)
+
+Παράδειγμα για καλύτερη κατανόηση:
+
+- `App\Core\MailerFactory` - δημιουργεί περιπτώσεις της κλάσης αποστολής ηλεκτρονικού ταχυδρομείου, χειρίζεται τις ρυθμίσεις SMTP
+- `App\Model\OrderMailer` - χρησιμοποιεί το `MailerFactory` για την αποστολή μηνυμάτων ηλεκτρονικού ταχυδρομείου σχετικά με τις παραγγελίες, γνωρίζει τα πρότυπα τους και πότε πρέπει να σταλούν
+
+
+Σενάρια εντολών .[#toc-command-scripts]
+=======================================
+
+Οι εφαρμογές πρέπει συχνά να εκτελούν εργασίες εκτός των κανονικών αιτήσεων HTTP - είτε πρόκειται για επεξεργασία δεδομένων στο παρασκήνιο, είτε για συντήρηση, είτε για περιοδικές εργασίες. Για την εκτέλεση χρησιμοποιούνται απλά σενάρια στον κατάλογο `bin/`, ενώ η πραγματική λογική της εφαρμογής τοποθετείται στον κατάλογο `app/Tasks/` (ή `app/Commands/`).
+
+Παράδειγμα:
+
+/--pre
+app/Tasks/
+├── Maintenance/ ← σενάρια συντήρησης
+│ ├── CleanupCommand.php ← διαγραφή παλαιών δεδομένων
+│ └── DbOptimizeCommand.php ← βελτιστοποίηση βάσης δεδομένων
+├── Integration/ ← ενσωμάτωση με εξωτερικά συστήματα
+│ ├── ImportProducts.php ← εισαγωγή από σύστημα προμηθευτή
+│ └── SyncOrders.php ← συγχρονισμός παραγγελιών
+└── Scheduled/ ← τακτικές εργασίες
+ ├── NewsletterCommand.php ← αποστολή ενημερωτικών δελτίων
+ └── ReminderCommand.php ← ειδοποιήσεις πελατών
+\--
+
+Τι ανήκει στο μοντέλο και τι στα σενάρια εντολών; Για παράδειγμα, η λογική για την αποστολή ενός email ανήκει στο μοντέλο, η μαζική αποστολή χιλιάδων email ανήκει στο `Tasks/`.
+
+Οι εργασίες [εκτελούνται |https://blog.nette.org/en/cli-scripts-in-nette-application] συνήθως [από τη γραμμή εντολών |https://blog.nette.org/en/cli-scripts-in-nette-application] ή μέσω cron. Μπορούν επίσης να εκτελούνται μέσω αίτησης HTTP, αλλά πρέπει να ληφθεί υπόψη η ασφάλεια. Ο παρουσιαστής που εκτελεί την εργασία πρέπει να είναι ασφαλής, π.χ. μόνο για συνδεδεμένους χρήστες ή με ένα ισχυρό token και πρόσβαση από επιτρεπόμενες διευθύνσεις IP. Για εργασίες μεγάλης διάρκειας, είναι απαραίτητο να αυξήσετε το χρονικό όριο της δέσμης ενεργειών και να χρησιμοποιήσετε το `session_write_close()` για να αποφύγετε το κλείδωμα της συνεδρίας.
+
+
+Άλλοι πιθανοί κατάλογοι .[#toc-other-possible-directories]
+==========================================================
+
+Εκτός από τους αναφερόμενους βασικούς καταλόγους, μπορείτε να προσθέσετε και άλλους εξειδικευμένους φακέλους ανάλογα με τις ανάγκες του έργου. Ας δούμε τους πιο συνηθισμένους και τη χρήση τους:
+
+/--pre
+app/
+├── Api/ ← Λογική API ανεξάρτητη από το επίπεδο παρουσίασης
+├── Database/ ← σενάρια μετάβασης και σπορείς για δεδομένα δοκιμών
+├── Components/ ← κοινά οπτικά στοιχεία σε όλη την εφαρμογή
+├── Event/ ← χρήσιμη αν χρησιμοποιείται αρχιτεκτονική με γνώμονα το γεγονός
+├── Mail/ ← πρότυπα ηλεκτρονικού ταχυδρομείου και σχετική λογική
+└── Utils/ ← βοηθητικές κλάσεις
+\--
+
+Για κοινά οπτικά στοιχεία που χρησιμοποιούνται σε παρουσιαστές σε όλη την εφαρμογή, μπορείτε να χρησιμοποιήσετε το φάκελο `app/Components` ή `app/Controls`:
+
+/--pre
+app/Components/
+├── Form/ ← κοινά στοιχεία φόρμας
+│ ├── SignInForm.php
+│ └── UserForm.php
+├── Grid/ ← στοιχεία για τις λίστες δεδομένων
+│ └── DataGrid.php
+└── Navigation/ ← στοιχεία πλοήγησης
+ ├── Breadcrumbs.php
+ └── Menu.php
+\--
+
+Εδώ ανήκουν τα στοιχεία με πιο σύνθετη λογική. Αν θέλετε να μοιράζεστε συστατικά μεταξύ πολλών έργων, είναι καλό να τα διαχωρίζετε σε ένα αυτόνομο πακέτο composer.
+
+Στον κατάλογο `app/Mail` μπορείτε να τοποθετήσετε τη διαχείριση της επικοινωνίας μέσω ηλεκτρονικού ταχυδρομείου:
+
+/--pre
+app/Mail/
+├── templates/ ← πρότυπα email
+│ ├── order-confirmation.latte
+│ └── welcome.latte
+└── OrderMailer.php
+\--
+
+
+Χαρτογράφηση παρουσιαστή .[#toc-presenter-mapping]
+==================================================
+
+Η αντιστοίχιση ορίζει κανόνες για την εξαγωγή ονομάτων κλάσεων από ονόματα παρουσιαστών. Τους καθορίζουμε στη [διαμόρφωση |configuration] στο κλειδί `application › mapping`.
+
+Σε αυτή τη σελίδα, έχουμε δείξει ότι τοποθετούμε τους παρουσιαστές στο φάκελο `app/Presentation` (ή `app/UI`). Πρέπει να ενημερώσουμε τη Nette για αυτή τη σύμβαση στο αρχείο διαμόρφωσης. Μια γραμμή είναι αρκετή:
+
+```neon
+application:
+ mapping: App\Presentation\*\**Presenter
+```
+
+Πώς λειτουργεί η αντιστοίχιση; Για την καλύτερη κατανόηση, ας φανταστούμε πρώτα μια εφαρμογή χωρίς ενότητες. Θέλουμε οι κλάσεις παρουσιαστών να εμπίπτουν στο χώρο ονομάτων `App\Presentation`, έτσι ώστε ο παρουσιαστής `Home` να αντιστοιχίζεται στην κλάση `App\Presentation\HomePresenter`. Αυτό επιτυγχάνεται με αυτή τη διαμόρφωση:
+
+```neon
+application:
+ mapping: App\Presentation\*Presenter
+```
+
+Η αντιστοίχιση λειτουργεί με την αντικατάσταση του αστερίσκου στη μάσκα `App\Presentation\*Presenter` με το όνομα του παρουσιαστή `Home`, με αποτέλεσμα το τελικό όνομα της κλάσης `App\Presentation\HomePresenter`. Απλό!
+
+Ωστόσο, όπως βλέπετε σε παραδείγματα σε αυτό και σε άλλα κεφάλαια, τοποθετούμε κλάσεις παρουσιαστή σε επώνυμους υποκαταλόγους, για παράδειγμα ο παρουσιαστής `Home` αντιστοιχίζεται στην κλάση `App\Presentation\Home\HomePresenter`. Αυτό το επιτυγχάνουμε διπλασιάζοντας την άνω και κάτω τελεία (απαιτεί Nette Application 3.2):
+
+```neon
+application:
+ mapping: App\Presentation\**Presenter
+```
+
+Τώρα θα προχωρήσουμε στην αντιστοίχιση των παρουσιαστών σε ενότητες. Μπορούμε να ορίσουμε συγκεκριμένη αντιστοίχιση για κάθε ενότητα:
+
+```neon
+application:
+ mapping:
+ Front: App\Presentation\Front\**Presenter
+ Admin: App\Presentation\Admin\**Presenter
+ Api: App\Api\*Presenter
+```
+
+Σύμφωνα με αυτή τη διαμόρφωση, ο παρουσιαστής `Front:Home` αντιστοιχίζεται στην τάξη `App\Presentation\Front\Home\HomePresenter`, ενώ ο παρουσιαστής `Api:OAuth` αντιστοιχίζεται στην τάξη `App\Api\OAuthPresenter`.
+
+Επειδή οι ενότητες `Front` και `Admin` έχουν παρόμοια μέθοδο αντιστοίχισης και πιθανώς θα υπάρξουν περισσότερες τέτοιες ενότητες, είναι δυνατόν να δημιουργηθεί ένας γενικός κανόνας που θα τις αντικαθιστά. Ένας νέος αστερίσκος για την ενότητα θα προστεθεί στη μάσκα κλάσης:
+
+```neon
+application:
+ mapping:
+ *: App\Presentation\*\**Presenter
+ Api: App\Api\*Presenter
+```
+
+Λειτουργεί επίσης για βαθύτερα φωλιασμένες δομές καταλόγων, όπως ο παρουσιαστής `Admin:User:Edit`, όπου το τμήμα με τον αστερίσκο επαναλαμβάνεται για κάθε επίπεδο και προκύπτει η κλάση `App\Presentation\Admin\User\Edit\EditPresenter`.
+
+Ένας εναλλακτικός συμβολισμός είναι η χρήση ενός πίνακα που αποτελείται από τρία τμήματα αντί για μια συμβολοσειρά. Αυτή η σημειογραφία είναι ισοδύναμη με την προηγούμενη:
+
+```neon
+application:
+ mapping:
+ *: [App\Presentation, *, **Presenter]
+ Api: [App\Api, '', *Presenter]
+```
diff --git a/application/el/how-it-works.texy b/application/el/how-it-works.texy
index 2cfe6c52b9..66270824a6 100644
--- a/application/el/how-it-works.texy
+++ b/application/el/how-it-works.texy
@@ -22,18 +22,18 @@
/--pre
web-project/
├── app/ ← directory with application
-│ ├── Presenters/ ← presenter classes
-│ │ ├── HomePresenter.php ← Home presenter class
-│ │ └── templates/ ← templates directory
-│ │ ├── @layout.latte ← template of shared layout
-│ │ └── Home/ ← templates for Home presenter
-│ │ └── default.latte ← template for action `default`
-│ ├── Router/ ← configuration of URL addresses
+│ ├── Core/ ← βασικές αναγκαίες τάξεις
+│ │ └── RouterFactory.php ← διαμόρφωση των διευθύνσεων URL
+│ ├── Presentation/ ← παρουσιαστές, πρότυπα και λοιπά.
+│ │ ├── @layout.latte ← πρότυπο κοινής διάταξης
+│ │ └── Home/ ← Αρχικός κατάλογος παρουσιαστών
+│ │ ├── HomePresenter.php ← Κλάση οικιακού παρουσιαστή
+│ │ └── default.latte ← πρότυπο για τη δράση default
│ └── Bootstrap.php ← booting class Bootstrap
├── bin/ ← scripts for the command line
├── config/ ← configuration files
│ ├── common.neon
-│ └── local.neon
+│ └── services.neon
├── log/ ← error logs
├── temp/ ← temporary files, cache, …
├── vendor/ ← libraries installed by Composer
@@ -45,9 +45,9 @@
└── .htaccess ← prohibits access to all directories except www
\--
-Μπορείτε να αλλάξετε τη δομή του καταλόγου με οποιονδήποτε τρόπο, να μετονομάσετε ή να μετακινήσετε φακέλους και στη συνέχεια να επεξεργαστείτε απλώς τις διαδρομές προς τα `log/` και `temp/` στο αρχείο `Bootstrap.php` και τη διαδρομή προς αυτό το αρχείο στο `composer.json` στο τμήμα `autoload`. Τίποτα περισσότερο, καμία περίπλοκη αναδιαμόρφωση, καμία συνεχής αλλαγή. Το Nette διαθέτει μια [έξυπνη αυτόματη αναγνώριση |bootstrap#development-vs-production-mode].
+Μπορείτε να τροποποιήσετε τη δομή του καταλόγου όπως θέλετε, να μετονομάσετε ή να μετακινήσετε φακέλους - είναι απολύτως ευέλικτο. Το Nette διαθέτει επίσης έξυπνη αυτόματη ανίχνευση και αναγνωρίζει αυτόματα τη θέση της εφαρμογής, συμπεριλαμβανομένης της βάσης URL της.
-Για λίγο μεγαλύτερες εφαρμογές, μπορούμε να χωρίσουμε τους φακέλους με τους παρουσιαστές και τα πρότυπα σε υποκαταλόγους (στο δίσκο) και σε χώρους ονομάτων (στον κώδικα), τους οποίους ονομάζουμε [ενότητες |modules].
+Για λίγο μεγαλύτερες εφαρμογές, μπορούμε να οργανώσουμε τους φακέλους παρουσιαστή και προτύπων σε [υποκαταλόγους |directory-structure#Presenters and templates] και να ομαδοποιήσουμε τις κλάσεις σε χώρους ονομάτων, τους οποίους ονομάζουμε ενότητες.
Ο κατάλογος `www/` είναι ο δημόσιος κατάλογος ή το document-root του έργου. Μπορείτε να τον μετονομάσετε χωρίς να χρειάζεται να ορίσετε κάτι άλλο στην πλευρά της εφαρμογής. Απλά πρέπει να [ρυθμίσετε τη φιλοξενία |nette:troubleshooting#How to change or remove www directory from URL] έτσι ώστε το document-root να πηγαίνει σε αυτόν τον κατάλογο.
@@ -75,7 +75,7 @@ composer create-project nette/web-project
Τι είδους εργοστάσιο; Δεν παράγουμε τρακτέρ, αλλά ιστοσελίδες! Περιμένετε, θα σας εξηγήσω αμέσως.
-Με τον όρο "αρχικοποίηση του περιβάλλοντος" εννοούμε, για παράδειγμα, ότι ενεργοποιείται το [Tracy |tracy:], το οποίο είναι ένα καταπληκτικό εργαλείο για την καταγραφή ή την οπτικοποίηση σφαλμάτων. Καταγράφει τα σφάλματα στον διακομιστή παραγωγής και τα εμφανίζει απευθείας στον διακομιστή ανάπτυξης. Επομένως, η αρχικοποίηση πρέπει επίσης να αποφασίσει αν ο ιστότοπος εκτελείται σε κατάσταση παραγωγής ή ανάπτυξης. Για να το κάνει αυτό, η Nette χρησιμοποιεί αυτόματη ανίχνευση: αν τρέξετε τον ιστότοπο στο localhost, τρέχει σε λειτουργία προγραμματιστή. Δεν χρειάζεται να ρυθμίσετε τίποτα και η εφαρμογή είναι έτοιμη τόσο για ανάπτυξη όσο και για ανάπτυξη παραγωγής. Αυτά τα βήματα εκτελούνται και περιγράφονται λεπτομερώς στο κεφάλαιο σχετικά με την [κλάση Bootstrap |bootstrap].
+Με τον όρο "αρχικοποίηση περιβάλλοντος" εννοούμε, για παράδειγμα, την ενεργοποίηση [του Tracy |tracy:], το οποίο είναι ένα φανταστικό εργαλείο για την καταγραφή και την απεικόνιση σφαλμάτων. Στους διακομιστές παραγωγής καταγράφει τα σφάλματα, ενώ στους διακομιστές ανάπτυξης τα εμφανίζει απευθείας. Επομένως, η αρχικοποίηση περιλαμβάνει τον καθορισμό του αν ο ιστότοπος εκτελείται σε κατάσταση παραγωγής ή ανάπτυξης. Για το σκοπό αυτό, η Nette χρησιμοποιεί [έξυπνη αυτόματη ανίχνευση |bootstrap#development-vs-production-mode]: αν τρέχετε τον ιστότοπο σε localhost, λειτουργεί σε κατάσταση ανάπτυξης. Δεν απαιτείται καμία διαμόρφωση και η εφαρμογή είναι έτοιμη τόσο για ανάπτυξη όσο και για παραγωγή. Αυτά τα βήματα εκτελούνται και περιγράφονται λεπτομερώς στο κεφάλαιο της [κλάσης Bootstrap |bootstrap].
Το τρίτο σημείο (ναι, παραλείψαμε το δεύτερο, αλλά θα επανέλθουμε σε αυτό) είναι η εκκίνηση της εφαρμογής. Ο χειρισμός των αιτημάτων HTTP στη Nette γίνεται από την κλάση `Nette\Application\Application` (στο εξής θα αναφέρεται ως `Application`), οπότε όταν λέμε "τρέχουμε μια εφαρμογή", εννοούμε να καλέσουμε μια μέθοδο με το όνομα `run()` σε ένα αντικείμενο αυτής της κλάσης.
@@ -91,7 +91,7 @@ composer create-project nette/web-project
Η εφαρμογή ξεκινά ζητώντας από τον λεγόμενο δρομολογητή να αποφασίσει ποιος από τους παρουσιαστές θα περάσει το τρέχον αίτημα για επεξεργασία. Ο δρομολογητής αποφασίζει ποιανού ευθύνη είναι. Κοιτάζει τη διεύθυνση URL εισόδου `https://example.com/product/123`, ο οποίος θέλει να `show` ένα προϊόν με `id: 123` ως ενέργεια. Είναι καλή συνήθεια να γράφετε τα ζεύγη παρουσιαστής + δράση χωρισμένα με άνω και κάτω τελεία ως `Product:show`.
-Έτσι, ο δρομολογητής μετατρέπει τη διεύθυνση URL σε ένα ζεύγος `Presenter:action` + παράμετροι, στην περίπτωσή μας `Product:show` + `id: 123`. Μπορείτε να δείτε πώς μοιάζει ένας δρομολογητής στο αρχείο `app/Router/RouterFactory.php` και θα τον περιγράψουμε αναλυτικά στο κεφάλαιο [Δρομολόγηση |Routing].
+Έτσι, ο δρομολογητής μετατρέπει τη διεύθυνση URL σε ένα ζεύγος `Presenter:action` + παράμετροι, στην περίπτωσή μας `Product:show` + `id: 123`. Μπορείτε να δείτε πώς μοιάζει ένας δρομολογητής στο αρχείο `app/Core/RouterFactory.php` και θα τον περιγράψουμε αναλυτικά στο κεφάλαιο [Δρομολόγηση |Routing].
Ας συνεχίσουμε. Η εφαρμογή γνωρίζει ήδη το όνομα του παρουσιαστή και μπορεί να συνεχίσει. Δημιουργώντας ένα αντικείμενο `ProductPresenter`, το οποίο είναι ο κώδικας του παρουσιαστή `Product`. Πιο συγκεκριμένα, ζητάει από το DI container τη δημιουργία του presenter, επειδή η παραγωγή αντικειμένων είναι η δουλειά του.
@@ -121,12 +121,9 @@ class ProductPresenter extends Nette\Application\UI\Presenter
Στη συνέχεια, ο παρουσιαστής επιστρέφει την απάντηση. Αυτό μπορεί να είναι μια σελίδα HTML, μια εικόνα, ένα έγγραφο XML, η αποστολή ενός αρχείου από το δίσκο, JSON ή η ανακατεύθυνση σε μια άλλη σελίδα. Σημαντικό είναι ότι, αν δεν πούμε ρητά πώς να απαντήσουμε (κάτι που συμβαίνει στην περίπτωση του `ProductPresenter`), η απάντηση θα είναι η απόδοση του προτύπου με μια σελίδα HTML. Γιατί; Λοιπόν, επειδή στο 99% των περιπτώσεων θέλουμε να σχεδιάσουμε ένα πρότυπο, οπότε ο παρουσιαστής θεωρεί αυτή τη συμπεριφορά ως προεπιλεγμένη και θέλει να διευκολύνει τη δουλειά μας. Αυτό είναι το νόημα της Nette.
-Δεν χρειάζεται καν να δηλώσουμε ποιο πρότυπο θέλουμε να σχεδιάσουμε, αυτός εξάγει τη διαδρομή προς αυτό σύμφωνα με απλή λογική. Στην περίπτωση του presenter `Product` και της δράσης `show`, προσπαθεί να δει αν ένα από αυτά τα αρχεία προτύπων υπάρχει σε σχέση με τον κατάλογο όπου βρίσκεται η κλάση `ProductPresenter`:
+Δεν χρειάζεται καν να καθορίσουμε ποιο πρότυπο θα αναπαραχθεί- το πλαίσιο θα βρει μόνο του τη διαδρομή. Στην περίπτωση της ενέργειας `show`, απλώς προσπαθεί να φορτώσει το πρότυπο `show.latte` στον κατάλογο με την κλάση `ProductPresenter`. Προσπαθεί επίσης να βρει τη διάταξη στο αρχείο `@layout.latte` (περισσότερα για την [αναζήτηση προτύπων |templates#Template Lookup]).
-- `templates/Product/show.latte`
-- `templates/Product.show.latte`
-
-Θα προσπαθήσει επίσης να βρει τη διάταξη στο αρχείο `@layout.latte` και στη συνέχεια θα αποδώσει το πρότυπο. Τώρα ολοκληρώνεται η εργασία του παρουσιαστή και ολόκληρης της εφαρμογής. Εάν το πρότυπο δεν υπάρχει, θα επιστραφεί μια σελίδα με σφάλμα 404. Μπορείτε να διαβάσετε περισσότερα για τους παρουσιαστές στη σελίδα [Παρουσιαστές |Presenters].
+Στη συνέχεια, τα πρότυπα αποδίδονται. Με αυτόν τον τρόπο ολοκληρώνεται η εργασία του παρουσιαστή και ολόκληρης της εφαρμογής, και η εργασία έχει τελειώσει. Εάν το πρότυπο δεν υπήρχε, θα επιστρεφόταν μια σελίδα σφάλματος 404. Μπορείτε να διαβάσετε περισσότερα για τους παρουσιαστές στη σελίδα [Παρουσιαστές |presenters].
[* request-flow.svg *]
@@ -137,7 +134,7 @@ class ProductPresenter extends Nette\Application\UI\Presenter
3) ο δρομολογητής αποκωδικοποιεί τη διεύθυνση URL ως ζεύγος `Home:default`
4) δημιουργείται ένα αντικείμενο `HomePresenter`
5) καλείται η μέθοδος `renderDefault()` (αν υπάρχει)
-6) αποδίδεται ένα πρότυπο `templates/Home/default.latte` με διάταξη `templates/@layout.latte`
+6) αποδίδεται ένα πρότυπο `default.latte` με διάταξη `@layout.latte`
Μπορεί να έχετε συναντήσει πολλές νέες έννοιες τώρα, αλλά πιστεύουμε ότι βγάζουν νόημα. Η δημιουργία εφαρμογών στη Nette είναι πανεύκολη.
diff --git a/application/el/modules.texy b/application/el/modules.texy
deleted file mode 100644
index b2bb1dffd0..0000000000
--- a/application/el/modules.texy
+++ /dev/null
@@ -1,148 +0,0 @@
-Ενότητες
-********
-
-.[perex]
-Στη Nette, οι ενότητες αντιπροσωπεύουν τις λογικές μονάδες που συνθέτουν μια εφαρμογή. Περιλαμβάνουν παρουσιαστές, πρότυπα, ενδεχομένως επίσης συστατικά και κλάσεις μοντέλων.
-
-Ένας κατάλογος για τους παρουσιαστές και ένας για τα πρότυπα δεν θα ήταν αρκετός για πραγματικά έργα. Το να έχετε δεκάδες αρχεία σε έναν φάκελο είναι τουλάχιστον ανοργάνωτο. Πώς να απαλλαγείτε από αυτό; Απλώς τα χωρίζουμε σε υποκαταλόγους στο δίσκο και σε χώρους ονομάτων στον κώδικα. Και αυτό ακριβώς κάνουν τα modules της Nette.
-
-Ας ξεχάσουμε λοιπόν έναν ενιαίο φάκελο για τους παρουσιαστές και τα πρότυπα και ας δημιουργήσουμε αντ' αυτού ενότητες, για παράδειγμα `Admin` και `Front`.
-
-/--pre
-app/
-├── Presenters/
-├── Modules/ ← directory with modules
-│ ├── Admin/ ← module Admin
-│ │ ├── Presenters/ ← its presenters
-│ │ │ ├── DashboardPresenter.php
-│ │ │ └── templates/
-│ └── Front/ ← module Front
-│ └── Presenters/ ← its presenters
-│ └── ...
-\--
-
-Αυτή η δομή καταλόγου θα αντικατοπτρίζεται από τα namespaces των κλάσεων, έτσι για παράδειγμα το `DashboardPresenter` θα βρίσκεται στο namespace `App\Modules\Admin\Presenters`:
-
-```php
-namespace App\Modules\Admin\Presenters;
-
-class DashboardPresenter extends Nette\Application\UI\Presenter
-{
- // ...
-}
-```
-
-Ο παρουσιαστής `Dashboard` μέσα στην ενότητα `Admin` αναφέρεται μέσα στην εφαρμογή χρησιμοποιώντας τον συμβολισμό της άνω και κάτω τελείας ως `Admin:Dashboard`, και η ενέργεια `default` ως `Admin:Dashboard:default`.
-Και πώς γνωρίζει η Nette proper ότι το `Admin:Dashboard` αντιπροσωπεύει την κλάση `App\Modules\Admin\Presenters\DashboardPresenter`; Αυτό καθορίζεται από την [αντιστοίχιση |#mapping] στη διαμόρφωση.
-Έτσι, η δεδομένη δομή δεν είναι αυστηρά καθορισμένη και μπορείτε να την τροποποιήσετε ανάλογα με τις ανάγκες σας.
-
-Οι ενότητες μπορούν φυσικά να περιέχουν όλα τα άλλα στοιχεία εκτός από τους παρουσιαστές και τα πρότυπα, όπως συστατικά, κλάσεις μοντέλων κ.λπ.
-
-
-Ενσωματωμένες ενότητες .[#toc-nested-modules]
----------------------------------------------
-
-Οι ενότητες δεν χρειάζεται να σχηματίζουν μόνο μια επίπεδη δομή, μπορείτε επίσης να δημιουργήσετε υποενότητες, για παράδειγμα:
-
-/--pre
-app/
-├── Modules/ ← directory with modules
-│ ├── Blog/ ← module Blog
-│ │ ├── Admin/ ← submodule Admin
-│ │ │ ├── Presenters/
-│ │ │ └── ...
-│ │ └── Front/ ← submodule Front
-│ │ ├── Presenters/
-│ │ └── ...
-│ ├── Forum/ ← module Forum
-│ │ └── ...
-\--
-
-Έτσι, η ενότητα `Blog` χωρίζεται σε υποενότητες `Admin` και `Front`. Και πάλι, αυτό θα αντικατοπτρίζεται στα namespaces, τα οποία θα είναι `App\Modules\Blog\Admin\Presenters` κ.λπ. Ο παρουσιαστής `Dashboard` μέσα στην υποενότητα αναφέρεται ως `Blog:Admin:Dashboard`.
-
-Η ένθεση μπορεί να προχωρήσει όσο βαθιά θέλετε, οπότε μπορούν να δημιουργηθούν υπο-υποενότητες.
-
-
-Δημιουργία συνδέσμων .[#toc-creating-links]
--------------------------------------------
-
-Οι σύνδεσμοι στα πρότυπα παρουσιαστή είναι σχετικοί με την τρέχουσα ενότητα. Έτσι, ο σύνδεσμος `Foo:default` οδηγεί στον παρουσιαστή `Foo` στην ίδια ενότητα με τον τρέχοντα παρουσιαστή. Εάν η τρέχουσα ενότητα είναι `Front`, για παράδειγμα, τότε ο σύνδεσμος έχει ως εξής:
-
-```latte
-link to Front:Product:show
-```
-
-Ένας σύνδεσμος είναι σχετικός ακόμη και αν περιλαμβάνει το όνομα μιας ενότητας, η οποία τότε θεωρείται υποενότητα:
-
-```latte
-link to Front:Shop:Product:show
-```
-
-Οι απόλυτοι σύνδεσμοι γράφονται ανάλογα με τις απόλυτες διαδρομές στο δίσκο, αλλά με άνω και κάτω τελεία αντί για κάθετους. Έτσι, ένας απόλυτος σύνδεσμος αρχίζει με άνω και κάτω τελεία:
-
-```latte
-link to Admin:Product:show
-```
-
-Για να μάθουμε αν βρισκόμαστε σε μια συγκεκριμένη ενότητα ή υποενότητά της μπορούμε να χρησιμοποιήσουμε τη συνάρτηση `isModuleCurrent(moduleName)`.
-
-```latte
-
- ...
-
-```
-
-
-Δρομολόγηση .[#toc-routing]
----------------------------
-
-Βλέπε [κεφάλαιο για τη δρομολόγηση |routing#Modules].
-
-
-Χαρτογράφηση .[#toc-mapping]
-----------------------------
-
-Καθορίζει τους κανόνες με τους οποίους το όνομα της κλάσης προκύπτει από το όνομα του παρουσιαστή. Τους γράφουμε στη [διαμόρφωση |configuration] κάτω από το κλειδί `application › mapping`.
-
-Ας ξεκινήσουμε με ένα δείγμα που δεν χρησιμοποιεί ενότητες. Θα θέλουμε απλώς οι κλάσεις presenter να έχουν το namespace `App\Presenters`. Αυτό σημαίνει ότι ένας παρουσιαστής όπως το `Home` θα πρέπει να αντιστοιχίζεται στην κλάση `App\Presenters\HomePresenter`. Αυτό μπορεί να επιτευχθεί με την ακόλουθη διαμόρφωση:
-
-```neon
-application:
- mapping:
- *: App\Presenters\*Presenter
-```
-
-Το όνομα του παρουσιαστή αντικαθίσταται με τον αστερίσκο στη μάσκα κλάσης και το αποτέλεσμα είναι το όνομα της κλάσης. Εύκολο!
-
-Αν χωρίσουμε τους παρουσιαστές σε ενότητες, μπορούμε να έχουμε τη δική μας χαρτογράφηση για κάθε ενότητα:
-
-```neon
-application:
- mapping:
- Front: App\Modules\Front\Presenters\*Presenter
- Admin: App\Modules\Admin\Presenters\*Presenter
- Api: App\Api\*Presenter
-```
-
-Τώρα ο παρουσιαστής `Front:Home` αντιστοιχίζεται στην κλάση `App\Modules\Front\Presenters\HomePresenter` και ο παρουσιαστής `Admin:Dashboard` στην κλάση `App\Modules\Admin\Presenters\DashboardPresenter`.
-
-Είναι πιο πρακτικό να δημιουργήσετε έναν γενικό κανόνα (αστέρι) για να αντικαταστήσετε τους δύο πρώτους. Ο επιπλέον αστερίσκος θα προστεθεί στη μάσκα κλάσης μόνο για την ενότητα:
-
-```neon
-application:
- mapping:
- *: App\Modules\*\Presenters\*Presenter
- Api: App\Api\*Presenter
-```
-
-Τι γίνεται όμως αν χρησιμοποιούμε φωλιασμένες ενότητες και έχουμε έναν παρουσιαστή `Admin:User:Edit`; Σε αυτή την περίπτωση, το τμήμα με τον αστερίσκο που αντιπροσωπεύει την ενότητα για κάθε επίπεδο απλώς επαναλαμβάνεται και το αποτέλεσμα είναι η κλάση `App\Modules\Admin\User\Presenters\EditPresenter`.
-
-Ένας εναλλακτικός συμβολισμός είναι η χρήση ενός πίνακα που αποτελείται από τρία τμήματα αντί για συμβολοσειρά. Αυτή η σημειογραφία είναι ισοδύναμη με την προηγούμενη:
-
-```neon
-application:
- mapping:
- *: [App\Modules, *, Presenters\*Presenter]
-```
-
-Η προεπιλεγμένη τιμή είναι `*: *Module\*Presenter`.
diff --git a/application/el/presenters.texy b/application/el/presenters.texy
index 07cb8b4482..8b7a737672 100644
--- a/application/el/presenters.texy
+++ b/application/el/presenters.texy
@@ -60,7 +60,7 @@ class ArticlePresenter extends Nette\Application\UI\Presenter
Είναι σημαντικό ότι `action()` καλείται πριν από την `render()`, ώστε μέσα σε αυτό να μπορούμε ενδεχομένως να αλλάξουμε την επόμενη πορεία του κύκλου ζωής, δηλαδή να αλλάξουμε το πρότυπο που θα αποδοθεί και επίσης τη μέθοδο `render()` που θα κληθεί, χρησιμοποιώντας το `setView('otherView')`.
-Οι παράμετροι από το αίτημα περνούν στη μέθοδο. Είναι δυνατόν και συνιστάται να καθορίσετε τύπους για τις παραμέτρους, π.χ. `actionShow(int $id, string $slug = null)` - αν η παράμετρος `id` λείπει ή αν δεν είναι ακέραιος αριθμός, ο παρουσιαστής επιστρέφει [σφάλμα 404 |#Error 404 etc.] και τερματίζει τη λειτουργία.
+Οι παράμετροι από το αίτημα περνούν στη μέθοδο. Είναι δυνατόν και συνιστάται να καθορίσετε τύπους για τις παραμέτρους, π.χ. `actionShow(int $id, ?string $slug = null)` - αν η παράμετρος `id` λείπει ή αν δεν είναι ακέραιος αριθμός, ο παρουσιαστής επιστρέφει [σφάλμα 404 |#Error 404 etc.] και τερματίζει τη λειτουργία.
`handle(args...)` .{toc: handle()}
@@ -205,7 +205,7 @@ $this->redirect(/* ... */);
Σφάλμα 404 κ.λπ. .[#toc-error-404-etc]
======================================
-Όταν δεν μπορούμε να ικανοποιήσουμε το αίτημα επειδή για παράδειγμα το άρθρο που θέλουμε να εμφανίσουμε δεν υπάρχει στη βάση δεδομένων, θα πετάξουμε το σφάλμα 404 χρησιμοποιώντας τη μέθοδο `error(string $message = null, int $httpCode = 404)`, η οποία αντιπροσωπεύει το σφάλμα HTTP 404:
+Όταν δεν μπορούμε να ικανοποιήσουμε το αίτημα επειδή για παράδειγμα το άρθρο που θέλουμε να εμφανίσουμε δεν υπάρχει στη βάση δεδομένων, θα πετάξουμε το σφάλμα 404 χρησιμοποιώντας τη μέθοδο `error(?string $message = null, int $httpCode = 404)`, η οποία αντιπροσωπεύει το σφάλμα HTTP 404:
```php
public function renderShow(int $id): void
@@ -236,6 +236,32 @@ public function actionData(): void
```
+Παράμετροι αίτησης .[#toc-request-parameters]
+=============================================
+
+Ο παρουσιαστής, όπως και κάθε στοιχείο, λαμβάνει τις παραμέτρους του από την αίτηση HTTP. Οι τιμές τους μπορούν να ανακτηθούν χρησιμοποιώντας τη μέθοδο `getParameter($name)` ή `getParameters()`. Οι τιμές είναι συμβολοσειρές ή πίνακες συμβολοσειρών, ουσιαστικά ακατέργαστα δεδομένα που λαμβάνονται απευθείας από τη διεύθυνση URL.
+
+Για μεγαλύτερη ευκολία, συνιστούμε να κάνετε τις παραμέτρους προσβάσιμες μέσω ιδιοτήτων. Απλά σχολιάστε τις με την εντολή `#[Parameter]` χαρακτηριστικό:
+
+```php
+use Nette\Application\Attributes\Parameter; // αυτή η γραμμή είναι σημαντική
+
+class HomePresenter extends Nette\Application\UI\Presenter
+{
+ #[Parameter]
+ public string $theme; // πρέπει να είναι δημόσια
+}
+```
+
+Για τις ιδιότητες, προτείνουμε να προσδιορίσετε τον τύπο δεδομένων (π.χ. `string`). Στη συνέχεια, η Nette θα μετατρέψει αυτόματα την τιμή με βάση αυτόν. Οι τιμές των παραμέτρων μπορούν επίσης να [επικυρωθούν |#Validation of Parameters].
+
+Κατά τη δημιουργία ενός συνδέσμου, μπορείτε να ορίσετε απευθείας την τιμή για τις παραμέτρους:
+
+```latte
+click
+```
+
+
Εμμένουσες παράμετροι .[#toc-persistent-parameters]
===================================================
@@ -257,7 +283,7 @@ class ProductPresenter extends Nette\Application\UI\Presenter
Εάν το `$this->lang` έχει μια τιμή όπως `'en'`, τότε οι σύνδεσμοι που δημιουργούνται με χρήση των `link()` ή `n:href` θα περιέχουν επίσης την παράμετρο `lang=en`. Και όταν ο σύνδεσμος πατηθεί, θα είναι και πάλι `$this->lang = 'en'`.
-Για τις ιδιότητες, συνιστούμε να περιλαμβάνετε τον τύπο δεδομένων (π.χ. `string`) και μπορείτε επίσης να συμπεριλάβετε μια προεπιλεγμένη τιμή. Οι τιμές των παραμέτρων μπορούν να [επικυρωθούν |#Validation of Persistent Parameters].
+Για τις ιδιότητες, συνιστούμε να συμπεριλάβετε τον τύπο δεδομένων (π.χ. `string`) και μπορείτε επίσης να συμπεριλάβετε μια προεπιλεγμένη τιμή. Οι τιμές των παραμέτρων μπορούν να [επικυρωθούν |#Validation of Parameters].
Οι μόνιμες παράμετροι μεταβιβάζονται μεταξύ όλων των ενεργειών ενός συγκεκριμένου παρουσιαστή από προεπιλογή. Για να τις περάσετε μεταξύ πολλαπλών παρουσιαστών, πρέπει να τις ορίσετε είτε:
@@ -307,18 +333,12 @@ class ProductPresenter extends Nette\Application\UI\Presenter
Όσα έχουμε δείξει μέχρι στιγμής σε αυτό το κεφάλαιο μάλλον αρκούν. Οι επόμενες γραμμές απευθύνονται σε όσους ενδιαφέρονται για τους παρουσιαστές σε βάθος και θέλουν να μάθουν τα πάντα.
-Απαίτηση και παράμετροι .[#toc-requirement-and-parameters]
-----------------------------------------------------------
+Επικύρωση των παραμέτρων .[#toc-validation-of-parameters]
+---------------------------------------------------------
-Το αίτημα που χειρίζεται ο παρουσιαστής είναι το αντικείμενο [api:Nette\Application\Request] και επιστρέφεται από τη μέθοδο του παρουσιαστή `getRequest()`. Περιλαμβάνει έναν πίνακα παραμέτρων και κάθε μία από αυτές ανήκει είτε σε κάποιο από τα συστατικά είτε απευθείας στον παρουσιαστή (ο οποίος στην πραγματικότητα είναι επίσης ένα συστατικό, αν και ειδικό). Έτσι, η Nette ανακατανέμει τις παραμέτρους και περνάει μεταξύ των επιμέρους συστατικών (και του παρουσιαστή) καλώντας τη μέθοδο `loadState(array $params)`. Οι παράμετροι μπορούν να ληφθούν με τη μέθοδο `getParameters(): array`, μεμονωμένα με τη χρήση του `getParameter($name)`. Οι τιμές των παραμέτρων είναι συμβολοσειρές ή πίνακες συμβολοσειρών, είναι ουσιαστικά ακατέργαστα δεδομένα που λαμβάνονται απευθείας από μια διεύθυνση URL.
+Οι τιμές των [παραμέτρων αίτησης |#request parameters] και των [μόνιμων παραμέτρων |#persistent parameters] που λαμβάνονται από τις διευθύνσεις URL εγγράφονται στις ιδιότητες από τη μέθοδο `loadState()`. Ελέγχει επίσης αν ο τύπος δεδομένων που καθορίζεται στην ιδιότητα ταιριάζει, διαφορετικά θα απαντήσει με σφάλμα 404 και η σελίδα δεν θα εμφανιστεί.
-
-Επικύρωση μόνιμων παραμέτρων .[#toc-validation-of-persistent-parameters]
-------------------------------------------------------------------------
-
-Οι τιμές των [μόνιμων παραμέτρων |#persistent parameters] που λαμβάνονται από τις διευθύνσεις URL εγγράφονται στις ιδιότητες με τη μέθοδο `loadState()`. Ελέγχει επίσης αν ο τύπος δεδομένων που καθορίζεται στην ιδιότητα ταιριάζει, διαφορετικά θα απαντήσει με σφάλμα 404 και η σελίδα δεν θα εμφανιστεί.
-
-Ποτέ μην εμπιστεύεστε τυφλά τις μόνιμες παραμέτρους, καθώς μπορούν εύκολα να αντικατασταθούν από τον χρήστη στη διεύθυνση URL. Για παράδειγμα, με αυτόν τον τρόπο ελέγχουμε αν το `$this->lang` είναι μεταξύ των υποστηριζόμενων γλωσσών. Ένας καλός τρόπος για να το κάνετε αυτό είναι να παρακάμψετε τη μέθοδο `loadState()` που αναφέρθηκε παραπάνω:
+Ποτέ μην εμπιστεύεστε τυφλά τις παραμέτρους, καθώς μπορούν εύκολα να αντικατασταθούν από τον χρήστη στη διεύθυνση URL. Για παράδειγμα, με αυτόν τον τρόπο ελέγχουμε αν το `$this->lang` είναι μεταξύ των υποστηριζόμενων γλωσσών. Ένας καλός τρόπος για να το κάνετε αυτό είναι να παρακάμψετε τη μέθοδο `loadState()` που αναφέρθηκε παραπάνω:
```php
class ProductPresenter extends Nette\Application\UI\Presenter
@@ -341,7 +361,9 @@ class ProductPresenter extends Nette\Application\UI\Presenter
Αποθήκευση και επαναφορά της αίτησης .[#toc-save-and-restore-the-request]
-------------------------------------------------------------------------
-Μπορείτε να αποθηκεύσετε την τρέχουσα αίτηση σε μια συνεδρία ή να την επαναφέρετε από τη συνεδρία και να αφήσετε τον παρουσιαστή να την εκτελέσει ξανά. Αυτό είναι χρήσιμο, για παράδειγμα, όταν ένας χρήστης συμπληρώνει μια φόρμα και λήγει η σύνδεσή του. Για να μην χαθούν δεδομένα, πριν από την ανακατεύθυνση στη σελίδα σύνδεσης, αποθηκεύουμε το τρέχον αίτημα στη σύνοδο χρησιμοποιώντας το `$reqId = $this->storeRequest()`, το οποίο επιστρέφει ένα αναγνωριστικό με τη μορφή σύντομης συμβολοσειράς και το περνάει ως παράμετρο στον παρουσιαστή σύνδεσης.
+Το αίτημα που χειρίζεται ο παρουσιαστής είναι ένα αντικείμενο [api:Nette\Application\Request] και επιστρέφεται από τη μέθοδο του παρουσιαστή `getRequest()`.
+
+Μπορείτε να αποθηκεύσετε την τρέχουσα αίτηση σε μια συνεδρία ή να την επαναφέρετε από τη συνεδρία και να αφήσετε τον παρουσιαστή να την εκτελέσει ξανά. Αυτό είναι χρήσιμο, για παράδειγμα, όταν ένας χρήστης συμπληρώνει μια φόρμα και η σύνδεσή του λήγει. Για να μην χαθούν δεδομένα, πριν από την ανακατεύθυνση στη σελίδα σύνδεσης, αποθηκεύουμε την τρέχουσα αίτηση στη σύνοδο χρησιμοποιώντας τη μέθοδο `$reqId = $this->storeRequest()`, η οποία επιστρέφει ένα αναγνωριστικό με τη μορφή σύντομης συμβολοσειράς και το περνάει ως παράμετρο στον παρουσιαστή σύνδεσης.
Μετά την είσοδο, καλούμε τη μέθοδο `$this->restoreRequest($reqId)`, η οποία παραλαμβάνει το αίτημα από τη σύνοδο και το προωθεί σε αυτήν. Η μέθοδος επαληθεύει ότι το αίτημα δημιουργήθηκε από τον ίδιο χρήστη που τώρα έχει συνδεθεί είναι. Αν συνδεθεί άλλος χρήστης ή το κλειδί είναι άκυρο, δεν κάνει τίποτα και το πρόγραμμα συνεχίζει.
@@ -362,7 +384,7 @@ class ProductPresenter extends Nette\Application\UI\Presenter
Μπορείτε επίσης να επικαλεστείτε την κανονικοποίηση χειροκίνητα χρησιμοποιώντας τη μέθοδο `canonicalize()`, η οποία, όπως και η μέθοδος `link()`, λαμβάνει τον παρουσιαστή, τις ενέργειες και τις παραμέτρους ως ορίσματα. Δημιουργεί έναν σύνδεσμο και τον συγκρίνει με την τρέχουσα διεύθυνση URL. Εάν είναι διαφορετική, ανακατευθύνει στον δημιουργημένο σύνδεσμο.
```php
-public function actionShow(int $id, string $slug = null): void
+public function actionShow(int $id, ?string $slug = null): void
{
$realSlug = $this->facade->getSlugForId($id);
// ανακατευθύνει εάν το $slug είναι διαφορετικό από το $realSlug
@@ -425,6 +447,51 @@ $this->sendResponse(new Responses\CallbackResponse($callback));
```
+Περιορισμός πρόσβασης με χρήση `#[Requires]` .[#toc-access-restriction-using-requires]{data-version:3.2.2}
+----------------------------------------------------------------------------------------------------------
+
+Το `#[Requires]` παρέχει προηγμένες επιλογές για τον περιορισμό της πρόσβασης στους παρουσιαστές και τις μεθόδους τους. Μπορεί να χρησιμοποιηθεί για τον προσδιορισμό μεθόδων HTTP, την απαίτηση αιτήσεων AJAX, τον περιορισμό της πρόσβασης στην ίδια προέλευση και τον περιορισμό της πρόσβασης μόνο στην προώθηση. Το χαρακτηριστικό μπορεί να εφαρμοστεί σε κλάσεις παρουσιαστών καθώς και σε μεμονωμένες μεθόδους όπως οι `action()`, `render()`, `handle()`, και `createComponent()`.
+
+Μπορείτε να καθορίσετε αυτούς τους περιορισμούς:
+- σε μεθόδους HTTP: `#[Requires(methods: ['GET', 'POST'])]`
+- AJAX: `#[Requires(ajax: true)]`
+- πρόσβαση μόνο από την ίδια προέλευση: `#[Requires(sameOrigin: true)]`
+- πρόσβαση μόνο μέσω προώθησης: `#[Requires(forward: true)]`
+- περιορισμοί σε συγκεκριμένες ενέργειες: `#[Requires(actions: 'default')]`
+
+Για λεπτομέρειες, ανατρέξτε στην ενότητα [Πώς να χρησιμοποιήσετε το Requires attribute |best-practices:attribute-requires].
+
+
+Έλεγχος μεθόδου HTTP .[#toc-http-method-check]
+----------------------------------------------
+
+Στη Nette, οι παρουσιαστές επαληθεύουν αυτόματα τη μέθοδο HTTP κάθε εισερχόμενης αίτησης κυρίως για λόγους ασφαλείας. Από προεπιλογή, επιτρέπονται οι μέθοδοι `GET`, `POST`, `HEAD`, `PUT`, `DELETE`, `PATCH`.
+
+Εάν θέλετε να ενεργοποιήσετε επιπλέον μεθόδους, όπως η `OPTIONS`, μπορείτε να χρησιμοποιήσετε την εντολή `#[Requires]` (από την εφαρμογή Nette Application v3.2):
+
+```php
+#[Requires(methods: ['GET', 'POST', 'HEAD', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'])]
+class MyPresenter extends Nette\Application\UI\Presenter
+{
+}
+```
+
+Στην έκδοση 3.1, η επαλήθευση πραγματοποιείται στο `checkHttpMethod()`, το οποίο ελέγχει αν η μέθοδος που καθορίζεται στην αίτηση περιλαμβάνεται στον πίνακα `$presenter->allowedMethods`. Προσθέστε μια μέθοδο ως εξής:
+
+```php
+class MyPresenter extends Nette\Application\UI\Presenter
+{
+ protected function checkHttpMethod(): void
+ {
+ $this->allowedMethods[] = 'OPTIONS';
+ parent::checkHttpMethod();
+ }
+}
+```
+
+Είναι ζωτικής σημασίας να τονιστεί ότι αν επιτρέψετε τη μέθοδο `OPTIONS`, πρέπει επίσης να τη χειριστείτε σωστά μέσα στον παρουσιαστή σας. Αυτή η μέθοδος χρησιμοποιείται συχνά ως το λεγόμενο preflight request, το οποίο οι φυλλομετρητές στέλνουν αυτόματα πριν από το πραγματικό αίτημα, όταν είναι απαραίτητο να καθοριστεί αν το αίτημα επιτρέπεται από την άποψη της πολιτικής CORS (Cross-Origin Resource Sharing). Εάν επιτρέψετε αυτή τη μέθοδο αλλά δεν υλοποιήσετε μια κατάλληλη απάντηση, μπορεί να οδηγήσει σε ασυνέπειες και πιθανά ζητήματα ασφάλειας.
+
+
Περαιτέρω ανάγνωση .[#toc-further-reading]
==========================================
diff --git a/application/el/routing.texy b/application/el/routing.texy
index 448fa6f67b..0ebcc505ba 100644
--- a/application/el/routing.texy
+++ b/application/el/routing.texy
@@ -216,7 +216,7 @@ $router->addRoute('//www.%sld%.%tld%/%basePath%//addRoute('/[/]', [
@@ -225,7 +225,7 @@ $router->addRoute('/[/]', [
]);
```
-Ή μπορούμε να χρησιμοποιήσουμε αυτή τη μορφή, παρατηρήστε την αναδιατύπωση της κανονικής έκφρασης επικύρωσης:
+Για μια πιο λεπτομερή προδιαγραφή, μπορεί να χρησιμοποιηθεί μια ακόμη πιο εκτεταμένη μορφή, όπου εκτός από τις προεπιλεγμένες τιμές, μπορούν να οριστούν και άλλες ιδιότητες παραμέτρων, όπως μια κανονική έκφραση επικύρωσης (βλ. την παράμετρο `id` ):
```php
use Nette\Routing\Route;
@@ -243,7 +243,7 @@ $router->addRoute('/[/]', [
]);
```
-Αυτές οι πιο ομιλητικές μορφές είναι χρήσιμες για την προσθήκη άλλων μεταδεδομένων.
+Είναι σημαντικό να σημειωθεί ότι εάν οι παράμετροι που ορίζονται στον πίνακα δεν περιλαμβάνονται στη μάσκα διαδρομής, οι τιμές τους δεν μπορούν να αλλάξουν, ούτε καν με τη χρήση παραμέτρων ερωτήματος που καθορίζονται μετά από ένα ερωτηματικό στη διεύθυνση URL.
Φίλτρα και μεταφράσεις .[#toc-filters-and-translations]
@@ -368,7 +368,7 @@ $router->addRoute('', function (string $lang) {
Ενότητες .[#toc-modules]
------------------------
-Αν έχουμε περισσότερες διαδρομές που ανήκουν σε ένα [module |modules], μπορούμε να χρησιμοποιήσουμε το `withModule()` για να τις ομαδοποιήσουμε:
+Αν έχουμε περισσότερες διαδρομές που ανήκουν σε μία [ενότητα |directory-structure#Presenters and Templates], μπορούμε να χρησιμοποιήσουμε το `withModule()` για να τις ομαδοποιήσουμε:
```php
$router = new RouteList;
@@ -477,10 +477,10 @@ $router->addRoute('index.html \.html?|\.php|>', /* ... */);
Ενσωμάτωση .[#toc-integration]
==============================
-Για να συνδέσουμε τον δρομολογητή μας στην εφαρμογή, πρέπει να ενημερώσουμε το DI container σχετικά με αυτόν. Ο ευκολότερος τρόπος είναι να προετοιμάσουμε το εργοστάσιο που θα κατασκευάσει το αντικείμενο του δρομολογητή και να πούμε στη διαμόρφωση του δοχείου να το χρησιμοποιήσει. Ας πούμε λοιπόν ότι γράφουμε μια μέθοδο για το σκοπό αυτό `App\Router\RouterFactory::createRouter()`:
+Για να συνδέσουμε τον δρομολογητή μας στην εφαρμογή, πρέπει να ενημερώσουμε το DI container σχετικά με αυτόν. Ο ευκολότερος τρόπος είναι να προετοιμάσουμε το εργοστάσιο που θα κατασκευάσει το αντικείμενο του δρομολογητή και να πούμε στη διαμόρφωση του δοχείου να το χρησιμοποιήσει. Ας πούμε λοιπόν ότι γράφουμε μια μέθοδο για το σκοπό αυτό `App\Core\RouterFactory::createRouter()`:
```php
-namespace App\Router;
+namespace App\Core;
use Nette\Application\Routers\RouteList;
@@ -499,7 +499,7 @@ class RouterFactory
```neon
services:
- - App\Router\RouterFactory::createRouter
+ - App\Core\RouterFactory::createRouter
```
Οποιεσδήποτε εξαρτήσεις, όπως μια σύνδεση βάσης δεδομένων κ.λπ., περνούν στη μέθοδο factory ως παράμετροι με τη χρήση [αυτόματης σύνδεσης |dependency-injection:autowiring]:
@@ -663,7 +663,7 @@ $router->addRoute(/* ... */);
Έτσι και πάλι θα δημιουργήσουμε μια μέθοδο που θα κατασκευάσει ένα δρομολογητή, για παράδειγμα:
```php
-namespace App\Router;
+namespace App\Core;
use Nette\Routing\RouteList;
@@ -694,7 +694,7 @@ $httpRequest = $container->getByType(Nette\Http\IRequest::class);
Ή θα δημιουργήσουμε αντικείμενα απευθείας:
```php
-$router = App\Router\RouterFactory::createRouter();
+$router = App\Core\RouterFactory::createRouter();
$httpRequest = (new Nette\Http\RequestFactory)->fromGlobals();
```
diff --git a/application/el/templates.texy b/application/el/templates.texy
index 4b83599f45..a26b5532fb 100644
--- a/application/el/templates.texy
+++ b/application/el/templates.texy
@@ -34,35 +34,81 @@
Ορίζει το μπλοκ `content`, το οποίο εισάγεται στη θέση του `{include content}` στη διάταξη, και επίσης επαναπροσδιορίζει το μπλοκ `title`, το οποίο αντικαθιστά το `{block title}` στη διάταξη. Προσπαθήστε να φανταστείτε το αποτέλεσμα.
-Αναζήτηση προτύπων .[#toc-search-for-templates]
------------------------------------------------
+Αναζήτηση προτύπου .[#toc-template-lookup]
+------------------------------------------
-Η διαδρομή προς τα πρότυπα προκύπτει σύμφωνα με μια απλή λογική. Προσπαθεί να δει αν ένα από αυτά τα αρχεία προτύπων υπάρχει σε σχέση με τον κατάλογο όπου βρίσκεται η κλάση presenter, όπου `` είναι το όνομα του τρέχοντος παρουσιαστή και `` είναι το όνομα της τρέχουσας δράσης:
+Στους παρουσιαστές, δεν χρειάζεται να καθορίσετε ποιο πρότυπο πρέπει να αποδοθεί- το πλαίσιο θα καθορίσει αυτόματα τη διαδρομή, διευκολύνοντας την κωδικοποίηση για εσάς.
-- `templates//.latte`
-- `templates/..latte`
+Αν χρησιμοποιείτε μια δομή καταλόγου όπου κάθε παρουσιαστής έχει το δικό του κατάλογο, απλά τοποθετήστε το πρότυπο σε αυτόν τον κατάλογο κάτω από το όνομα της ενέργειας (π.χ. προβολή). Για παράδειγμα, για τη δράση `default`, χρησιμοποιήστε το πρότυπο `default.latte`:
-Αν το πρότυπο δεν βρεθεί, θα προσπαθήσει να ψάξει στον κατάλογο `templates` ένα επίπεδο πιο πάνω, δηλαδή στο ίδιο επίπεδο με τον κατάλογο με την κλάση παρουσιαστή.
+/--pre
+app/
+└── Presentation/
+ └── Home/
+ ├── HomePresenter.php
+ └── default.latte
+\--
-Εάν το πρότυπο δεν βρεθεί ούτε εκεί, η απάντηση είναι ένα [σφάλμα 404 |presenters#Error 404 etc.].
+Εάν χρησιμοποιείτε μια δομή όπου οι παρουσιαστές βρίσκονται μαζί σε έναν κατάλογο και τα πρότυπα σε έναν φάκελο `templates`, αποθηκεύστε το είτε σε ένα αρχείο `..latte` είτε στο `/.latte`:
-Μπορείτε επίσης να αλλάξετε την προβολή χρησιμοποιώντας το `$this->setView('otherView')`. Ή, αντί για αναζήτηση, καθορίστε απευθείας το όνομα του αρχείου προτύπου χρησιμοποιώντας τη διεύθυνση `$this->template->setFile('/path/to/template.latte')`.
+/--pre
+app/
+└── Presenters/
+ ├── HomePresenter.php
+ └── templates/
+ ├── Home.default.latte ← 1st variant
+ └── Home/
+ └── default.latte ← 2nd variant
+\--
+
+Ο κατάλογος `templates` μπορεί επίσης να τοποθετηθεί ένα επίπεδο ψηλότερα, στο ίδιο επίπεδο με τον κατάλογο με τις κλάσεις παρουσιαστών.
+
+Εάν το πρότυπο δεν βρεθεί, ο παρουσιαστής απαντά με το [σφάλμα 404 - σελίδα δεν βρέθηκε |presenters#Error 404 etc].
+
+Μπορείτε να αλλάξετε την προβολή χρησιμοποιώντας το `$this->setView('anotherView')`. Είναι επίσης δυνατό να καθορίσετε απευθείας το αρχείο προτύπου με το `$this->template->setFile('/path/to/template.latte')`.
.[note]
-Μπορείτε να αλλάξετε τις διαδρομές στις οποίες αναζητούνται τα πρότυπα υπερκαλύπτοντας τη μέθοδο [formatTemplateFiles |api:Nette\Application\UI\Presenter::formatTemplateFiles()], η οποία επιστρέφει έναν πίνακα πιθανών διαδρομών αρχείων.
+Τα αρχεία στα οποία αναζητούνται τα πρότυπα μπορούν να αλλάξουν με την παράκαμψη της μεθόδου [formatTemplateFiles() |api:Nette\Application\UI\Presenter::formatTemplateFiles()], η οποία επιστρέφει έναν πίνακα πιθανών ονομάτων αρχείων.
+
+
+Αναζήτηση προτύπων διάταξης .[#toc-layout-template-lookup]
+----------------------------------------------------------
+
+Η Nette αναζητά επίσης αυτόματα το αρχείο διάταξης.
+
+Εάν χρησιμοποιείτε μια δομή καταλόγου όπου κάθε παρουσιαστής έχει το δικό του κατάλογο, τοποθετήστε τη διάταξη είτε στο φάκελο με τον παρουσιαστή, εάν αφορά μόνο αυτόν, είτε ένα επίπεδο ψηλότερα εάν είναι κοινή για πολλούς παρουσιαστές:
+
+/--pre
+app/
+└── Presentation/
+ ├── @layout.latte ← common layout
+ └── Home/
+ ├── @layout.latte ← only for Home presenter
+ ├── HomePresenter.php
+ └── default.latte
+\--
+
+Εάν χρησιμοποιείτε μια δομή όπου οι παρουσιαστές είναι ομαδοποιημένοι σε έναν κατάλογο και τα πρότυπα βρίσκονται σε έναν φάκελο `templates`, η διάταξη αναμένεται στις ακόλουθες θέσεις:
-Η διάταξη αναμένεται στα ακόλουθα αρχεία:
+/--pre
+app/
+└── Presenters/
+ ├── HomePresenter.php
+ └── templates/
+ ├── @layout.latte ← common layout
+ ├── Home.@layout.latte ← only for Home, 1st variant
+ └── Home/
+ └── @layout.latte ← only for Home, 2nd variant
+\--
-- `templates//@.latte`
-- `templates/.@.latte`
-- `templates/@.latte` διάταξη κοινή για πολλούς παρουσιαστές
+Εάν ο παρουσιαστής βρίσκεται σε μια ενότητα, θα αναζητήσει επίσης πιο πάνω στο δέντρο καταλόγων σύμφωνα με την ένθεση της ενότητας.
-`` είναι το όνομα του τρέχοντος παρουσιαστή και `` είναι το όνομα της διάταξης, η οποία είναι εξ ορισμού `'layout'`. Το όνομα μπορεί να αλλάξει με το `$this->setLayout('otherLayout')`, έτσι ώστε να δοκιμάζονται τα αρχεία `@otherLayout.latte`.
+Το όνομα της διάταξης μπορεί να αλλάξει χρησιμοποιώντας το `$this->setLayout('layoutAdmin')` και τότε θα αναμένεται στο αρχείο `@layoutAdmin.latte`. Μπορείτε επίσης να καθορίσετε απευθείας το αρχείο προτύπου διάταξης χρησιμοποιώντας το `$this->setLayout('/path/to/template.latte')`.
-Μπορείτε επίσης να καθορίσετε απευθείας το όνομα του αρχείου του προτύπου διάταξης χρησιμοποιώντας το `$this->setLayout('/path/to/template.latte')`. Η χρήση του `$this->setLayout(false)` θα απενεργοποιήσει την αναζήτηση διάταξης.
+Η χρήση του `$this->setLayout(false)` ή της ετικέτας `{layout none}` μέσα στο πρότυπο απενεργοποιεί την αναζήτηση διάταξης.
.[note]
-Μπορείτε να αλλάξετε τις διαδρομές στις οποίες αναζητούνται τα πρότυπα με την παράκαμψη της μεθόδου [formatLayoutTemplateFiles |api:Nette\Application\UI\Presenter::formatLayoutTemplateFiles()], η οποία επιστρέφει έναν πίνακα πιθανών διαδρομών αρχείων.
+Τα αρχεία στα οποία αναζητούνται τα πρότυπα διάταξης μπορούν να αλλάξουν με την παράκαμψη της μεθόδου [formatLayoutTemplateFiles() |api:Nette\Application\UI\Presenter::formatLayoutTemplateFiles()], η οποία επιστρέφει έναν πίνακα πιθανών ονομάτων αρχείων.
Μεταβλητές στο πρότυπο .[#toc-variables-in-the-template]
@@ -104,7 +150,7 @@ class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template
Μπορείτε επίσης να αφεθείτε στην πολυτέλεια του ψιθυρίσματος στα πρότυπα, απλά εγκαταστήστε το πρόσθετο Latte στο PhpStorm και καθορίστε το όνομα της κλάσης στην αρχή του προτύπου, δείτε το άρθρο "Latte: πώς να πληκτρολογήσετε το σύστημα":https://blog.nette.org/el/latte-pos-na-chresimopoiesete-to-systema-typon:
```latte
-{templateType App\Presenters\ArticleTemplate}
+{templateType App\Presentation\Article\ArticleTemplate}
...
```
@@ -176,7 +222,7 @@ public function beforeRender(): void
Latte έκδοση 3 προσφέρει έναν πιο προηγμένο τρόπο δημιουργώντας μια [επέκταση |latte:creating-extension] για κάθε έργο ιστού. Εδώ είναι ένα πρόχειρο παράδειγμα μιας τέτοιας κλάσης:
```php
-namespace App\Templating;
+namespace App\Presentation\Accessory;
final class LatteExtension extends Latte\Extension
{
@@ -214,7 +260,7 @@ final class LatteExtension extends Latte\Extension
```neon
latte:
extensions:
- - App\Templating\LatteExtension
+ - App\Presentation\Accessory\LatteExtension
```
@@ -239,7 +285,7 @@ protected function beforeRender(): void
```neon
latte:
extensions:
- - Latte\Essential\TranslatorExtension
+ - Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)
```
Ο μεταφραστής μπορεί στη συνέχεια να χρησιμοποιηθεί, για παράδειγμα, ως φίλτρο `|translate`, με πρόσθετες παραμέτρους που περνούν στη μέθοδο `translate()` (βλ. `foo, bar`):
diff --git a/application/en/@home.texy b/application/en/@home.texy
index fac8c0d767..69bea8c6ba 100644
--- a/application/en/@home.texy
+++ b/application/en/@home.texy
@@ -2,35 +2,84 @@ Nette Application
*****************
.[perex]
-The `nette/application` package is the basis for creating interactive web applications.
-
-- [How do applications work? |how-it-works]
-- [Bootstrap]
-- [Presenters]
-- [Templates]
-- [Modules]
-- [Routing]
-- [Creating URL Links |creating-links]
-- [Interactive Components |components]
-- [AJAX & Snippets |ajax]
-- [Multiplier |multiplier]
-- [Configuration]
+Nette Application is the core of the Nette framework that brings powerful tools for creating modern web applications. It offers numerous exceptional features that significantly simplify development and improve code security and maintainability.
Installation
------------
-Download and install the package using [Composer|best-practices:composer]:
+Download and install the library using [Composer|best-practices:composer]:
```shell
composer require nette/application
```
-| version | compatible with PHP
+
+Why choose Nette Application?
+-----------------------------
+
+Nette has always been a pioneer in web technologies.
+
+**Bidirectional Router:** Nette features an advanced routing system unique in its bidirectionality - it not only translates URLs to application actions but can also generate URLs in reverse. This means:
+- You can modify the URL structure of the entire application at any time without modifying template files
+- URLs are automatically canonicalized, improving SEO
+- Routing is defined in one place, not scattered in annotations
+
+**Components and Signals:** The built-in component system inspired by Delphi and React.js is unique among PHP frameworks:
+- Enables creating reusable UI elements
+- Supports hierarchical component composition
+- Offers elegant AJAX request handling using signals
+- Rich library of ready-made components on [Componette](https://componette.org)
+
+**AJAX and Snippets:** Nette introduced a revolutionary way of working with AJAX in 2009, before solutions like Hotwire for Ruby on Rails or Symfony UX Turbo:
+- Snippets allow updating only parts of the page without writing JavaScript
+- Automatic integration with the component system
+- Smart invalidation of page sections
+- Minimal data transfer
+
+**Intuitive [Latte|latte:] Templates:** The most secure templating system for PHP with advanced features:
+- Automatic XSS protection with context-sensitive escaping
+- Extensible with custom filters, functions, and tags
+- Template inheritance and snippets for AJAX
+- Excellent PHP 8.x support with type system
+
+**Dependency Injection:** Nette fully utilizes Dependency Injection:
+- Automatic dependency passing (autowiring)
+- Configuration using clear NEON format
+- Support for component factories
+
+
+Main Benefits
+-------------
+
+- **Security**: Automatic protection against [vulnerabilities|nette:vulnerability-protection] like XSS, CSRF, etc.
+- **Productivity**: Less writing, more features thanks to smart design
+- **Debugging**: [Tracy debugger|tracy:] with routing panel
+- **Performance**: Intelligent caching system, lazy loading of components
+- **Flexibility**: Easy URL modification even after application completion
+- **Components**: Unique system of reusable UI elements
+- **Modern**: Full support for PHP 8.4+ and type system
+
+
+Getting Started
+---------------
+
+1. [Understanding Applications |how-it-works] - Understanding basic architecture
+2. [Presenters |presenters] - Working with presenters and actions
+3. [Templates |templates] - Creating templates in Latte
+4. [Routing |routing] - URL configuration
+5. [Interactive Components |components] - Using the component system
+
+
+PHP Compatibility
+-----------------
+
+| version | compatible with PHP
|-----------|-------------------
-| Nette Application 4.0 | PHP 8.0 – 8.2
-| Nette Application 3.1 | PHP 7.2 – 8.2
+| Nette Application 4.0 | PHP 8.1 – 8.4
+| Nette Application 3.2 | PHP 8.1 – 8.4
+| Nette Application 3.1 | PHP 7.2 – 8.3
| Nette Application 3.0 | PHP 7.1 – 8.0
| Nette Application 2.4 | PHP 5.6 – 8.0
-Applies to the latest patch versions.
+Valid for the latest patch versions.
\ No newline at end of file
diff --git a/application/en/@left-menu.texy b/application/en/@left-menu.texy
index bc4b360cee..94208d2550 100644
--- a/application/en/@left-menu.texy
+++ b/application/en/@left-menu.texy
@@ -4,7 +4,7 @@ Nette Application
- [Bootstrap]
- [Presenters]
- [Templates]
-- [Modules]
+- [Directory structure |directory-structure]
- [Routing]
- [Creating URL Links |creating-links]
- [Interactive Components |components]
diff --git a/application/en/ajax.texy b/application/en/ajax.texy
index f43ff28c2f..819a6e4627 100644
--- a/application/en/ajax.texy
+++ b/application/en/ajax.texy
@@ -3,10 +3,10 @@ AJAX & Snippets
-Modern web applications nowadays run half on a server and half in a browser. AJAX is a vital uniting factor. What support does the Nette Framework offer?
-- sending template fragments (so-called *snippets*)
+In the era of modern web applications, where functionality often spans between the server and the browser, AJAX is an essential connecting element. What options does the Nette Framework offer in this area?
+- sending parts of the template, so-called snippets
- passing variables between PHP and JavaScript
-- AJAX applications debugging
+- tools for debugging AJAX requests
@@ -14,29 +14,32 @@ Modern web applications nowadays run half on a server and half in a browser. AJA
AJAX Request
============
-An AJAX request does not differ from a classic request - the presenter is called with a specific view and parameters. It is also up to the presenter how to respond to it: it can use its own routine, which returns an HTML code fragment (HTML snippet), an XML document, a JSON object, or JavaScript code.
+An AJAX request fundamentally does not differ from a classic HTTP request. A presenter is called with specific parameters. It's up to the presenter how to respond to the request - it can return data in JSON format, send a part of HTML code, an XML document, etc.
-On the server side, an AJAX request can be detected using the service method [encapsulating the HTTP request |http:request] `$httpRequest->isAjax()` (detects based on the HTTP header `X-Requested-With`). Inside the presenter, a shortcut is available in the form of the method `$this->isAjax()`.
+On the browser side, we initiate an AJAX request using the `fetch()` function:
-There is a pre-processed object called `payload` dedicated to sending data to the browser in JSON.
-
-```php
-public function actionDelete(int $id): void
-{
- if ($this->isAjax()) {
- $this->payload->message = 'Success';
- }
- // ...
-}
+```js
+fetch(url, {
+ headers: {'X-Requested-With': 'XMLHttpRequest'},
+})
+.then(response => response.json())
+.then(payload => {
+ // processing the response
+});
```
-For a full control over your JSON output use the `sendJson` method in your presenter. It terminates presenter immediately and you'll do without a template:
+On the server side, an AJAX request is recognized by the `$httpRequest->isAjax()` method of the service [encapsulating the HTTP request |http:request]. It uses the HTTP header `X-Requested-With`, so it's essential to send it. Within the presenter, you can use the `$this->isAjax()` method.
+
+If you want to send data in JSON format, use the [`sendJson()` |presenters#Sending a response] method. The method also terminates the presenter's activity.
```php
-$this->sendJson(['key' => 'value', /* ... */]);
+public function actionExport(): void
+{
+ $this->sendJson($this->model->getData);
+}
```
-If we want to send HTML, we can either set a special template for AJAX requests:
+If you plan to respond with a special template designed for AJAX, you can do it as follows:
```php
public function handleClick($param): void
@@ -49,22 +52,38 @@ public function handleClick($param): void
```
+Snippets
+========
+
+The most powerful tool offered by Nette for connecting the server with the client are snippets. With them, you can turn an ordinary application into an AJAX one with minimal effort and a few lines of code. The Fifteen example demonstrates how it all works, and its code can be found on [GitHub |https://github.com/nette-examples/fifteen].
+
+Snippets, or clippings, allow you to update only parts of the page, instead of reloading the entire page. This is faster and more efficient, and also provides a more comfortable user experience. Snippets might remind you of Hotwire for Ruby on Rails or Symfony UX Turbo. Interestingly, Nette introduced snippets 14 years earlier.
+
+How do snippets work? When the page is first loaded (a non-AJAX request), the entire page, including all snippets, is loaded. When the user interacts with the page (e.g., clicks a button, submits a form, etc.), instead of loading the entire page, an AJAX request is made. The code in the presenter performs the action and decides which snippets need updating. Nette renders these snippets and sends them in the form of a JSON array. The handling code in the browser then inserts the received snippets back into the page. Therefore, only the code of the changed snippets is transferred, saving bandwidth and speeding up loading compared to transferring the entire page content.
+
+
Naja
-====
+----
-The [Naja library|https://naja.js.org] is used to handle AJAX requests on the browser side. [Install |https://naja.js.org/#/guide/01-install-setup-naja] it as a node.js package (to use with Webpack, Rollup, Vite, Parcel and more):
+To handle snippets on the browser side, the [Naja library |https://naja.js.org] is used. [Install it |https://naja.js.org/#/guide/01-install-setup-naja] as a node.js package (for use with applications such as Webpack, Rollup, Vite, Parcel, and others):
```shell
npm install naja
```
-…or insert it directly into the page template:
+... or insert it directly into the page template:
```html
```
-To create an AJAX request from a regular link (signal) or form submittion, simply mark the relevant link, form, or button with the class `ajax`:
+First you need to [initialize |https://naja.js.org/#/guide/01-install-setup-naja?id=initialization] the library:
+
+```js
+naja.initialize();
+```
+
+To make an ordinary link (signal) or form submission an AJAX request, simply mark the respective link, form, or button with the `ajax` class:
```html
Go
@@ -74,64 +93,39 @@ To create an AJAX request from a regular link (signal) or form submittion, simpl
or
+
```
-Snippets
-========
-
-There is a far more powerful tool of built-in AJAX support – snippets. Using them makes it possible to turn a regular application into an AJAX one using only a few lines of code. How it all works is demonstrated in the Fifteen example whose code is also accessible in the build or on [GitHub |https://github.com/nette-examples/fifteen].
-
-The way snippets work is that the whole page is transferred during the initial (i.e. non-AJAX) request and then with every AJAX [subrequest |components#signal] (request of the same view of the same presenter) only the code of the changed parts is transferred in the `payload` repository mentioned earlier.
+Redrawing Snippets
+------------------
-Snippets may remind you of Hotwire for Ruby on Rails or Symfony UX Turbo, but Nette came up with them fourteen years earlier.
-
-
-Invalidation of Snippets
-========================
-
-Each descendant of class [Control |components] (which a Presenter is too) is able to remember whether there were any changes during a request that require it to re-render. There are a pair of methods to handle this: `redrawControl()` and `isControlInvalid()`. An example:
+Every object of the [Control |components] class (including the Presenter itself) keeps a record of whether changes have occurred that necessitate its redrawing. The `redrawControl()` method is employed for this purpose.
```php
public function handleLogin(string $user): void
{
- // The object has to re-render after the user has logged in
+ // after logging in, it is necessary to redraw the relevant part
$this->redrawControl();
// ...
}
```
-Nette however offers an even finer resolution than whole components. The listed methods accept the name of a so-called "snippet" as an optional parameter. A "snippet" is basically an element in your template marked for that purpose by a Latte tag, more on that later. Thus it is possible to ask a component to redraw only *parts* of its template. If the entire component is invalidated then all of its snippets are re-rendered. A component is “invalid” also if any of its subcomponents is invalid.
-
-```php
-$this->isControlInvalid(); // -> false
-$this->redrawControl('header'); // invalidates the snippet named 'header'
-$this->isControlInvalid('header'); // -> true
-$this->isControlInvalid('footer'); // -> false
-$this->isControlInvalid(); // -> true, at least one snippet is invalid
+Nette also allows for finer control of what needs redrawing. The aforementioned method can take the snippet name as an argument. Thus, it's possible to invalidate (meaning: force a redraw) at the template part level. If the entire component is invalidated, every snippet of it is also redrawn:
-$this->redrawControl(); // invalidates the whole component, every snippet
-$this->isControlInvalid('footer'); // -> true
+```php
+// invalidates the 'header' snippet
+$this->redrawControl('header');
```
-A component which receives a signal is automatically marked for redrawing.
-
-Thanks to snippet redrawing we know exactly which parts of which elements should be re-rendered.
-
-
-Tag `{snippet} … {/snippet}` .{toc: Tag snippet}
-================================================
-Rendering of the page proceeds very similarly to a regular request: the same templates are loaded, etc. The vital part is, however, to leave out the parts that are not supposed to reach the output; the other parts shall be associated with an identifier and sent to the user in a comprehensible format for a JavaScript handler.
+Snippets in Latte
+-----------------
-
-Syntax
-------
-
-If there is a control or a snippet in the template, we have to wrap it using the `{snippet} ... {/snippet}` pair tag - it will make sure that the rendered snippet will be "cut out" and sent to the browser. It will also enclose it in a helper `` tag (it is possible to use a different one). In the following example a snippet named `header` is defined. It may as well represent the template of a component:
+Using snippets in Latte is extremely easy. To define a part of the template as a snippet, simply wrap it in `{snippet}` and `{/snippet}` tags:
```latte
{snippet header}
@@ -139,7 +133,9 @@ If there is a control or a snippet in the template, we have to wrap it using the
{/snippet}
```
-A snippet of a type other than `
` or a snippet with additional HTML attributes is achieved by using the attribute variant:
+The snippet creates an element `
` in the HTML page with a specially generated `id`. When redrawing a snippet, the content of this element is updated. Therefore, when the page is initially rendered, all snippets must also be rendered, even if they may initially be empty.
+
+You can also create a snippet with an element other than `
` using an n:attribute:
```latte
@@ -148,138 +144,106 @@ A snippet of a type other than `` or a snippet with additional HTML attribu
```
-Dynamic Snippets
-================
+Snippet Areas
+-------------
-In Nette you can also define snippets with a dynamic name based on a runtime parameter. This is most suitable for various lists where we need to change just one row but we don't want transfer the whole list along with it. An example of this would be:
+Snippet names can also be expressions:
```latte
-
- {foreach $list as $id => $item}
- - {$item} update
- {/foreach}
-
+{foreach $items as $id => $item}
+
{$item}
+{/foreach}
```
-There is one static snippet called `itemsContainer`, containing several dynamic snippets: `item-0`, `item-1` and so on.
+This way, we'll get several snippets like `item-0`, `item-1`, etc. If we were to directly invalidate a dynamic snippet (e.g., `item-1`), nothing would be redrawn. The reason being, snippets function as true excerpts and only they themselves are rendered directly. However, in the template, there isn't technically a snippet named `item-1`. It only emerges when executing the surrounding code of the snippet, in this case, the foreach loop. Hence, we'll mark the part of the template that needs to be executed with the `{snippetArea}` tag:
-You can't redraw a dynamic snippet directly (redrawing of `item-1` has no effect), you have to redraw its parent snippet (in this example `itemsContainer`). This causes the code of the parent snippet to be executed, but then just its sub-snippets are sent to the browser. If you want to send over just one of the sub-snippets, you have to modify input for the parent snippet to not generate the other sub-snippets.
+```latte
+
+ {foreach $items as $id => $item}
+ - {$item}
+ {/foreach}
+
+```
-In the example above you have to make sure that for an AJAX request only one item will be added to the `$list` array, therefore the `foreach` loop will print just one dynamic snippet.
+And we'll redraw both the individual snippet and the entire overarching area:
```php
-class HomePresenter extends Nette\Application\UI\Presenter
-{
- /**
- * This method returns data for the list.
- * Usually this would just request the data from a model.
- * For the purpose of this example, the data is hard-coded.
- */
- private function getTheWholeList(): array
- {
- return [
- 'First',
- 'Second',
- 'Third',
- ];
- }
-
- public function renderDefault(): void
- {
- if (!isset($this->template->list)) {
- $this->template->list = $this->getTheWholeList();
- }
- }
-
- public function handleUpdate(int $id): void
- {
- $this->template->list = $this->isAjax()
- ? []
- : $this->getTheWholeList();
- $this->template->list[$id] = 'Updated item';
- $this->redrawControl('itemsContainer');
- }
-}
+$this->redrawControl('itemsContainer');
+$this->redrawControl('item-1');
```
+It's also essential to ensure that the `$items` array contains only the items that should be redrawn.
-Snippets in an Included Template
-================================
-
-It can happen that the snippet is in a template which is being included from a different template. In that case we need to wrap the inclusion code in the second template with the `snippetArea` tag, then we redraw both the snippetArea and the actual snippet.
-
-Tag `snippetArea` ensures that the code inside is executed but only the actual snippet in the included template is sent to the browser.
+When inserting another template into the main one using the `{include}` tag, which has snippets, it's necessary to again wrap the included template in a `snippetArea` and invalidate both the snippet and the area together:
```latte
-{* parent.latte *}
-{snippetArea wrapper}
- {include 'child.latte'}
+{snippetArea include}
+ {include 'included.latte'}
{/snippetArea}
```
+
```latte
-{* child.latte *}
+{* included.latte *}
{snippet item}
-...
+ ...
{/snippet}
```
+
```php
-$this->redrawControl('wrapper');
+$this->redrawControl('include');
$this->redrawControl('item');
```
-You can also combine it with dynamic snippets.
+Snippets in Components
+----------------------
-Adding and Deleting
-===================
-
-If you add a new item into the list and invalidate `itemsContainer`, the AJAX request returns snippets including the new one, but the javascript handler won’t be able to render it. This is because there is no HTML element with the newly created ID.
-
-In this case, the simplest way is to wrap the whole list in one more snippet and invalidate it all:
+You can create snippets within [components], and Nette will automatically redraw them. However, there's a specific limitation: to redraw snippets, it calls the `render()` method without any parameters. Thus, passing parameters in the template won't work:
```latte
-{snippet wholeList}
-
- {foreach $list as $id => $item}
- - {$item} update
- {/foreach}
-
-{/snippet}
-
Add
+OK
+{control productGrid}
+
+will not work:
+{control productGrid $arg, $arg}
+{control productGrid:paginator}
```
+
+Sending User Data
+-----------------
+
+Along with snippets, you can send any additional data to the client. Simply write them into the `payload` object:
+
```php
-public function handleAdd(): void
+public function actionDelete(int $id): void
{
- $this->template->list = $this->getTheWholeList();
- $this->template->list[] = 'New one';
- $this->redrawControl('wholeList');
+ // ...
+ if ($this->isAjax()) {
+ $this->payload->message = 'Success';
+ }
}
```
-The same goes for deleting an item. It would be possible to send empty snippet, but usually lists can be paginated and it would be complicated to implement deleting one item and loading another (which used to be on a different page of the paginated list).
-
-Sending Parameters to Component
-===============================
+Sending Parameters
+==================
When we send parameters to the component via AJAX request, whether signal parameters or persistent parameters, we must provide their global name, which also contains the name of the component. The full name of parameter returns the `getParameterId()` method.
```js
-$.getJSON(
- {link changeCountBasket!},
- {
- {$control->getParameterId('id')}: id,
- {$control->getParameterId('count')}: count
- }
-});
+let url = new URL({link //foo!});
+url.searchParams.set({$control->getParameterId('bar')}, bar);
+
+fetch(url, {
+ headers: {'X-Requested-With': 'XMLHttpRequest'},
+})
```
-And handle method with s corresponding parameters in component.
+A handle method with the corresponding parameters in the component:
```php
-public function handleChangeCountBasket(int $id, int $count): void
+public function handleFoo(int $bar): void
{
-
}
```
diff --git a/application/en/bootstrap.texy b/application/en/bootstrap.texy
index c763e715a3..6459dee6d0 100644
--- a/application/en/bootstrap.texy
+++ b/application/en/bootstrap.texy
@@ -20,18 +20,44 @@ use Nette\Bootstrap\Configurator;
class Bootstrap
{
- public static function boot(): Configurator
+ private Configurator $configurator;
+ private string $rootDir;
+
+ public function __construct()
+ {
+ $this->rootDir = dirname(__DIR__);
+ // The configurator is responsible for setting up the application environment and services.
+ $this->configurator = new Configurator;
+ // Set the directory for temporary files generated by Nette (e.g. compiled templates)
+ $this->configurator->setTempDirectory($this->rootDir . '/temp');
+ }
+
+ public function bootWebApplication(): Nette\DI\Container
+ {
+ $this->initializeEnvironment();
+ $this->setupContainer();
+ return $this->configurator->createContainer();
+ }
+
+ private function initializeEnvironment(): void
{
- $appDir = dirname(__DIR__);
- $configurator = new Configurator;
- //$configurator->setDebugMode('secret@23.75.345.200');
- $configurator->enableTracy($appDir . '/log');
- $configurator->setTempDirectory($appDir . '/temp');
- $configurator->createRobotLoader()
+ // Nette is smart, and the development mode turns on automatically,
+ // or you can enable for a specific IP address it by uncommenting the following line:
+ // $this->configurator->setDebugMode('secret@23.75.345.200');
+
+ // Enables Tracy: the ultimate "swiss army knife" debugging tool.
+ $this->configurator->enableTracy($this->rootDir . '/log');
+
+ // RobotLoader: autoloads all classes in the given directory
+ $this->configurator->createRobotLoader()
->addDirectory(__DIR__)
->register();
- $configurator->addConfig($appDir . '/config/common.neon');
- return $configurator;
+ }
+
+ private function setupContainer(): void
+ {
+ // Load configuration files
+ $this->configurator->addConfig($this->rootDir . '/config/common.neon');
}
}
```
@@ -40,16 +66,15 @@ class Bootstrap
index.php
=========
-In the case of web applications, the initial file is `index.php`, which is located in the public directory `www/`. It lets the `Bootstrap` class to initialize the environment and return the `$configurator` which creates DI container. Then it obtains the `Application` service, that runs the web application:
+In the case of web applications, the primary file is `index.php`, which is located in the [public directory |directory-structure#public-directory-www] `www/`. This will have the Bootstrap class initialize the environment and produce a DI container. It then gets the `Application` service from it, which starts the web application:
```php
-// initialize the environment + get Configurator object
-$configurator = App\Bootstrap::boot();
-// create a DI container
-$container = $configurator->createContainer();
+$bootstrap = new App\Bootstrap;
+// Initialize the environment + create a DI container
+$container = $bootstrap->bootWebApplication();
// DI container creates a Nette\Application\Application object
$application = $container->getByType(Nette\Application\Application::class);
-// start Nette application
+// Start the Nette application and handle the incoming request
$application->run();
```
@@ -59,26 +84,42 @@ As you can see, the [api:Nette\Bootstrap\Configurator] class, which we will now
Development vs Production Mode
==============================
-Nette distinguishes between two basic modes in which a request is executed: development and production. The development mode is focused on the maximum comfort of the programmer, Tracy is displayed, the cache is automatically updated when changing templates or DI container configuration, etc. Production mode is focused on performance, Tracy only logs errors and changes of templates and other files are not checked.
+Nette behaves differently depending on whether it is running on a development or production server:
+
+🛠️ Development Mode:
+ - Displays the Tracy debug bar with useful information (e.g., SQL queries, execution time, memory usage).
+ - Shows a detailed error page with function call traces and variable contents when an error occurs.
+ - Automatically refreshes the cache when Latte templates, configuration files, etc., are modified.
+
+
+🚀 Production Mode:
+ - Does not display any debugging information; all errors are logged.
+ - Shows an `ErrorPresenter` or a generic "Server Error" page when an error occurs.
+ - Cache is never automatically refreshed!
+ - Optimized for speed and security.
-Mode selection is done by autodetection, so there is usually no need to configure or switch anything manually. The mode is development if the application is running on localhost (ie IP address `127.0.0.1` or `::1`) and no proxy is present (ie its HTTP header). Otherwise, it runs in production mode.
+
+The mode is determined automatically, so in most cases, there’s no need to configure or switch it manually:
+
+- Development mode: Active on localhost (IP address `127.0.0.1` or `::1`) unless a proxy is in use (i.e., based on its HTTP headers).
+- Production mode: Active everywhere else.
If you want to enable development mode in other cases, for example, for programmers accessing from a specific IP address, you can use `setDebugMode()`:
```php
-$configurator->setDebugMode('23.75.345.200'); // one or more IP addresses
+$this->configurator->setDebugMode('23.75.345.200'); // one or more IP addresses
```
We definitely recommend combining an IP address with a cookie. We will store a secret token into the `nette-debug` cookie, e.g. `secret1234`, and the development mode will be activated for programmers with this combination of IP and cookie.
```php
-$configurator->setDebugMode('secret1234@23.75.345.200');
+$this->configurator->setDebugMode('secret1234@23.75.345.200');
```
We can also turn off developer mode completely, even for localhost:
```php
-$configurator->setDebugMode(false);
+$this->configurator->setDebugMode(false);
```
Note that the value `true` turns on developer mode by hard, which should never happen on a production server.
@@ -90,7 +131,7 @@ Debugging Tool Tracy
For easy debugging, we will turn on the great tool [Tracy |tracy:]. In developer mode it visualizes errors and in production mode it logs errors to the specified directory:
```php
-$configurator->enableTracy($appDir . '/log');
+$this->configurator->enableTracy($this->rootDir . '/log');
```
@@ -100,7 +141,7 @@ Temporary Files
Nette uses the cache for DI container, RobotLoader, templates, etc. Therefore it is necessary to set the path to the directory where the cache will be stored:
```php
-$configurator->setTempDirectory($appDir . '/temp');
+$this->configurator->setTempDirectory($this->rootDir . '/temp');
```
On Linux or macOS, set the [write permissions |nette:troubleshooting#Setting directory permissions] for directories `log/` and `temp/`.
@@ -112,7 +153,7 @@ RobotLoader
Usually, we will want to automatically load the classes using [RobotLoader |robot-loader:], so we have to start it up and let it load classes from the directory where `Bootstrap.php` is located (i.e. `__DIR__`) and all its subdirectories:
```php
-$configurator->createRobotLoader()
+$this->configurator->createRobotLoader()
->addDirectory(__DIR__)
->register();
```
@@ -126,7 +167,7 @@ Timezone
Configurator allows you to specify a timezone for your application.
```php
-$configurator->setTimeZone('Europe/Prague');
+$this->configurator->setTimeZone('Europe/Prague');
```
@@ -143,16 +184,17 @@ In the development mode, the container is automatically updated each time you ch
Configuration files are loaded using `addConfig()`:
```php
-$configurator->addConfig($appDir . '/config/common.neon');
+$this->configurator->addConfig($this->rootDir . '/config/common.neon');
```
The method `addConfig()` can be called multiple times to add multiple files.
```php
-$configurator->addConfig($appDir . '/config/common.neon');
-$configurator->addConfig($appDir . '/config/local.neon');
+$configDir = $this->rootDir . '/config';
+$this->configurator->addConfig($configDir . '/common.neon');
+$this->configurator->addConfig($configDir . '/services.neon');
if (PHP_SAPI === 'cli') {
- $configurator->addConfig($appDir . '/config/cli.php');
+ $this->configurator->addConfig($configDir . '/cli.php');
}
```
@@ -169,7 +211,7 @@ Static Parameters
Parameters used in configuration files can be defined [in the section `parameters`|dependency-injection:configuration#parameters] and also passed (or overwritten) by the `addStaticParameters()` method (it has alias `addParameters()`). It is important that different parameter values cause the generation of additional DI containers, i.e. additional classes.
```php
-$configurator->addStaticParameters([
+$this->configurator->addStaticParameters([
'projectId' => 23,
]);
```
@@ -183,7 +225,7 @@ Dynamic Parameters
We can also add dynamic parameters to the container, their different values, unlike static parameters, will not cause the generation of new DI containers.
```php
-$configurator->addDynamicParameters([
+$this->configurator->addDynamicParameters([
'remoteIp' => $_SERVER['REMOTE_ADDR'],
]);
```
@@ -191,7 +233,7 @@ $configurator->addDynamicParameters([
Environment variables could be easily made available using dynamic parameters. We can access them via `%env.variable%` in configuration files.
```php
-$configurator->addDynamicParameters([
+$this->configurator->addDynamicParameters([
'env' => getenv(),
]);
```
@@ -206,6 +248,7 @@ You can use the following static parameters in the configuration files:
- `%wwwDir%` is the absolute path to the directory containing the `index.php` entry file
- `%tempDir%` is the absolute path to the directory for temporary files
- `%vendorDir%` is the absolute path to the directory where Composer installs libraries
+- `%rootDir%` is the absolute path to the root directory of the project
- `%debugMode%` indicates whether the application is in debug mode
- `%consoleMode%` indicates whether the request came through the command line
@@ -225,7 +268,7 @@ services:
Create a new instance and insert it in bootstrap:
```php
-$configurator->addServices([
+$this->configurator->addServices([
'myservice' => new App\Model\MyCustomService('foobar'),
]);
```
@@ -234,13 +277,21 @@ $configurator->addServices([
Different Environments
======================
-Feel free to customize the `Bootstrap` class to suit your needs. You can add parameters to the `boot()` method to differentiate web projects, or add other methods, such as `bootForTests()`, which initializes the environment for unit tests, `bootForCli()` for scripts called from the command line, and so on.
+Don't hesitate to customize the `Bootstrap` class according to your needs. You can add parameters to the `bootWebApplication()` method to differentiate between web projects. Alternatively, you can add other methods, such as `bootTestEnvironment()` to initialize the environment for unit tests, `bootConsoleApplication()` for scripts called from the command line, and so on.
```php
-public static function bootForTests(): Configurator
+public function bootTestEnvironment(): Nette\DI\Container
{
- $configurator = self::boot();
Tester\Environment::setup(); // Nette Tester initialization
- return $configurator;
+ $this->setupContainer();
+ return $this->configurator->createContainer();
+}
+
+public function bootConsoleApplication(): Nette\DI\Container
+{
+ $this->configurator->setDebugMode(false);
+ $this->initializeEnvironment();
+ $this->setupContainer();
+ return $this->configurator->createContainer();
}
```
diff --git a/application/en/components.texy b/application/en/components.texy
index b610a84739..2c79308bef 100644
--- a/application/en/components.texy
+++ b/application/en/components.texy
@@ -198,7 +198,7 @@ The link that calls the signal is created in the usual way, i.e. in the template
click here
```
-The signal is always called on the current presenter and view, so it is not possible to link to signal in different presenter / action.
+The signal is always called on the current presenter and action, it cannot be called on another presenter or action.
Thus, the signal causes the page to be reloaded in exactly the same way as in the original request, only in addition it calls the signal handling method with the appropriate parameters. If the method does not exist, exception [api:Nette\Application\UI\BadSignalException] is thrown, which is displayed to the user as error page 403 Forbidden.
@@ -230,6 +230,28 @@ In the template, these messages are available in the variable `$flashes` as obje
```
+Redirection After a Signal
+==========================
+
+After processing a component signal, redirection often follows. This situation is similar to forms—after submitting a form, we also redirect to prevent resubmission of data when the page is refreshed in the browser.
+
+```php
+$this->redirect('this') // redirects to the current presenter and action
+```
+
+Since a component is a reusable element and should not usually have a direct dependency on specific presenters, the `redirect()` and `link()` methods automatically interpret the parameter as a component signal:
+
+```php
+$this->redirect('click') // redirects to the 'click' signal of the same component
+```
+
+If you need to redirect to a different presenter or action, you can do so through the presenter:
+
+```php
+$this->getPresenter()->redirect('Product:show'); // redirects to a different presenter/action
+```
+
+
Persistent Parameters
=====================
@@ -347,7 +369,7 @@ services:
Finally, we will use this factory in our presenter:
```php
-class PollPresenter extends Nette\UI\Application\Presenter
+class PollPresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private PollControlFactory $pollControlFactory,
@@ -380,7 +402,7 @@ Components in Depth
Components in a Nette Application are the reusable parts of a web application that we embed in pages, which is the subject of this chapter. What exactly are the capabilities of such a component?
1) it is renderable in a template
-2) it knows which part of itself to render during an [AJAX request |ajax#invalidation] (snippets)
+2) it knows [which part of itself |ajax#snippets] to render during an AJAX request (snippets)
3) it has the ability to store its state in a URL (persistent parameters)
4) has the ability to respond to user actions (signals)
5) creates a hierarchical structure (where the root is the presenter)
@@ -430,7 +452,7 @@ class PaginatingControl extends Control
}
```
-The opposite process, that is, collecting values from persistent properites, is handled by the `saveState()` method.
+The opposite process, that is, collecting values from persistent properties, is handled by the `saveState()` method.
Signals in Depth
@@ -444,7 +466,7 @@ Signal can be received by any component, presenter of object which implements in
The main receivers of signals are `Presenters` and visual components extending `Control`. A signal is a sign for an object that it has to do something - poll counts in a vote from user, box with news has to unfold, form was sent and has to process data and so on.
-The URL for the signal is created using the method [Component::link() |api:Nette\Application\UI\Component::link()]. As parameter `$destination` we pass string `{signal}!` and as `$args` an array of arguments which we want to pass to the signal handler. Signal parameters are attached to the URL of the current presenter/view. **The parameter `?do` in the URL determines the signal called.**
+The URL for the signal is created using the [Component::link() |api:Nette\Application\UI\Component::link()] method. We pass the string `{signal}!` as the `$destination` parameter and the array of arguments we want to pass to the signal as `$args`. The signal is always called on the current presenter and action with the current parameters, the signal parameters are just added. In addition, the **parameter `?do`, which specifies the signal** is added right at the beginning.
Its format is `{signal}` or `{signalReceiver}-{signal}`. `{signalReceiver}` is the name of the component in the presenter. This is why hyphen (inaccurately dash) can't be present in the name of components - it is used to divide the name of the component and signal, but it's possible to compose several components.
diff --git a/application/en/configuration.texy b/application/en/configuration.texy
index 208194e78f..78ff08d5aa 100644
--- a/application/en/configuration.texy
+++ b/application/en/configuration.texy
@@ -14,10 +14,14 @@ application:
debugger: ... # (bool) defaults to true
# will error-presenter be called on error?
- catchExceptions: ... # (bool) defaults to true in production mode
+ # has effect only in developer mode
+ catchExceptions: ... # (bool) defaults to true
# name of error-presenter
- errorPresenter: Error # (string) defaults to 'Nette:Error'
+ errorPresenter: Error # (string|array) defaults to 'Nette:Error'
+
+ # defines aliases for presenters and events
+ aliases: ...
# defines the rules for resolving the presenter name to a class
mapping: ...
@@ -27,11 +31,20 @@ application:
silentLinks: ... # (bool) defaults to false
```
-Because error-presenters are not called by default in development mode and the errors are displayed by Tracy, changing the value `catchExceptions` to `true` helps to verify that error-presenters works correct during development.
+As of `nette/application` version 3.2 it is possible to define a pair of error-presenters:
+
+```neon
+application:
+ errorPresenter:
+ 4xx: Error4xx # for Nette\Application\BadRequestException
+ 5xx: Error5xx # for other exceptions
+```
Option `silentLinks` determines how Nette behaves in developer mode when link generation fails (for example, because there is no presenter, etc). The default value `false` means that Nette triggers `E_USER_WARNING`. Setting to `true` suppresses this error message. In a production environment, `E_USER_WARNING` is always invoked. We can also influence this behavior by setting the presenter variable [$invalidLinkMode |creating-links#Invalid Links].
-The [mapping defines the rules |modules#mapping] by which the class name is derived from the presenter name.
+[Aliases simplify referencing |creating-links#aliases] frequently used presenters.
+
+The [mapping defines the rules |directory-structure#Presenter Mapping] by which the class name is derived from the presenter name.
Automatic Registration of Presenters
@@ -82,6 +95,9 @@ latte:
# enables [checking generated code |latte:develop#Checking Generated Code]
phpLinter: ... # (string) default is null
+ # sets the locale
+ locale: cs_CZ # (string) default is null
+
# class of $this->template
templateClass: App\MyTemplateClass # defaults to Nette\Bridges\ApplicationLatte\DefaultTemplate
```
@@ -91,7 +107,7 @@ If you are using Latte version 3, you can add new [extension |latte:creating-ext
```neon
latte:
extensions:
- - Latte\Essential\TranslatorExtension
+ - Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)
```
If you are using Latte version 2, you can register new tags either by entering the class name or by referring to the service. Method `install()` is called by default, but this can be changed by specifying the name of another method:
diff --git a/application/en/creating-links.texy b/application/en/creating-links.texy
index 0fb783c609..437ad53f5b 100644
--- a/application/en/creating-links.texy
+++ b/application/en/creating-links.texy
@@ -38,7 +38,7 @@ It is also possible to pass named parameters. The following link passes paramete
detail
```
-If method `ProductPresenter::renderShow()` does not have `$lang` in its signature, it can read the value of the parameter using `$lang = $this->getParameter('lang')`.
+If the method `ProductPresenter::renderShow()` does not have `$lang` in its signature, it can retrieve the value of the parameter using `$lang = $this->getParameter('lang')` or from the [property |presenters#Request Parameters].
If the parameters are stored in an array, they can be expanded with the `...` operator (or `(expand)` operator in Latte 2.x):
@@ -103,7 +103,7 @@ If the action is `default`, we can omit it, but the colon must remain:
home
```
-Links may also point to other [modules]. Here, the links are distinguished into relative to the submodules, or absolute. The principle is analogous to disk paths, only instead of slashes there are colons. Let's assume that the actual presenter is part of module `Front`, then we will write:
+Links may also point to other [modules|directory-structure#Presenters and Templates]. Here, the links are distinguished into relative to the submodules, or absolute. The principle is analogous to disk paths, only instead of slashes there are colons. Let's assume that the actual presenter is part of module `Front`, then we will write:
```latte
link to Front:Shop:Product:show
@@ -140,7 +140,7 @@ The target `this` will create a link to the current page:
refresh
```
-At the same time, all parameters specified in the signature of the `render
()` or `action()` method are transferred. So if we are on the `Product:show` and `id:123` pages, the link to `this` will also pass this parameter.
+At the same time, all parameters specified in the signature of the `action()` or `render()` method, if the `action()` is not defined, are transferred. So if we are on the `Product:show` and `id:123` pages, the link to `this` will also pass this parameter.
Of course, it is possible to specify the parameters directly:
@@ -213,7 +213,7 @@ Because [components] are separate reusable units that should have no relations t
If we want to link to presenters in the component template, we use the tag `{plink}`:
```latte
-home
+home
```
or in the code
@@ -223,6 +223,30 @@ $this->getPresenter()->link('Home:default')
```
+Aliases .{data-version:v3.2.2}
+==============================
+
+Sometimes it's useful to assign an easily memorable alias to a Presenter:action pair. For example, you could name the homepage `Front:Home:default` simply as `home` or `Admin:Dashboard:default` as `admin`.
+
+Aliases are defined in the [configuration|configuration] under the key `application › aliases`:
+
+```neon
+application:
+ aliases:
+ home: Front:Home:default
+ admin: Admin:Dashboard:default
+ sign: Front:Sign:in
+```
+
+In links, they are written using the at symbol, for example:
+
+```latte
+administration
+```
+
+They are supported in all methods that work with links, such as `redirect()` and similar.
+
+
Invalid Links
=============
@@ -257,6 +281,6 @@ How to create links with the method `link()` comfort, but without the presence o
LinkGenerator is a service that you can have passed through the constructor and then create links using its method `link()`.
-There is a difference compared to presenters. LinkGenerator creates all links as absolute URLs. Furthermore, there is no "current presenter", so it is not possible to specify only the name of the action `link('default')` or the relative paths to the [modules].
+Compared to presenters, there's a difference. LinkGenerator creates all links directly as absolute URLs. Also, there is no "actual presenter", so you can't just list the action name `link('default')` as the target or list relative paths to modules.
Invalid links always throw `Nette\Application\UI\InvalidLinkException`.
diff --git a/application/en/directory-structure.texy b/application/en/directory-structure.texy
new file mode 100644
index 0000000000..553a288c8b
--- /dev/null
+++ b/application/en/directory-structure.texy
@@ -0,0 +1,526 @@
+Directory Structure of the Application
+**************************************
+
+
+
+How to design a clear and scalable directory structure for projects in Nette Framework? We'll show you proven practices that will help you organize your code. You'll learn:
+
+- how to **logically structure** the application into directories
+- how to design the structure to **scale well** as the project grows
+- what are the **possible alternatives** and their advantages or disadvantages
+
+
+
+
+It's important to mention that Nette Framework itself doesn't insist on any specific structure. It's designed to be easily adaptable to any needs and preferences.
+
+
+Basic Project Structure
+=======================
+
+Although Nette Framework doesn't dictate any fixed directory structure, there is a proven default arrangement in the form of [Web Project|https://github.com/nette/web-project]:
+
+/--pre
+web-project/
+├── app/ ← application directory
+├── assets/ ← SCSS, JS files, images..., alternatively resources/
+├── bin/ ← command line scripts
+├── config/ ← configuration
+├── log/ ← logged errors
+├── temp/ ← temporary files, cache
+├── tests/ ← tests
+├── vendor/ ← libraries installed by Composer
+└── www/ ← public directory (document-root)
+\--
+
+You can freely modify this structure according to your needs - rename or move folders. Then you just need to adjust the relative paths to directories in `Bootstrap.php` and possibly `composer.json`. Nothing else is needed, no complex reconfiguration, no constant changes. Nette has smart autodetection and automatically recognizes the application location including its URL base.
+
+
+Code Organization Principles
+============================
+
+When you first explore a new project, you should be able to quickly orient yourself. Imagine clicking on the `app/Model/` directory and seeing this structure:
+
+/--pre
+app/Model/
+├── Services/
+├── Repositories/
+└── Entities/
+\--
+
+From this, you'll only learn that the project uses some services, repositories and entities. You won't learn anything about the actual purpose of the application.
+
+Let's look at a different approach - **organization by domains**:
+
+/--pre
+app/Model/
+├── Cart/
+├── Payment/
+├── Order/
+└── Product/
+\--
+
+This is different - at first glance it's clear that this is an e-commerce site. The directory names themselves reveal what the application can do - it works with payments, orders and products.
+
+The first approach (organization by class type) brings several problems in practice: code that is logically related is scattered across different folders and you have to jump between them. Therefore, we will organize by domains.
+
+
+Namespaces
+----------
+
+It is conventional that the directory structure corresponds to namespaces in the application. This means that the physical location of files corresponds to their namespace. For example, a class located in `app/Model/Product/ProductRepository.php` should have namespace `App\Model\Product`. This principle helps in code orientation and simplifies autoloading.
+
+
+Singular vs Plural in Names
+---------------------------
+
+Notice that we use singular for main application directories: `app`, `config`, `log`, `temp`, `www`. The same applies inside the application: `Model`, `Core`, `Presentation`. This is because each represents one unified concept.
+
+Similarly, `app/Model/Product` represents everything about products. We don't call it `Products` because it's not a folder full of products (that would contain files like `iphone.php`, `samsung.php`). It's a namespace containing classes for working with products - `ProductRepository.php`, `ProductService.php`.
+
+The folder `app/Tasks` is plural because it contains a set of standalone executable scripts - `CleanupTask.php`, `ImportTask.php`. Each of them is an independent unit.
+
+For consistency, we recommend using:
+- Singular for namespaces representing a functional unit (even if working with multiple entities)
+- Plural for collections of independent units
+- In case of uncertainty or if you don't want to think about it, choose singular
+
+
+Public Directory `www/`
+=======================
+
+This directory is the only one accessible from the web (so-called document-root). You may often encounter the name `public/` instead of `www/` - it's just a matter of convention and doesn't affect the functionality. The directory contains:
+- Application [entry point |bootstrap#index.php] `index.php`
+- `.htaccess` file with mod_rewrite rules (for Apache)
+- Static files (CSS, JavaScript, images)
+- Uploaded files
+
+For proper application security, it's crucial to have correctly [configured document-root |nette:troubleshooting#how-to-change-or-remove-www-directory-from-url].
+
+.[note]
+Never place the `node_modules/` folder in this directory - it contains thousands of files that may be executable and shouldn't be publicly accessible.
+
+
+Application Directory `app/`
+============================
+
+This is the main directory with application code. Basic structure:
+
+/--pre
+app/
+├── Core/ ← infrastructure matters
+├── Model/ ← business logic
+├── Presentation/ ← presenters and templates
+├── Tasks/ ← command scripts
+└── Bootstrap.php ← application bootstrap class
+\--
+
+`Bootstrap.php` is the [application startup class|bootstrap] that initializes the environment, loads configuration and creates the DI container.
+
+Let's now look at individual subdirectories in detail.
+
+
+Presenters and Templates
+========================
+
+We have the presentation part of the application in the `app/Presentation` directory. An alternative is the short `app/UI`. This is the place for all presenters, their templates and any helper classes.
+
+We organize this layer by domains. In a complex project that combines e-commerce, blog and API, the structure would look like this:
+
+/--pre
+app/Presentation/
+├── Shop/ ← e-commerce frontend
+│ ├── Product/
+│ ├── Cart/
+│ └── Order/
+├── Blog/ ← blog
+│ ├── Home/
+│ └── Post/
+├── Admin/ ← administration
+│ ├── Dashboard/
+│ └── Products/
+└── Api/ ← API endpoints
+ └── V1/
+\--
+
+Conversely, for a simple blog we would use this structure:
+
+/--pre
+app/Presentation/
+├── Front/ ← website frontend
+│ ├── Home/
+│ └── Post/
+├── Admin/ ← administration
+│ ├── Dashboard/
+│ └── Posts/
+├── Error/
+└── Export/ ← RSS, sitemaps etc.
+\--
+
+Folders like `Home/` or `Dashboard/` contain presenters and templates. Folders like `Front/`, `Admin/` or `Api/` are called **modules**. Technically, these are regular directories that serve for logical organization of the application.
+
+Each folder with a presenter contains a similarly named presenter and its templates. For example, the `Dashboard/` folder contains:
+
+/--pre
+Dashboard/
+├── DashboardPresenter.php ← presenter
+└── default.latte ← template
+\--
+
+This directory structure is reflected in class namespaces. For example, `DashboardPresenter` is in the namespace `App\Presentation\Admin\Dashboard` (see [#presenter mapping]):
+
+```php
+namespace App\Presentation\Admin\Dashboard;
+
+class DashboardPresenter extends Nette\Application\UI\Presenter
+{
+ // ...
+}
+```
+
+We refer to the `Dashboard` presenter inside the `Admin` module in the application using colon notation as `Admin:Dashboard`. To its `default` action then as `Admin:Dashboard:default`. For nested modules, we use more colons, for example `Shop:Order:Detail:default`.
+
+
+Flexible Structure Development
+------------------------------
+
+One of the great advantages of this structure is how elegantly it adapts to growing project needs. As an example, let's take the part generating XML feeds. Initially, we have a simple form:
+
+/--pre
+Export/
+├── ExportPresenter.php ← one presenter for all exports
+├── sitemap.latte ← template for sitemap
+└── feed.latte ← template for RSS feed
+\--
+
+Over time, more feed types are added and we need more logic for them... No problem! The `Export/` folder simply becomes a module:
+
+/--pre
+Export/
+├── Sitemap/
+│ ├── SitemapPresenter.php
+│ └── sitemap.latte
+└── Feed/
+ ├── FeedPresenter.php
+ ├── amazon.latte ← feed for Amazon
+ └── ebay.latte ← feed for eBay
+\--
+
+This transformation is completely smooth - just create new subfolders, divide the code into them and update links (e.g. from `Export:feed` to `Export:Feed:amazon`). Thanks to this, we can gradually expand the structure as needed, the nesting level is not limited in any way.
+
+For example, if in the administration you have many presenters related to order management, such as `OrderDetail`, `OrderEdit`, `OrderDispatch` etc., you can create a module (folder) `Order` for better organization, which will contain (folders for) presenters `Detail`, `Edit`, `Dispatch` and others.
+
+
+Template Location
+-----------------
+
+In previous examples, we saw that templates are located directly in the folder with the presenter:
+
+/--pre
+Dashboard/
+├── DashboardPresenter.php ← presenter
+├── DashboardTemplate.php ← optional template class
+└── default.latte ← template
+\--
+
+This location proves to be the most convenient in practice - you have all related files right at hand.
+
+Alternatively, you can place templates in a `templates/` subfolder. Nette supports both variants. You can even place templates completely outside the `Presentation/` folder. Everything about template location options can be found in the [Template Lookup|templates#Template Lookup] chapter.
+
+
+Helper Classes and Components
+-----------------------------
+
+Presenters and templates often come with other helper files. We place them logically according to their scope:
+
+1. **Directly with the presenter** in case of specific components for the given presenter:
+
+/--pre
+Product/
+├── ProductPresenter.php
+├── ProductGrid.php ← component for product listing
+└── FilterForm.php ← form for filtering
+\--
+
+2. **For module** - we recommend using the `Accessory` folder, which is placed neatly at the beginning of the alphabet:
+
+/--pre
+Front/
+├── Accessory/
+│ ├── NavbarControl.php ← components for frontend
+│ └── TemplateFilters.php
+├── Product/
+└── Cart/
+\--
+
+3. **For entire application** - in `Presentation/Accessory/`:
+/--pre
+app/Presentation/
+├── Accessory/
+│ ├── LatteExtension.php
+│ └── TemplateFilters.php
+├── Front/
+└── Admin/
+\--
+
+Or you can place helper classes like `LatteExtension.php` or `TemplateFilters.php` in the infrastructure folder `app/Core/Latte/`. And components in `app/Components`. The choice depends on team conventions.
+
+
+Model - Heart of the Application
+================================
+
+The model contains all business logic of the application. For its organization, the same rule applies - we structure by domains:
+
+/--pre
+app/Model/
+├── Payment/ ← everything about payments
+│ ├── PaymentFacade.php ← main entry point
+│ ├── PaymentRepository.php
+│ ├── Payment.php ← entity
+├── Order/ ← everything about orders
+│ ├── OrderFacade.php
+│ ├── OrderRepository.php
+│ ├── Order.php
+└── Shipping/ ← everything about shipping
+\--
+
+In the model, you typically encounter these types of classes:
+
+**Facades**: represent the main entry point into a specific domain in the application. They act as an orchestrator that coordinates cooperation between different services to implement complete use-cases (like "create order" or "process payment"). Under their orchestration layer, the facade hides implementation details from the rest of the application, thus providing a clean interface for working with the given domain.
+
+```php
+class OrderFacade
+{
+ public function createOrder(Cart $cart): Order
+ {
+ // validation
+ // order creation
+ // email sending
+ // writing to statistics
+ }
+}
+```
+
+**Services**: focus on specific business operations within a domain. Unlike facades that orchestrate entire use-cases, a service implements specific business logic (like price calculations or payment processing). Services are typically stateless and can be used either by facades as building blocks for more complex operations, or directly by other parts of the application for simpler tasks.
+
+```php
+class PricingService
+{
+ public function calculateTotal(Order $order): Money
+ {
+ // price calculation
+ }
+}
+```
+
+**Repositories**: handle all communication with the data storage, typically a database. Their task is to load and save entities and implement methods for searching them. A repository shields the rest of the application from database implementation details and provides an object-oriented interface for working with data.
+
+```php
+class OrderRepository
+{
+ public function find(int $id): ?Order
+ {
+ }
+
+ public function findByCustomer(int $customerId): array
+ {
+ }
+}
+```
+
+**Entities**: objects representing main business concepts in the application that have their identity and change over time. Typically these are classes mapped to database tables using ORM (like Nette Database Explorer or Doctrine). Entities can contain business rules concerning their data and validation logic.
+
+```php
+// Entity mapped to database table orders
+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,
+ ]);
+ }
+}
+```
+
+**Value objects**: immutable objects representing values without their own identity - for example, a money amount or email address. Two instances of a value object with the same values are considered identical.
+
+
+Infrastructure Code
+===================
+
+The `Core/` folder (or also `Infrastructure/`) is home to the technical foundation of the application. Infrastructure code typically includes:
+
+/--pre
+app/Core/
+├── Router/ ← routing and URL management
+│ └── RouterFactory.php
+├── Security/ ← authentication and authorization
+│ ├── Authenticator.php
+│ └── Authorizator.php
+├── Logging/ ← logging and monitoring
+│ ├── SentryLogger.php
+│ └── FileLogger.php
+├── Cache/ ← caching layer
+│ └── FullPageCache.php
+└── Integration/ ← integration with ext. services
+ ├── Slack/
+ └── Stripe/
+\--
+
+For smaller projects, a flat structure is naturally sufficient:
+
+/--pre
+Core/
+├── RouterFactory.php
+├── Authenticator.php
+└── QueueMailer.php
+\--
+
+This is code that:
+
+- Handles technical infrastructure (routing, logging, caching)
+- Integrates external services (Sentry, Elasticsearch, Redis)
+- Provides basic services for the entire application (mail, database)
+- Is mostly independent of specific domain - cache or logger works the same for e-commerce or blog.
+
+Wondering if a certain class belongs here or in the model? The key difference is that code in `Core/`:
+
+- Knows nothing about the domain (products, orders, articles)
+- Can usually be transferred to another project
+- Solves "how it works" (how to send mail), not "what it does" (what mail to send)
+
+Example for better understanding:
+
+- `App\Core\MailerFactory` - creates instances of email sending class, handles SMTP settings
+- `App\Model\OrderMailer` - uses `MailerFactory` to send emails about orders, knows their templates and when they should be sent
+
+
+Command Scripts
+===============
+
+Applications often need to perform tasks outside of regular HTTP requests - whether it's background data processing, maintenance, or periodic tasks. Simple scripts in the `bin/` directory are used for execution, while the actual implementation logic is placed in `app/Tasks/` (or `app/Commands/`).
+
+Example:
+
+/--pre
+app/Tasks/
+├── Maintenance/ ← maintenance scripts
+│ ├── CleanupCommand.php ← deleting old data
+│ └── DbOptimizeCommand.php ← database optimization
+├── Integration/ ← integration with external systems
+│ ├── ImportProducts.php ← import from supplier system
+│ └── SyncOrders.php ← order synchronization
+└── Scheduled/ ← regular tasks
+ ├── NewsletterCommand.php ← sending newsletters
+ └── ReminderCommand.php ← customer notifications
+\--
+
+What belongs in the model and what in command scripts? For example, logic for sending one email is part of the model, bulk sending of thousands of emails belongs in `Tasks/`.
+
+Tasks are usually [run from command line |https://blog.nette.org/en/cli-scripts-in-nette-application] or via cron. They can also be run via HTTP request, but security must be considered. The presenter that runs the task needs to be secured, for example only for logged-in users or with a strong token and access from allowed IP addresses. For long tasks, it's necessary to increase the script time limit and use `session_write_close()` to avoid locking the session.
+
+
+Other Possible Directories
+==========================
+
+In addition to the mentioned basic directories, you can add other specialized folders according to project needs. Let's look at the most common ones and their use:
+
+/--pre
+app/
+├── Api/ ← API logic independent of presentation layer
+├── Database/ ← migration scripts and seeders for test data
+├── Components/ ← shared visual components across the application
+├── Event/ ← useful if using event-driven architecture
+├── Mail/ ← email templates and related logic
+└── Utils/ ← helper classes
+\--
+
+For shared visual components used in presenters across the application, you can use the `app/Components` or `app/Controls` folder:
+
+/--pre
+app/Components/
+├── Form/ ← shared form components
+│ ├── SignInForm.php
+│ └── UserForm.php
+├── Grid/ ← components for data listings
+│ └── DataGrid.php
+└── Navigation/ ← navigation elements
+ ├── Breadcrumbs.php
+ └── Menu.php
+\--
+
+This is where components with more complex logic belong. If you want to share components between multiple projects, it's good to separate them into a standalone composer package.
+
+In the `app/Mail` directory you can place email communication management:
+
+/--pre
+app/Mail/
+├── templates/ ← email templates
+│ ├── order-confirmation.latte
+│ └── welcome.latte
+└── OrderMailer.php
+\--
+
+
+Presenter Mapping
+=================
+
+Mapping defines rules for deriving class names from presenter names. We specify them in the [configuration|configuration] under the key `application › mapping`.
+
+On this page, we've shown that we place presenters in the `app/Presentation` folder (or `app/UI`). We need to tell Nette about this convention in the configuration file. One line is enough:
+
+```neon
+application:
+ mapping: App\Presentation\*\**Presenter
+```
+
+How does mapping work? To better understand, let's first imagine an application without modules. We want presenter classes to fall under the `App\Presentation` namespace, so that the `Home` presenter maps to the `App\Presentation\HomePresenter` class. This is achieved with this configuration:
+
+```neon
+application:
+ mapping: App\Presentation\*Presenter
+```
+
+Mapping works by replacing the asterisk in the mask `App\Presentation\*Presenter` with the presenter name `Home`, resulting in the final class name `App\Presentation\HomePresenter`. Simple!
+
+However, as you see in examples in this and other chapters, we place presenter classes in eponymous subdirectories, for example the `Home` presenter maps to class `App\Presentation\Home\HomePresenter`. We achieve this by doubling the colon (requires Nette Application 3.2):
+
+```neon
+application:
+ mapping: App\Presentation\**Presenter
+```
+
+Now we'll move on to mapping presenters into modules. We can define specific mapping for each module:
+
+```neon
+application:
+ mapping:
+ Front: App\Presentation\Front\**Presenter
+ Admin: App\Presentation\Admin\**Presenter
+ Api: App\Api\*Presenter
+```
+
+According to this configuration, the presenter `Front:Home` maps to class `App\Presentation\Front\Home\HomePresenter`, while presenter `Api:OAuth` maps to class `App\Api\OAuthPresenter`.
+
+Because modules `Front` and `Admin` have a similar mapping method and there will probably be more such modules, it's possible to create a general rule that will replace them. A new asterisk for the module will be added to the class mask:
+
+```neon
+application:
+ mapping:
+ *: App\Presentation\*\**Presenter
+ Api: App\Api\*Presenter
+```
+
+It also works for deeper nested directory structures, such as presenter `Admin:User:Edit`, where the segment with asterisk repeats for each level and results in class `App\Presentation\Admin\User\Edit\EditPresenter`.
+
+An alternative notation is to use an array consisting of three segments instead of a string. This notation is equivalent to the previous one:
+
+```neon
+application:
+ mapping:
+ *: [App\Presentation, *, **Presenter]
+ Api: [App\Api, '', *Presenter]
+```
diff --git a/application/en/how-it-works.texy b/application/en/how-it-works.texy
index 08d7c3eb8b..57be02d32f 100644
--- a/application/en/how-it-works.texy
+++ b/application/en/how-it-works.texy
@@ -22,18 +22,18 @@ The directory structure looks something like this:
/--pre
web-project/
├── app/ ← directory with application
-│ ├── Presenters/ ← presenter classes
-│ │ ├── HomePresenter.php ← Home presenter class
-│ │ └── templates/ ← templates directory
-│ │ ├── @layout.latte ← template of shared layout
-│ │ └── Home/ ← templates for Home presenter
-│ │ └── default.latte ← template for action `default`
-│ ├── Router/ ← configuration of URL addresses
+│ ├── Core/ ← basic necessary classes
+│ │ └── RouterFactory.php ← configuration of URL addresses
+│ ├── Presentation/ ← presenters, templates & co.
+│ │ ├── @layout.latte ← template of shared layout
+│ │ └── Home/ ← Home presenter directory
+│ │ ├── HomePresenter.php ← Home presenter class
+│ │ └── default.latte ← template for action default
│ └── Bootstrap.php ← booting class Bootstrap
├── bin/ ← scripts for the command line
├── config/ ← configuration files
│ ├── common.neon
-│ └── local.neon
+│ └── services.neon
├── log/ ← error logs
├── temp/ ← temporary files, cache, …
├── vendor/ ← libraries installed by Composer
@@ -45,9 +45,9 @@ The directory structure looks something like this:
└── .htaccess ← prohibits access to all directories except www
\--
-You can change the directory structure in any way, rename or move folders, and then just edit the paths to `log/` and `temp/` in the `Bootstrap.php` file and the path to this file in `composer.json` in the `autoload` section. Nothing more, no complicated reconfiguration, no constant changes. Nette has a [smart autodetection|bootstrap#development-vs-production-mode].
+You can modify the directory structure however you like, rename or move folders - it's completely flexible. Nette also features smart autodetection and automatically recognizes the application location including its URL base.
-For slightly larger applications, we can divide folders with presenters and templates into subdirectories (on disk) and into namespaces (in code), which we call [modules].
+For slightly larger applications, we can organize presenter and template folders into [subdirectories |directory-structure#Presenters and templates] and group classes into namespaces, which we call modules.
The `www/` directory is the public directory or document-root of the project. You can rename it without having to set anything else on the application side. You just need to [configure the hosting |nette:troubleshooting#How to change or remove www directory from URL] so that the document-root goes to this directory.
@@ -75,7 +75,7 @@ Its task is:
What kind of factory? We do not produce tractors, but websites! Hold on, it'll be explained right away.
-By "initialize the environment" we mean, for example, that [Tracy |tracy:] is activated, which is an amazing tool for logging or visualizing errors. It logs errors on the production server and displays them directly on the development server. Therefore, initialization also needs to decide whether the site is running in production or developer mode. To do this, Nette uses autodetection: if you run the site on localhost, it runs in developer mode. You don't have to configure anything and the application is ready for both development and production deployment. These steps are performed and described in detail in the chapter about [Bootstrap class |bootstrap].
+By "environment initialization" we mean, for example, activating [Tracy|tracy:], which is a fantastic tool for logging and error visualization. On production servers it logs errors, while on development it displays them directly. Therefore, initialization includes determining whether the website runs in production or development mode. For this, Nette uses [smart autodetection|bootstrap#development-vs-production-mode]: if you run the site on localhost, it operates in development mode. No configuration is needed and the application is ready for both development and production deployment. These steps are performed and detailed in the [Bootstrap class|bootstrap] chapter.
The third point (yes, we skipped the second, but we will return to it) is to start the application. The handling of HTTP requests in Nette is done by the class `Nette\Application\Application` (hereinafter referred to as the `Application`), so when we say "run an application", we mean to call a method with the name `run()` on an object of this class.
@@ -91,7 +91,7 @@ Applications written in Nette are divided into many so-called presenters (in oth
The application starts by asking the so-called router to decide which of the presenters to pass the current request for processing. The router decides whose responsibility it is. It looks at the input URL `https://example.com/product/123` and, based on how it is set up, decides that this is a job, for example, for **presenter** `Product`, who wants to `show` a product with `id: 123` as an action. It is a good habit to write a pairs of presenter + action separated by a colon as `Product:show`.
-So the router transformed the URL into a pair `Presenter:action` + parameters, in our case `Product:show` + `id: 123`. You can see how a router looks like in file `app/Router/RouterFactory.php` and we will describe it in detail in chapter [Routing].
+So the router transformed the URL into a pair `Presenter:action` + parameters, in our case `Product:show` + `id: 123`. You can see how a router looks like in file `app/Core/RouterFactory.php` and we will describe it in detail in chapter [Routing].
Let's move on. The application already knows the name of the presenter and can continue. By creating an object `ProductPresenter`, which is the code of presenter `Product`. More precisely, it asks the DI container for creating the presenter, because producting objects is its job.
@@ -121,12 +121,9 @@ So, the method `renderShow(123)` was called, whose code is fictional example, bu
Subsequently, the presenter returns the answer. This can be an HTML page, an image, an XML document, sending a file from disk, JSON or redirecting to another page. Importantly, if we do not explicitly say how to respond (which is the case of `ProductPresenter`), the answer will be to render the template with an HTML page. Why? Well, because in 99% of cases we want to draw a template, so the presenter takes this behavior as the default and wants to make our work easier. That's Nette's point.
-We don't even have to state which template to draw, he derives the path to it according to simple logic. In the case of presenter `Product` and action `show`, it tries to see if one of these template files exists relative to the directory where class `ProductPresenter` is located:
+We don't even need to specify which template to render; the framework will deduce the path itself. In the case of the `show` action, it simply tries to load the `show.latte` template in the directory with the `ProductPresenter` class. It also attempts to find the layout in the `@layout.latte` file (more about [template searching |templates#Template Lookup]).
-- `templates/Product/show.latte`
-- `templates/Product.show.latte`
-
-It will also try to find the layout in file `@layout.latte` and then it renders the template. Now the task of the presenter and the entire application is completed. If the template does not exist, a page with error 404 will be returned. You can read more about presenters on the [Presenters] page.
+Subsequently, the templates are rendered. This completes the task of the presenter and the entire application, and the work is done. If the template did not exist, a 404 error page would be returned. You can read more about presenters on the page [Presenters|presenters].
[* request-flow.svg *]
@@ -137,7 +134,7 @@ Just to be sure, let's try to recap the whole process with a slightly different
3) the router decodes the URL as a pair `Home:default`
4) an `HomePresenter` object is created
5) method `renderDefault()` is called (if exists)
-6) a template `templates/Home/default.latte` with a layout `templates/@layout.latte` is rendered
+6) a template `default.latte` with a layout `@layout.latte` is rendered
You may have come across a lot of new concepts now, but we believe they make sense. Creating applications in Nette is a breeze.
diff --git a/application/en/modules.texy b/application/en/modules.texy
deleted file mode 100644
index 6312143fdd..0000000000
--- a/application/en/modules.texy
+++ /dev/null
@@ -1,148 +0,0 @@
-Modules
-*******
-
-.[perex]
-In Nette, modules represent the logical units that make up an application. They include presenters, templates, possibly also components and model classes.
-
-One directory for presenters and one for templates would not be enough for real projects. Having dozens of files in one folder is at least unorganized. How to get out of it? We simply split them into subdirectories on disk and into namespaces in the code. And that's exactly what the Nette modules do.
-
-So let's forget about a single folder for presenters and templates and instead create modules, for example `Admin` and `Front`.
-
-/--pre
-app/
-├── Presenters/
-├── Modules/ ← directory with modules
-│ ├── Admin/ ← module Admin
-│ │ ├── Presenters/ ← its presenters
-│ │ │ ├── DashboardPresenter.php
-│ │ │ └── templates/
-│ └── Front/ ← module Front
-│ └── Presenters/ ← its presenters
-│ └── ...
-\--
-
-This directory structure will be reflected by the class namespaces, so for example `DashboardPresenter` will be in the `App\Modules\Admin\Presenters` namespace:
-
-```php
-namespace App\Modules\Admin\Presenters;
-
-class DashboardPresenter extends Nette\Application\UI\Presenter
-{
- // ...
-}
-```
-
-The `Dashboard` presenter inside the `Admin` module is referenced within the application using the colon notation as `Admin:Dashboard`, and its `default` action as `Admin:Dashboard:default`.
-And how does Nette proper know that `Admin:Dashboard` represents the `App\Modules\Admin\Presenters\DashboardPresenter` class? This is determined by [#mapping] in the configuration.
-Thus, the given structure is not hard set and you can modify it according to your needs.
-
-Modules can of course contain all other items besides presenters and templates, such as components, model classes, etc.
-
-
-Nested Modules
---------------
-
-Modules don't have to form only a flat structure, you can also create submodules, for example:
-
-/--pre
-app/
-├── Modules/ ← directory with modules
-│ ├── Blog/ ← module Blog
-│ │ ├── Admin/ ← submodule Admin
-│ │ │ ├── Presenters/
-│ │ │ └── ...
-│ │ └── Front/ ← submodule Front
-│ │ ├── Presenters/
-│ │ └── ...
-│ ├── Forum/ ← module Forum
-│ │ └── ...
-\--
-
-Thus, the `Blog` module is divided into `Admin` and `Front` submodules. Again, this will be reflected in the namespaces, which will be `App\Modules\Blog\Admin\Presenters` etc. The presenter `Dashboard` inside the submodule is referred to as `Blog:Admin:Dashboard`.
-
-The nesting can go as deep as you like, so sub-submodules can be created.
-
-
-Creating Links
---------------
-
-Links in presenter templates are relative to the current module. Thus, the link `Foo:default` leads to the presenter `Foo` in the same module as the current presenter. If the current module is `Front`, for example, then the link goes like this:
-
-```latte
-link to Front:Product:show
-```
-
-A link is relative even if it includes the name of a module, which is then considered a submodule:
-
-```latte
-link to Front:Shop:Product:show
-```
-
-Absolute links are written analogously to absolute paths on disk, but with colons instead of slashes. Thus, an absolute link starts with a colon:
-
-```latte
-link to Admin:Product:show
-```
-
-To find out if we are in a certain module or its submodule we can use `isModuleCurrent(moduleName)` function.
-
-```latte
-
- ...
-
-```
-
-
-Routing
--------
-
-See [chapter on routing |routing#Modules].
-
-
-Mapping
--------
-
-Defines the rules by which the class name is derived from the presenter name. We write them in [configuration] under the `application › mapping` key.
-
-Let's start with a sample that doesn't use modules. We'll just want the presenter classes to have the `App\Presenters` namespace. That means that a presenter such as `Home` should map to the `App\Presenters\HomePresenter` class. This can be achieved by the following configuration:
-
-```neon
-application:
- mapping:
- *: App\Presenters\*Presenter
-```
-
-The presenter name is replaced with the asterisk in the class mask and the result is the class name. Easy!
-
-If we divide presenters into modules, we can have our own mapping for each module:
-
-```neon
-application:
- mapping:
- Front: App\Modules\Front\Presenters\*Presenter
- Admin: App\Modules\Admin\Presenters\*Presenter
- Api: App\Api\*Presenter
-```
-
-Now presenter `Front:Home` maps to class `App\Modules\Front\Presenters\HomePresenter` and presenter `Admin:Dashboard` to class `App\Modules\Admin\Presenters\DashboardPresenter`.
-
-It is more practical to create a general (star) rule to replace the first two. The extra asterisk will be added to the class mask just for the module:
-
-```neon
-application:
- mapping:
- *: App\Modules\*\Presenters\*Presenter
- Api: App\Api\*Presenter
-```
-
-But what if we use nested modules and have a presenter `Admin:User:Edit`? In this case, the segment with an asterisk representing the module for each level is simply repeated and the result is class `App\Modules\Admin\User\Presenters\EditPresenter`.
-
-An alternative notation is to use an array consisting of three segments instead of a string. This notation is equivalent to the previous one:
-
-```neon
-application:
- mapping:
- *: [App\Modules, *, Presenters\*Presenter]
-```
-
-The default value is `*: *Module\*Presenter`.
diff --git a/application/en/presenters.texy b/application/en/presenters.texy
index e653f0945c..2773f8486b 100644
--- a/application/en/presenters.texy
+++ b/application/en/presenters.texy
@@ -60,7 +60,7 @@ Similar to the method `render()`. While `render()` is intended to pr
It is important that `action()` is called before `render()`, so inside it we can possibly change the next course of life cycle, i.e. change the template that will be rendered and also the method `render()` that will be called, using `setView('otherView')`.
-The parameters from the request are passed to the method. It is possible and recommended to specify types for the parameters, e.g. `actionShow(int $id, string $slug = null)` - if parameter `id` is missing or if it is not an integer, the presenter returns [error 404|#Error 404 etc.] and terminates the operation.
+The parameters from the request are passed to the method. It is possible and recommended to specify types for the parameters, e.g. `actionShow(int $id, ?string $slug = null)` - if parameter `id` is missing or if it is not an integer, the presenter returns [error 404|#Error 404 etc.] and terminates the operation.
`handle(args...)` .{toc: handle()}
@@ -205,7 +205,7 @@ In the template, these messages are available in the variable `$flashes` as obje
Error 404 etc.
==============
-When we can't fulfill the request because for example the article we want to display does not exist in the database, we will throw out the 404 error using method `error(string $message = null, int $httpCode = 404)`, which represents HTTP error 404:
+When we can't fulfill the request because for example the article we want to display does not exist in the database, we will throw out the 404 error using method `error(?string $message = null, int $httpCode = 404)`, which represents HTTP error 404:
```php
public function renderShow(int $id): void
@@ -236,6 +236,32 @@ public function actionData(): void
```
+Request Parameters .{data-version:3.1.14}
+=========================================
+
+The presenter, as well as every component, obtains its parameters from the HTTP request. Their values can be retrieved using the `getParameter($name)` method or `getParameters()`. The values are strings or arrays of strings, essentially raw data obtained directly from the URL.
+
+For added convenience, we recommend making parameters accessible through properties. Simply annotate them with the `#[Parameter]` attribute:
+
+```php
+use Nette\Application\Attributes\Parameter; // this line is important
+
+class HomePresenter extends Nette\Application\UI\Presenter
+{
+ #[Parameter]
+ public string $theme; // must be public
+}
+```
+
+For properties, we suggest specifying the data type (e.g., `string`). Nette will then automatically cast the value based on it. Parameter values can be also [validated |#Validation of Parameters].
+
+When creating a link, you can directly set the value for the parameters:
+
+```latte
+click
+```
+
+
Persistent Parameters
=====================
@@ -257,7 +283,7 @@ class ProductPresenter extends Nette\Application\UI\Presenter
If `$this->lang` has a value such as `'en'`, then links created using `link()` or `n:href` will also contain the `lang=en` parameter. And when the link is clicked, it will again be `$this->lang = 'en'`.
-For properties, we recommend that you include the data type (e.g. `string`) and you can also include a default value. Parameter values can be [validated |#Validation of Persistent Parameters].
+For properties, we recommend that you include the data type (e.g. `string`) and you can also include a default value. Parameter values can be [validated |#Validation of Parameters].
Persistent parameters are passed between all actions of a given presenter by default. To pass them between multiple presenters, you need to define them either:
@@ -307,18 +333,12 @@ Going Deeper
What we have shown so far in this chapter will probably suffice. The following lines are intended for those who are interested in presenters in depth and want to know everything.
-Requirement and Parameters
---------------------------
+Validation of Parameters
+------------------------
-The request handled by the presenter is the [api:Nette\Application\Request] object and is returned by the presenter's method `getRequest()`. It includes an array of parameters and each of them belongs either to some of the components or directly to the presenter (which is actually also a component, albeit a special one). So Nette redistributes the parameters and passes between the individual components (and the presenter) by calling the method `loadState(array $params)`. The parameters can be obtained by the method `getParameters(): array`, individually using `getParameter($name)`. Parameter values are strings or arrays of strings, they are basically raw data obtained directly from a URL.
+The values of [#request parameters] and [#persistent parameters] received from URLs are written to properties by the `loadState()` method. It also checks if the data type specified in the property matches, otherwise it will respond with a 404 error and the page will not be displayed.
-
-Validation of Persistent Parameters
------------------------------------
-
-The values of [#persistent parameters] received from URLs are written to properties by the `loadState()` method. It also checks if the data type specified in the property matches, otherwise it will respond with a 404 error and the page will not be displayed.
-
-Never blindly trust persistent parameters, as they can easily be overwritten by the user in the URL. For example, this is how we check if `$this->lang` is among the supported languages. A good way to do this is to override the `loadState()` method mentioned above:
+Never blindly trust parameters, as they can easily be overwritten by the user in the URL. For example, this is how we check if `$this->lang` is among the supported languages. A good way to do this is to override the `loadState()` method mentioned above:
```php
class ProductPresenter extends Nette\Application\UI\Presenter
@@ -341,6 +361,8 @@ class ProductPresenter extends Nette\Application\UI\Presenter
Save and Restore the Request
----------------------------
+The request that the presenter handles is an object [api:Nette\Application\Request] and is returned by the presenter's method `getRequest()`.
+
You can save the current request to a session or restore it from the session and let the presenter execute it again. This is useful, for example, when a user fills out a form and its login expires. In order not to lose data, before redirecting to the sign-in page, we save the current request to the session using `$reqId = $this->storeRequest()`, which returns an identifier in the form of a short string and passes it as a parameter to the sign-in presenter.
After sign in, we call the method `$this->restoreRequest($reqId)`, which picks up the request from the session and forwards it to it. The method verifies that the request was created by the same user as now logged in is. If another user logs in or the key is invalid, it does nothing and the program continues.
@@ -362,7 +384,7 @@ Redirection does not occur with an AJAX or POST request because it would result
You can also invoke canonization manually using method `canonicalize()`, which, like method `link()`, receives the presenter, actions, and parameters as arguments. It creates a link and compares it to the current URL. If it is different, it redirects to the generated link.
```php
-public function actionShow(int $id, string $slug = null): void
+public function actionShow(int $id, ?string $slug = null): void
{
$realSlug = $this->facade->getSlugForId($id);
// redirects if $slug is different from $realSlug
@@ -425,6 +447,51 @@ $this->sendResponse(new Responses\CallbackResponse($callback));
```
+Access Restriction Using `#[Requires]` .{data-version:3.2.2}
+------------------------------------------------------------
+
+The `#[Requires]` attribute provides advanced options for restricting access to presenters and their methods. It can be used to specify HTTP methods, require AJAX requests, limit access to the same origin, and restrict access to forwarding only. The attribute can be applied to presenter classes as well as individual methods such as `action()`, `render()`, `handle()`, and `createComponent()`.
+
+You can specify these restrictions:
+- on HTTP methods: `#[Requires(methods: ['GET', 'POST'])]`
+- requiring an AJAX request: `#[Requires(ajax: true)]`
+- access only from the same origin: `#[Requires(sameOrigin: true)]`
+- access only via forwarding: `#[Requires(forward: true)]`
+- restrictions on specific actions: `#[Requires(actions: 'default')]`
+
+For details, see [How to use the Requires attribute |best-practices:attribute-requires].
+
+
+HTTP Method Check
+-----------------
+
+In Nette, presenters automatically verify the HTTP method of each incoming request primarily for security reasons. By default, the methods `GET`, `POST`, `HEAD`, `PUT`, `DELETE`, `PATCH` are allowed.
+
+If you want to enable additional methods such as `OPTIONS`, you can use the `#[Requires]` attribute (from Nette Application v3.2):
+
+```php
+#[Requires(methods: ['GET', 'POST', 'HEAD', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'])]
+class MyPresenter extends Nette\Application\UI\Presenter
+{
+}
+```
+
+In version 3.1, the verification is performed in `checkHttpMethod()`, which checks if the method specified in the request is included in the array `$presenter->allowedMethods`. Add a method like this:
+
+```php
+class MyPresenter extends Nette\Application\UI\Presenter
+{
+ protected function checkHttpMethod(): void
+ {
+ $this->allowedMethods[] = 'OPTIONS';
+ parent::checkHttpMethod();
+ }
+}
+```
+
+It's crucial to emphasize that if you enable the `OPTIONS` method, you must also properly handle it within your presenter. This method is often used as a so-called preflight request, which browsers automatically send before the actual request when it's necessary to determine if the request is allowed from the standpoint of CORS (Cross-Origin Resource Sharing) policy. If you allow this method but do not implement an appropriate response, it can lead to inconsistencies and potential security issues.
+
+
Further Reading
===============
diff --git a/application/en/routing.texy b/application/en/routing.texy
index c35c692adb..6c50739932 100644
--- a/application/en/routing.texy
+++ b/application/en/routing.texy
@@ -216,7 +216,7 @@ $router->addRoute('//www.%sld%.%tld%/%basePath%//addRoute('/[/]', [
@@ -225,7 +225,7 @@ $router->addRoute('/[/]', [
]);
```
-Or we can use this form, notice the rewriting of the validation regular expression:
+For a more detailed specification, an even more extended form can be used, where in addition to default values, other parameter properties can be set, such as a validation regular expression (see the `id` parameter):
```php
use Nette\Routing\Route;
@@ -243,7 +243,7 @@ $router->addRoute('/[/]', [
]);
```
-These more talkative formats are useful for adding other metadata.
+It is important to note that if the parameters defined in the array are not included in the path mask, their values cannot be changed, not even using query parameters specified after a question mark in the URL.
Filters and Translations
@@ -368,7 +368,7 @@ $router->addRoute('', function (string $lang) {
Modules
-------
-If we have more routes that belong to one [module |modules], we can use `withModule()` to group them:
+If we have more routes that belong to one [module |directory-structure#Presenters and Templates], we can use `withModule()` to group them:
```php
$router = new RouteList;
@@ -477,10 +477,10 @@ $router->addRoute('index.html \.html?|\.php|>', /* ... */);
Integration
===========
-In order to connect the our router into the application, we must tell the DI container about it. The easiest way is to prepare the factory that will build the router object and tell the container configuration to use it. So let's say we write a method for this purpose `App\Router\RouterFactory::createRouter()`:
+In order to connect the our router into the application, we must tell the DI container about it. The easiest way is to prepare the factory that will build the router object and tell the container configuration to use it. So let's say we write a method for this purpose `App\Core\RouterFactory::createRouter()`:
```php
-namespace App\Router;
+namespace App\Core;
use Nette\Application\Routers\RouteList;
@@ -499,7 +499,7 @@ Then we write in [configuration |dependency-injection:services]:
```neon
services:
- - App\Router\RouterFactory::createRouter
+ - App\Core\RouterFactory::createRouter
```
Any dependencies, such as a database connection etc., are passed to the factory method as its parameters using [autowiring |dependency-injection:autowiring]:
@@ -663,7 +663,7 @@ By separated usage, we mean the use of the router's capabilities in an applicati
So again we will create a method that will build a router, for example:
```php
-namespace App\Router;
+namespace App\Core;
use Nette\Routing\RouteList;
@@ -694,7 +694,7 @@ $httpRequest = $container->getByType(Nette\Http\IRequest::class);
Or we will create objects directly:
```php
-$router = App\Router\RouterFactory::createRouter();
+$router = App\Core\RouterFactory::createRouter();
$httpRequest = (new Nette\Http\RequestFactory)->fromGlobals();
```
diff --git a/application/en/templates.texy b/application/en/templates.texy
index 5107b4493a..40a4163344 100644
--- a/application/en/templates.texy
+++ b/application/en/templates.texy
@@ -34,35 +34,81 @@ And this might be the action template:
It defines block `content`, which is inserted in place of `{include content}` in the layout, and also re-defines block `title`, which overwrites `{block title}` in the layout. Try to imagine the result.
-Search for Templates
---------------------
+Template Lookup
+---------------
-The path to the templates is deduced according to simple logic. It tries to see if one of these template files exists relative to the directory where presenter class is located, where `` is the name of the current presenter and `` is the name of the current action:
+In presenters, you don't need to specify which template should be rendered; the framework will automatically determine the path, making coding easier for you.
-- `templates//.latte`
-- `templates/..latte`
+If you use a directory structure where each presenter has its own directory, simply place the template in this directory under the name of the action (i.e. view). For example, for the `default` action, use the `default.latte` template:
-If the template is not found, it will try to search in the `templates` directory one level up, i.e., at the same level as the directory with the presenter class.
+/--pre
+app/
+└── Presentation/
+ └── Home/
+ ├── HomePresenter.php
+ └── default.latte
+\--
-If the template is not found there either, the response is a [404 error|presenters#Error 404 etc.].
+If you use a structure where presenters are together in one directory and templates in a `templates` folder, save it either in a file `..latte` or `/.latte`:
-You can also change the view using `$this->setView('otherView')`. Or, instead of searching, directly specify the name of the template file using `$this->template->setFile('/path/to/template.latte')`.
+/--pre
+app/
+└── Presenters/
+ ├── HomePresenter.php
+ └── templates/
+ ├── Home.default.latte ← 1st variant
+ └── Home/
+ └── default.latte ← 2nd variant
+\--
+
+The `templates` directory can also be placed one level higher, at the same level as the directory with presenter classes.
+
+If the template is not found, the presenter responds with [404 - page not found error|presenters#Error 404 etc].
+
+You can change the view using `$this->setView('anotherView')`. It is also possible to directly specify the template file with `$this->template->setFile('/path/to/template.latte')`.
.[note]
-You can change the paths where templates are searched by overriding the [formatTemplateFiles |api:Nette\Application\UI\Presenter::formatTemplateFiles()] method, which returns an array of possible file paths.
+Files where templates are searched can be changed by overriding the method [formatTemplateFiles() |api:Nette\Application\UI\Presenter::formatTemplateFiles()], which returns an array of possible file names.
+
+
+Layout Template Lookup
+----------------------
+
+Nette also automatically searches for the layout file.
+
+If you use a directory structure where each presenter has its own directory, place the layout either in the folder with the presenter, if it is specific only to them, or a level higher if it is common to multiple presenters:
+
+/--pre
+app/
+└── Presentation/
+ ├── @layout.latte ← common layout
+ └── Home/
+ ├── @layout.latte ← only for Home presenter
+ ├── HomePresenter.php
+ └── default.latte
+\--
+
+If you use a structure where presenters are grouped together in one directory and templates are in a `templates` folder, the layout will be expected in the following places:
-The layout is expected in the following files:
+/--pre
+app/
+└── Presenters/
+ ├── HomePresenter.php
+ └── templates/
+ ├── @layout.latte ← common layout
+ ├── Home.@layout.latte ← only for Home, 1st variant
+ └── Home/
+ └── @layout.latte ← only for Home, 2nd variant
+\--
-- `templates//@.latte`
-- `templates/.@.latte`
-- `templates/@.latte` layout common to multiple presenters
+If the presenter is in a module, it will also search further up the directory tree according to the module's nesting.
-`` is the name of the current presenter and `` is the name of the layout, which is by default `'layout'`. The name can be changed with `$this->setLayout('otherLayout')`, so that `@otherLayout.latte` files will be tried.
+The name of the layout can be changed using `$this->setLayout('layoutAdmin')` and then it will be expected in the file `@layoutAdmin.latte`. You can also directly specify the layout template file using `$this->setLayout('/path/to/template.latte')`.
-You can also directly specify the file name of the layout template using `$this->setLayout('/path/to/template.latte')`. Using `$this->setLayout(false)` will disable the layout searching.
+Using `$this->setLayout(false)` or the `{layout none}` tag inside the template disables layout search.
.[note]
-You can change the paths where templates are searched by overriding the [formatLayoutTemplateFiles |api:Nette\Application\UI\Presenter::formatLayoutTemplateFiles()] method, which returns an array of possible file paths.
+Files where layout templates are searched can be changed by overriding the method [formatLayoutTemplateFiles() |api:Nette\Application\UI\Presenter::formatLayoutTemplateFiles()], which returns an array of possible file names.
Variables in the Template
@@ -104,7 +150,7 @@ The `@property-read` annotation is for IDE and static analysis, it will make aut
You can indulge in the luxury of whispering in templates too, just install the Latte plugin in PhpStorm and specify the class name at the beginning of the template, see the article "Latte: how to type system":https://blog.nette.org/en/latte-how-to-use-type-system:
```latte
-{templateType App\Presenters\ArticleTemplate}
+{templateType App\Presentation\Article\ArticleTemplate}
...
```
@@ -176,7 +222,7 @@ public function beforeRender(): void
Latte version 3 offers a more advanced way by creating an [extension |latte:creating-extension] for each web project. Here is a rough example of such a class:
```php
-namespace App\Templating;
+namespace App\Presentation\Accessory;
final class LatteExtension extends Latte\Extension
{
@@ -214,7 +260,7 @@ We register it using [configuration#Latte]:
```neon
latte:
extensions:
- - App\Templating\LatteExtension
+ - App\Presentation\Accessory\LatteExtension
```
@@ -239,7 +285,7 @@ Alternatively, the translator can be set using the [configuration |configuration
```neon
latte:
extensions:
- - Latte\Essential\TranslatorExtension
+ - Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)
```
The translator can then be used, for example, as a filter `|translate`, with additional parameters passed to the `translate()` method (see `foo, bar`):
diff --git a/application/es/@home.texy b/application/es/@home.texy
index b2966f5062..708c39c397 100644
--- a/application/es/@home.texy
+++ b/application/es/@home.texy
@@ -2,35 +2,84 @@ Aplicación Nette
****************
.[perex]
-El paquete `nette/application` es la base para crear aplicaciones web interactivas.
+Nette Application es el núcleo del framework Nette que aporta potentes herramientas para crear aplicaciones web modernas. Ofrece numerosas características excepcionales que simplifican significativamente el desarrollo y mejoran la seguridad y la mantenibilidad del código.
-- [¿Cómo funcionan las aplicaciones? |how-it-works]
-- [Bootstrap]
-- [Presentadores |Presenters]
-- [Plantillas |Templates]
-- [Módulos |Modules]
-- [Enrutamiento |Routing]
-- [Creación de enlaces URL |creating-links]
-- [Componentes interactivos |components]
-- [AJAX y fragmentos |ajax]
-- [Multiplicador |multiplier]
-- [Configuración |Configuration]
+Instalación .[#toc-installation]
+--------------------------------
-Instalación
------------
-
-Descargue e instale el paquete utilizando [Composer |best-practices:composer]:
+Descargue e instale la biblioteca utilizando [Composer |best-practices:composer]:
```shell
composer require nette/application
```
+
+¿Por qué elegir Nette Application? .[#toc-why-choose-nette-application]
+-----------------------------------------------------------------------
+
+Nette siempre ha sido pionera en tecnologías web.
+
+**Enrutador bidireccional:** Nette cuenta con un avanzado sistema de enrutamiento único en su bidireccionalidad - no sólo traduce URLs a acciones de la aplicación, sino que también puede generar URLs a la inversa. Esto significa que:
+- Puede modificar la estructura de URL de toda la aplicación en cualquier momento sin modificar los archivos de plantilla.
+- Las URL se canonizan automáticamente, lo que mejora el SEO.
+- El enrutamiento se define en un solo lugar, no disperso en anotaciones
+
+**Componentes y señales:** El sistema de componentes integrado inspirado en Delphi y React.js es único entre los frameworks PHP:
+- Permite crear elementos de interfaz de usuario reutilizables
+- Soporta la composición jerárquica de componentes
+- Ofrece un elegante manejo de peticiones AJAX mediante señales
+- Amplia biblioteca de componentes listos para usar en [Componette](https://componette.org).
+
+**AJAX y Snippets:** Nette introdujo una forma revolucionaria de trabajar con AJAX en 2009, antes que soluciones como Hotwire para Ruby on Rails o Symfony UX Turbo:
+- Los snippets permiten actualizar sólo partes de la página sin escribir JavaScript
+- Integración automática con el sistema de componentes
+- Invalidación inteligente de secciones de la página
+- Transferencia mínima de datos
+
+**Plantillas Intuitivas [Latte |latte:]:** El sistema de plantillas más seguro para PHP con características avanzadas:
+- Protección XSS automática con escape sensible al contexto
+- Extensible con filtros personalizados, funciones y etiquetas
+- Herencia de plantillas y fragmentos para AJAX
+- Excelente soporte PHP 8.x con sistema de tipos
+
+**Inyección de dependencias:** Nette utiliza completamente la inyección de dependencias:
+- Paso automático de dependencias (autowiring)
+- Configuración utilizando el formato NEON
+- Soporte para fábricas de componentes
+
+
+Principales ventajas .[#toc-main-benefits]
+------------------------------------------
+
+- Seguridad**: Protección automática contra [vulnerabilidades |nette:vulnerability-protection] como XSS, CSRF, etc.
+- Productividad**: Menos escritura, más funcionalidades gracias a un diseño inteligente
+- Depuración**: [Depurador de Tracy |tracy:] con panel de enrutamiento
+- Rendimiento**: Sistema de caché inteligente, carga perezosa de componentes.
+- Flexibilidad**: Fácil modificación de la URL incluso después de finalizar la aplicación
+- Componentes**: Sistema único de elementos de interfaz de usuario reutilizables
+- Moderno**: Soporte completo para PHP 8.4+ y sistema de tipos
+
+
+Primeros pasos .[#toc-getting-started]
+--------------------------------------
+
+1. Comprender [las aplicaciones |how-it-works] - Comprender la arquitectura básica
+2. [Presentadores |presenters] - Trabajar con presentadores y acciones
+3. [Plantillas |templates] - Creación de plantillas en Latte
+4. [Enrutamiento |routing] - Configuración de URL
+5. [Componentes interactivos |components] - Uso del sistema de componentes
+
+
+Compatibilidad con PHP .[#toc-php-compatibility]
+------------------------------------------------
+
| versión | compatible con PHP
|-----------|-------------------
-| Aplicación Nette 4.0 | PHP 8.0 - 8.2
-| Aplicación Nette 3.1 | PHP 7.2 - 8.2
+| Aplicación Nette 4.0 | PHP 8.1 - 8.4
+| Aplicación Nette 3.2 | PHP 8.1 - 8.4
+| Aplicación Nette 3.1 | PHP 7.2 - 8.3
| Aplicación Nette 3.0 | PHP 7.1 - 8.0
| Aplicación Nette 2.4 | PHP 5.6 - 8.0
-Se aplica a las últimas versiones de parches.
+Válido para las últimas versiones de parches.
\ No newline at end of file
diff --git a/application/es/@left-menu.texy b/application/es/@left-menu.texy
index c615204682..be808de52f 100644
--- a/application/es/@left-menu.texy
+++ b/application/es/@left-menu.texy
@@ -4,7 +4,7 @@ Aplicación Nette
- [Arranque |Bootstrap]
- [Presentadores |Presenters]
- [Plantillas |Templates]
-- [Módulos |Modules]
+- [Estructura del directorio |directory-structure]
- [Enrutamiento |Routing]
- [Creación de enlaces URL |creating-links]
- [Componentes interactivos |components]
diff --git a/application/es/ajax.texy b/application/es/ajax.texy
index 0ba8886dd4..d1414e84c4 100644
--- a/application/es/ajax.texy
+++ b/application/es/ajax.texy
@@ -3,10 +3,10 @@ AJAX y fragmentos
-Hoy en día, las aplicaciones web modernas se ejecutan mitad en un servidor y mitad en un navegador. AJAX es un factor de unión vital. ¿Qué soporte ofrece Nette Framework?
-- envío de fragmentos de plantillas (los llamados *snippets*)
+En la era de las aplicaciones web modernas, donde la funcionalidad a menudo se extiende entre el servidor y el navegador, AJAX es un elemento de conexión esencial. ¿Qué opciones ofrece Nette Framework en este ámbito?
+- envío de partes de la plantilla, los llamados snippets
- paso de variables entre PHP y JavaScript
-- depuración de aplicaciones AJAX
+- herramientas para depurar peticiones AJAX
@@ -14,29 +14,32 @@ Hoy en día, las aplicaciones web modernas se ejecutan mitad en un servidor y mi
Solicitud AJAX .[#toc-ajax-request]
===================================
-Una petición AJAX no difiere de una petición clásica: se llama al presentador con una vista y unos parámetros específicos. También depende del presentador cómo responder a ella: puede utilizar su propia rutina, que devuelve un fragmento de código HTML (HTML snippet), un documento XML, un objeto JSON o código JavaScript.
+Una petición AJAX fundamentalmente no difiere de una petición HTTP clásica. Se llama a un presentador con parámetros específicos. Depende del presentador cómo responder a la petición - puede devolver datos en formato JSON, enviar una parte de código HTML, un documento XML, etc.
-En el lado del servidor, una petición AJAX puede detectarse utilizando el método de servicio [que encapsula la petición HTTP |http:request] `$httpRequest->isAjax()` (detecta basándose en la cabecera HTTP `X-Requested-With`). Dentro del presentador, se dispone de un acceso directo en forma del método `$this->isAjax()`.
+En el navegador, iniciamos una petición AJAX utilizando la función `fetch()`:
-Existe un objeto preprocesado llamado `payload` dedicado a enviar datos al navegador en JSON.
-
-```php
-public function actionDelete(int $id): void
-{
- if ($this->isAjax()) {
- $this->payload->message = 'Success';
- }
- // ...
-}
+```js
+fetch(url, {
+ headers: {'X-Requested-With': 'XMLHttpRequest'},
+})
+.then(response => response.json())
+.then(payload => {
+ // tratamiento de la respuesta
+});
```
-Para un control total sobre su salida JSON utilice el método `sendJson` en su presentador. Terminará el presentador inmediatamente y prescindirá de una plantilla:
+En el lado del servidor, una petición AJAX es reconocida por el método `$httpRequest->isAjax()` del servicio [que encapsula la petición HTTP |http:request]. Utiliza la cabecera HTTP `X-Requested-With`, por lo que es imprescindible enviarla. Dentro del presentador, puede utilizar el método `$this->isAjax()`.
+
+Si desea enviar datos en formato JSON, utilice el método [`sendJson()` |presenters#Sending a response] método. El método también finaliza la actividad del presentador.
```php
-$this->sendJson(['key' => 'value', /* ... */]);
+public function actionExport(): void
+{
+ $this->sendJson($this->model->getData);
+}
```
-Si queremos enviar HTML, podemos establecer una plantilla especial para peticiones AJAX:
+Si planea responder con una plantilla especial diseñada para AJAX, puede hacerlo de la siguiente manera:
```php
public function handleClick($param): void
@@ -44,27 +47,43 @@ public function handleClick($param): void
if ($this->isAjax()) {
$this->template->setFile('path/to/ajax.latte');
}
- // ...
+ //...
}
```
+Recortes .[#toc-snippets]
+=========================
+
+La herramienta más potente que ofrece Nette para conectar el servidor con el cliente son los snippets. Con ellos, puedes convertir una aplicación ordinaria en una AJAX con el mínimo esfuerzo y unas pocas líneas de código. El ejemplo Fifteen demuestra cómo funciona todo, y su código puede encontrarse en [GitHub |https://github.com/nette-examples/fifteen].
+
+Los snippets, o recortes, permiten actualizar sólo partes de la página, en lugar de recargarla entera. Esto es más rápido y eficiente, y también proporciona una experiencia de usuario más cómoda. Puede que los snippets te recuerden a Hotwire para Ruby on Rails o a Symfony UX Turbo. Curiosamente, Nette introdujo los snippets 14 años antes.
+
+¿Cómo funcionan los fragmentos? Cuando se carga la página por primera vez (una petición no-AJAX), se carga toda la página, incluidos todos los fragmentos. Cuando el usuario interactúa con la página (por ejemplo, hace clic en un botón, envía un formulario, etc.), en lugar de cargarse toda la página, se realiza una solicitud AJAX. El código del presentador ejecuta la acción y decide qué fragmentos deben actualizarse. Nette renderiza estos fragmentos y los envía en forma de matriz JSON. A continuación, el código de gestión del navegador vuelve a insertar en la página los fragmentos recibidos. Por lo tanto, sólo se transfiere el código de los fragmentos modificados, lo que ahorra ancho de banda y acelera la carga en comparación con la transferencia de todo el contenido de la página.
+
+
Naja .[#toc-naja]
-=================
+-----------------
-K obsluze AJAXových požadavků na straně prohlížeče slouží [knihovna Naja |https://naja.js.org], [instálalo |https://naja.js.org/#/guide/01-install-setup-naja] como un paquete node.js (para usarlo con Webpack, Rollup, Vite, Parcel y más):
+Para manejar snippets en el lado del navegador, se utiliza la [librería Naja |https://naja.js.org]. [Instálala |https://naja.js.org/#/guide/01-install-setup-naja] como un paquete node.js (para usar con aplicaciones como Webpack, Rollup, Vite, Parcel, y otras):
```shell
npm install naja
```
-...o insertarlo directamente en la plantilla de la página:
+... o insértala directamente en la plantilla de la página:
```html
```
-Para crear una solicitud AJAX a partir de un enlace normal (señal) o el envío de un formulario, basta con marcar el enlace, formulario o botón correspondiente con la clase `ajax`:
+Primero hay que [inicializar |https://naja.js.org/#/guide/01-install-setup-naja?id=initialization] la biblioteca:
+
+```js
+naja.initialize();
+```
+
+Para convertir un enlace ordinario (señal) o el envío de un formulario en una petición AJAX, basta con marcar el enlace, formulario o botón correspondiente con la clase `ajax`:
```html
Go
@@ -74,64 +93,39 @@ Para crear una solicitud AJAX a partir de un enlace normal (señal) o el envío
or
+
```
-Fragmentos .[#toc-snippets]
-===========================
-
-Existe una herramienta mucho más potente de soporte AJAX integrado: los snippets. Su uso permite convertir una aplicación normal en una aplicación AJAX utilizando sólo unas pocas líneas de código. Cómo funciona todo se demuestra en el ejemplo Fifteen cuyo código también está accesible en la compilación o en [GitHub |https://github.com/nette-examples/fifteen].
-
-La forma en que funcionan los snippets es que toda la página se transfiere durante la petición inicial (es decir, no AJAX) y luego con cada [subpetición |components#signal] AJAX (petición de la misma vista del mismo presentador) sólo se transfiere el código de las partes modificadas en el repositorio `payload` mencionado anteriormente.
-
-Puede que los snippets te recuerden a Hotwire para Ruby on Rails o a Symfony UX Turbo, pero Nette los inventó catorce años antes.
-
+Redibujar fragmentos .[#toc-redrawing-snippets]
+-----------------------------------------------
-Invalidación de Snippets .[#toc-invalidation-of-snippets]
-=========================================================
-
-Cada descendiente de la clase [Control |components] (que también es un Presentador) es capaz de recordar si hubo algún cambio durante una petición que requiera que se vuelva a renderizar. Hay un par de métodos para manejar esto: `redrawControl()` y `isControlInvalid()`. Un ejemplo:
+Cada objeto de la clase [Control |components] (incluido el propio Presentador) mantiene un registro de si se han producido cambios que requieran su redibujado. Para ello se emplea el método `redrawControl()`.
```php
public function handleLogin(string $user): void
{
- // The object has to re-render after the user has logged in
+ // después de iniciar la sesión, es necesario volver a dibujar la parte pertinente
$this->redrawControl();
- // ...
+ //...
}
```
-Nette, sin embargo, ofrece una resolución aún más fina que la de los componentes completos. Los métodos mencionados aceptan el nombre de un "fragmento" como parámetro opcional. Un "fragmento" es básicamente un elemento de su plantilla marcado para ese propósito por una tag Latte, más sobre esto más adelante. Así, es posible pedir a un componente que redibuje sólo *partes* de su plantilla. Si se invalida todo el componente, entonces se vuelven a renderizar todos sus fragmentos. Un componente es "inválido" también si cualquiera de sus subcomponentes es inválido.
-
-```php
-$this->isControlInvalid(); // -> false
-$this->redrawControl('header'); // invalidates the snippet named 'header'
-$this->isControlInvalid('header'); // -> true
-$this->isControlInvalid('footer'); // -> false
-$this->isControlInvalid(); // -> true, at least one snippet is invalid
+Nette también permite un control más preciso de lo que hay que redibujar. El método mencionado puede tomar el nombre del fragmento como argumento. Así, es posible invalidar (es decir: forzar un redibujado) a nivel de la parte de la plantilla. Si se invalida todo el componente, también se redibujan todos sus fragmentos:
-$this->redrawControl(); // invalidates the whole component, every snippet
-$this->isControlInvalid('footer'); // -> true
+```php
+// invalida el fragmento de cabecera
+$this->redrawControl('header');
```
-Un componente que recibe una señal se marca automáticamente para ser redibujado.
-
-Gracias al redibujado de fragmentos, sabemos exactamente qué partes de qué elementos deben redibujarse.
-
-
-Etiqueta `{snippet} … {/snippet}` .{toc: Tag snippet}
-=====================================================
-
-El renderizado de la página procede de forma muy similar a una petición normal: se cargan las mismas plantillas, etc. Sin embargo, lo esencial es dejar fuera las partes que no deben llegar a la salida; las demás partes se asociarán a un identificador y se enviarán al usuario en un formato comprensible para un manipulador JavaScript.
-
-Sintaxis .[#toc-syntax]
------------------------
+Fragmentos en Latte .[#toc-snippets-in-latte]
+---------------------------------------------
-Si hay un control o un fragmento en la plantilla, tenemos que envolverlo usando la etiqueta `{snippet} ... {/snippet}` pair - se asegurará de que el fragmento renderizado será "recortado" y enviado al navegador. También lo encerrará en una etiqueta helper `` (es posible utilizar otra). En el siguiente ejemplo se define un fragmento llamado `header`. También puede representar la plantilla de un componente:
+Utilizar snippets en Latte es extremadamente fácil. Para definir una parte de la plantilla como fragmento, basta con envolverla en las etiquetas `{snippet}` y `{/snippet}`:
```latte
{snippet header}
@@ -139,7 +133,9 @@ Si hay un control o un fragmento en la plantilla, tenemos que envolverlo usando
{/snippet}
```
-Si desea crear un fragmento con un elemento contenedor distinto de `
` o añadir atributos personalizados al elemento, puede utilizar la siguiente definición:
+El fragmento crea un elemento `
` en la página HTML con un `id` especialmente generado. Al redibujar un fragmento, se actualiza el contenido de este elemento. Por lo tanto, cuando la página se renderiza inicialmente, también deben renderizarse todos los fragmentos, aunque inicialmente puedan estar vacíos.
+
+También puede crear un fragmento con un elemento distinto de `
` mediante un atributo n:attribute:
```latte