Moduły

W Nette moduły reprezentują logiczne jednostki, które tworzą aplikację. Należą do nich prezentery, szablony, ewentualnie komponenty i klasy modeli.

Jeden komponent dla prezenterów i jeden dla szablonów nie wystarczyłby dla prawdziwych projektów. Posiadanie kilkudziesięciu plików w jednym folderze jest co najmniej niezorganizowane. Jak się z niego wydostać? Po prostu dzielimy je na podkatalogi na dysku i na przestrzenie nazw w kodzie. I właśnie to robią moduły Nette.

Zapomnijmy więc o jednym folderze dla prezenterów i szablonów, a zamiast tego stwórzmy moduły, na przykład Admin i Front.

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

Ta struktura katalogów będzie odzwierciedlona w przestrzeni nazw klas, więc na przykład DashboardPresenter będzie w przestrzeni App\Modules\Admin\Presenters:

namespace App\Modules\Admin\Presenters;

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

Prezenter Dashboard wewnątrz modułu Admin jest określany w aplikacji za pomocą notacji z podwójną kropką jako Admin:Dashboard, a jego działanie default jest określane jako Admin:Dashboard:default. A skąd Nette own wie, że Admin:Dashboard reprezentuje klasę App\Modules\Admin\Presenters\DashboardPresenter? Mówimy to za pomocą mapowania w konfiguracji. Tak więc podana struktura nie jest stała i można ją modyfikować według potrzeb.

Moduły mogą oczywiście zawierać wszystkie inne części oprócz prezenterów i szablonów, jak komponenty, klasy modeli itp.

Moduły zagnieżdżone

Moduły nie muszą tworzyć tylko płaskiej struktury, można też tworzyć np. submoduły:

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

Tak więc moduł Blog jest podzielony na podmoduły Admin i Front. I znowu będzie to odzwierciedlone w przestrzeni nazw, która będzie App\Modules\Blog\Admin\Presenters itd. Prezenter Dashboard wewnątrz submodułu jest określany jako Blog:Admin:Dashboard.

Rozgałęzienie może iść tak głęboko, jak chcesz, więc możesz tworzyć podmoduły.

Linki w szablonach prezenterów są względne do bieżącego modułu. Tak więc link Foo:default prowadzi do prezentera Foo w tym samym module co aktualny prezenter. Na przykład, jeśli bieżący moduł to Front, to link idzie tak:

<a n:href="Product:show">odkaz na Front:Product:show</a>

Link jest względny, nawet jeśli nazwa modułu jest jego częścią, jest wtedy uważany za submoduł:

<a n:href="Shop:Product:show">odkaz na Front:Shop:Product:show</a>

Odwołania bezwzględne są zapisywane analogicznie do ścieżek bezwzględnych na dysku, ale z dwukropkami zamiast ukośników. Tak więc link bezwzględny zaczyna się od dwukropka:

<a n:href=":Admin:Product:show">odkaz na Admin:Product:show</a>

Aby dowiedzieć się, czy jesteśmy w danym module lub submodule, korzystamy z funkcji isModuleCurrent(moduleName).

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

Routing

Patrz rozdział dotyczący routingu.

Mapowanie

Określa zasady, według których nazwa klasy jest wyprowadzana z nazwy prezentera. Zapisujemy je w konfiguracji pod kluczem application › mapping.

Zacznijmy od próbki, która nie korzysta z modułów. Będziemy chcieli, aby klasy prezentera miały przestrzeń nazw App\Presenters. To znaczy, będziemy chcieli, aby prezenter, na przykład, Home mapował do klasy App\Presenters\HomePresenter. Można to osiągnąć dzięki następującej konfiguracji:

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

Zastąp nazwę prezentera gwiazdką w masce klasy, a wynikiem będzie nazwa klasy. Spokojnie!

Jeśli złamiemy prezenterów na moduły, możemy mieć niestandardowe mapowanie dla każdego modułu:

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

Teraz prezenter Front:Home mapuje do klasy App\Modules\Front\Presenters\HomePresenter, a prezenter Admin:Dashboard mapuje do klasy App\Modules\Admin\Presenters\DashboardPresenter.

Bardziej praktyczne będzie stworzenie ogólnej (gwiazdkowej) reguły, która zastąpi pierwsze dwie. W masce klasy zostanie dodana dodatkowa gwiazdka tylko dla tego modułu:

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

Ale co w przypadku, gdy korzystamy z wielu zagnieżdżonych modułów i mamy np. prezentera Admin:User:Edit? W takim przypadku segment z gwiazdką reprezentujący moduł dla każdego poziomu zostanie po prostu powtórzony, a wynikiem będzie klasa App\Modules\Admin\User\Presenters\EditPresenter.

Alternatywną notacją jest użycie tablicy składającej się z trzech segmentów zamiast ciągu. Ta notacja jest równoważna z poprzednią:

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

Wartość domyślna to *: *Module\*Presenter.