Nette Documentation Preview

syntax
Adresářová struktura aplikace
*****************************

<div class=perex>

Jak navrhnout přehlednou a škálovatelnou adresářovou strukturu pro projekty v Nette Framework? Ukážeme si osvědčené postupy, které vám pomohou s organizací kódu. Dozvíte se:

- jak **logicky rozčlenit** aplikaci do adresářů
- jak strukturu navrhnout tak, aby **dobře škálovala** s růstem projektu
- jaké jsou **možné alternativy** a jejich výhody či nevýhody

</div>


Důležité je zmínit, že Nette Framework samotný na žádné konkrétní struktuře nelpí. Je navržen tak, aby se dal snadno přizpůsobit jakýmkoliv potřebám a preferencím.


Základní struktura projektu
===========================

Přestože Nette Framework nediktuje žádnou pevnou adresářovou strukturu, existuje osvědčené výchozí uspořádání v podobě [Web Project|https://github.com/nette/web-project]:

/--pre
<b>web-project/</b>
├── <b>app/</b>              ← adresář s aplikací
├── <b>assets/</b>           ← soubory SCSS, JS, obrázky..., alternativně resources/
├── <b>bin/</b>              ← skripty pro příkazovou řádku
├── <b>config/</b>           ← konfigurace
├── <b>log/</b>              ← logované chyby
├── <b>temp/</b>             ← dočasné soubory, cache
├── <b>tests/</b>            ← testy
├── <b>vendor/</b>           ← knihovny instalované Composerem
└── <b>www/</b>              ← veřejný adresář (document-root)
\--

Tuto strukturu můžete libovolně upravovat podle svých potřeb - složky přejmenovat či přesouvat. Poté stačí pouze upravit relativní cesty k adresářům v souboru `Bootstrap.php` a případně `composer.json`. Nic víc není potřeba, žádná složitá rekonfigurace, žádné změny konstant. Nette disponuje chytrou autodetekcí a automaticky rozpozná umístění aplikace včetně její URL základny.


Moduly
======

Moduly představují v Nette logické celky, ze kterých se aplikace skládá. Jejich součástí jsou presentery, šablony, případně i komponenty a modelové třídy.

S jednou složkou pro presentery a jednou pro šablony bychom si u reálných projektů nevystačili. Mít v jedné složce desítky souborů je minimálně nepřehledné. Jak z toho ven? Jednoduše je na disku rozdělíme do podadresářů a v kódu do jmenných prostorů. A přesně to jsou v Nette moduly.

Zapomeňme tedy na jednu složku pro presentery a šablony a místo toho vytvoříme moduly, například `Admin` a `Front`.

/--pre
<b>app/</b>
├── <del>presenters/</del>
├── <b>modules/</b>              ← adresář s moduly
│   ├── <b>Admin/</b>            ← modul Admin
│   │   ├── <b>presenters/</b>   ← jeho presentery
│   │   │   ├── <b>DashboardPresenter.php</b>
│   │   │   └── <b>templates/</b>
│   └── <b>Front/</b>            ← modul Front
│       └── <b>presenters/</b>   ← jeho presentery
│           └── ...
\--

Tuto adresářovou strukturu budou reflektovat jmenné prostory tříd, takže třeba `DashboardPresenter` bude v prostoru `App\Modules\Admin\Presenters`:

```php
namespace App\Modules\Admin\Presenters;

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

Na presenter `Dashboard` uvnitř modulu `Admin` se v rámci aplikace odkazujeme pomocí dvojtečkové notace jako na `Admin:Dashboard`, na jeho akci `default` potom jako na `Admin:Dashboard:default`.
A jak Nette vlastní ví, že `Admin:Dashboard` představuje třídu `App\Modules\Admin\Presenters\DashboardPresenter`? To mu řekneme pomocí [#mapování] v konfiguraci.
Tedy uvedená struktura není pevná a můžete si ji upravit podle potřeb.

Moduly mohou kromě presenterů a šablon samozřejmě obsahovat všechny další součásti, jako jsou třeba komponenty, modelové třídy, atd.


Vnořené moduly
--------------

Moduly nemusí tvořit jen plochou strukturu, lze vytvářet i submoduly, například:

/--pre
<b>app/</b>
├── <b>modules/</b>              ← adresář s moduly
│   ├── <b>Blog/</b>             ← modul Blog
│   │   ├── <b>Admin/</b>        ← submodul Admin
│   │   │   ├── <b>presenters/</b>
│   │   │   └── ...
│   │   └── <b>Front/</b>        ← submodul Front
│   │       ├── <b>presenters/</b>
│   │       └── ...
│   ├── <b>Forum/</b>            ← modul Forum
│   │   └── ...
\--

Tedy modul `Blog` je rozdělen do submodulů `Admin` a `Front`. A opět se to odrazí na jmenných prostorech, které budou `App\Modules\Blog\Admin\Presenters` apod. Na presenter `Dashboard` uvnitř submodulu se odkazujeme jako `Blog:Admin:Dashboard`.

Zanořování může pokračovat libovolně hluboko, lze tedy vytvářet sub-submoduly.


Mapování presenterů
-------------------

Definuje pravidla, podle kterých se z názvu presenteru odvodí název třídy. Zapisujeme je v [konfiguraci|configuration] pod klíčem `application › mapping`.

Začněme ukázkou, která moduly nepoužívá. Budeme jen chtít, aby třídy presenterů měly jmenný prostor `App\Presenters`. Tedy aby se presenter například `Homepage` mapoval na třídu `App\Presenters\HomepagePresenter`. Toho lze docílit následující konfigurací:

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

Název presenteru se nahradí za hvezdičku v masce třídy a výsledkem je název třídy. Snadné!

Pokud presentery členíme do modulů, můžeme pro každý modul mít vlastní mapování:

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

Nyní se presenter `Front:Homepage` mapuje na třídu `App\Modules\Front\Presenters\HomepagePresenter` a presenter `Admin:Dashboard` na třídu `App\Modules\Admin\Presenters\DashboardPresenter`.

Praktičtější bude vytvořit obecné (hvězdičkové) pravidlo, které první dvě nahradí. V masce třídy přibude hvezdička navíc právě pro modul:

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

Ale co když používáme vícenásobně zanořené moduly a máme třeba presenter `Admin:User:Edit`? V takovém případě se segment s hvězdičkou představující modul pro každou úroveň jednoduše zopakuje a výsledkem bude třída `App\Modules\Admin\User\Presenters\EditPresenter`.

Alternativním zápisem je místo řetězce použít pole skládající se ze tří segmentů. Tento zápis je ekvivaletní s předchozím:

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

Výchozí hodnotou je `*: *Module\*Presenter`.

Adresářová struktura aplikace

Jak navrhnout přehlednou a škálovatelnou adresářovou strukturu pro projekty v Nette Framework? Ukážeme si osvědčené postupy, které vám pomohou s organizací kódu. Dozvíte se:

  • jak logicky rozčlenit aplikaci do adresářů
  • jak strukturu navrhnout tak, aby dobře škálovala s růstem projektu
  • jaké jsou možné alternativy a jejich výhody či nevýhody

Důležité je zmínit, že Nette Framework samotný na žádné konkrétní struktuře nelpí. Je navržen tak, aby se dal snadno přizpůsobit jakýmkoliv potřebám a preferencím.

Základní struktura projektu

Přestože Nette Framework nediktuje žádnou pevnou adresářovou strukturu, existuje osvědčené výchozí uspořádání v podobě Web Project:

web-project/
├── app/              ← adresář s aplikací
├── assets/           ← soubory SCSS, JS, obrázky..., alternativně resources/
├── bin/              ← skripty pro příkazovou řádku
├── config/           ← konfigurace
├── log/              ← logované chyby
├── temp/             ← dočasné soubory, cache
├── tests/            ← testy
├── vendor/           ← knihovny instalované Composerem
└── www/              ← veřejný adresář (document-root)

Tuto strukturu můžete libovolně upravovat podle svých potřeb – složky přejmenovat či přesouvat. Poté stačí pouze upravit relativní cesty k adresářům v souboru Bootstrap.php a případně composer.json. Nic víc není potřeba, žádná složitá rekonfigurace, žádné změny konstant. Nette disponuje chytrou autodetekcí a automaticky rozpozná umístění aplikace včetně její URL základny.

Moduly

Moduly představují v Nette logické celky, ze kterých se aplikace skládá. Jejich součástí jsou presentery, šablony, případně i komponenty a modelové třídy.

S jednou složkou pro presentery a jednou pro šablony bychom si u reálných projektů nevystačili. Mít v jedné složce desítky souborů je minimálně nepřehledné. Jak z toho ven? Jednoduše je na disku rozdělíme do podadresářů a v kódu do jmenných prostorů. A přesně to jsou v Nette moduly.

Zapomeňme tedy na jednu složku pro presentery a šablony a místo toho vytvoříme moduly, například Admin a Front.

app/
├── presenters/
├── modules/              ← adresář s moduly
│   ├── Admin/            ← modul Admin
│   │   ├── presenters/   ← jeho presentery
│   │   │   ├── DashboardPresenter.php
│   │   │   └── templates/
│   └── Front/            ← modul Front
│       └── presenters/   ← jeho presentery
│           └── ...

Tuto adresářovou strukturu budou reflektovat jmenné prostory tříd, takže třeba DashboardPresenter bude v prostoru App\Modules\Admin\Presenters:

namespace App\Modules\Admin\Presenters;

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

Na presenter Dashboard uvnitř modulu Admin se v rámci aplikace odkazujeme pomocí dvojtečkové notace jako na Admin:Dashboard, na jeho akci default potom jako na Admin:Dashboard:default. A jak Nette vlastní ví, že Admin:Dashboard představuje třídu App\Modules\Admin\Presenters\DashboardPresenter? To mu řekneme pomocí mapování v konfiguraci. Tedy uvedená struktura není pevná a můžete si ji upravit podle potřeb.

Moduly mohou kromě presenterů a šablon samozřejmě obsahovat všechny další součásti, jako jsou třeba komponenty, modelové třídy, atd.

Vnořené moduly

Moduly nemusí tvořit jen plochou strukturu, lze vytvářet i submoduly, například:

app/
├── modules/              ← adresář s moduly
│   ├── Blog/             ← modul Blog
│   │   ├── Admin/        ← submodul Admin
│   │   │   ├── presenters/
│   │   │   └── ...
│   │   └── Front/        ← submodul Front
│   │       ├── presenters/
│   │       └── ...
│   ├── Forum/            ← modul Forum
│   │   └── ...

Tedy modul Blog je rozdělen do submodulů Admin a Front. A opět se to odrazí na jmenných prostorech, které budou App\Modules\Blog\Admin\Presenters apod. Na presenter Dashboard uvnitř submodulu se odkazujeme jako Blog:Admin:Dashboard.

Zanořování může pokračovat libovolně hluboko, lze tedy vytvářet sub-submoduly.

Mapování presenterů

Definuje pravidla, podle kterých se z názvu presenteru odvodí název třídy. Zapisujeme je v konfiguraci pod klíčem application › mapping.

Začněme ukázkou, která moduly nepoužívá. Budeme jen chtít, aby třídy presenterů měly jmenný prostor App\Presenters. Tedy aby se presenter například Homepage mapoval na třídu App\Presenters\HomepagePresenter. Toho lze docílit následující konfigurací:

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

Název presenteru se nahradí za hvezdičku v masce třídy a výsledkem je název třídy. Snadné!

Pokud presentery členíme do modulů, můžeme pro každý modul mít vlastní mapování:

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

Nyní se presenter Front:Homepage mapuje na třídu App\Modules\Front\Presenters\HomepagePresenter a presenter Admin:Dashboard na třídu App\Modules\Admin\Presenters\DashboardPresenter.

Praktičtější bude vytvořit obecné (hvězdičkové) pravidlo, které první dvě nahradí. V masce třídy přibude hvezdička navíc právě pro modul:

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

Ale co když používáme vícenásobně zanořené moduly a máme třeba presenter Admin:User:Edit? V takovém případě se segment s hvězdičkou představující modul pro každou úroveň jednoduše zopakuje a výsledkem bude třída App\Modules\Admin\User\Presenters\EditPresenter.

Alternativním zápisem je místo řetězce použít pole skládající se ze tří segmentů. Tento zápis je ekvivaletní s předchozím:

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

Výchozí hodnotou je *: *Module\*Presenter.