Módulos

Em Nette, os módulos representam as unidades lógicas que compõem uma aplicação. Eles incluem apresentadores, gabaritos, possivelmente também componentes e classes de modelos.

Um diretório para apresentadores e um para modelos não seria suficiente para projetos reais. Ter dezenas de arquivos em uma pasta é pelo menos desorganizado. Como sair dela? Nós simplesmente os dividimos em subdiretórios em disco e em namespaces no código. E é exatamente isso que os módulos Nette fazem.

Portanto, vamos esquecer uma única pasta para apresentadores e modelos e, em vez disso, criar módulos, por exemplo Admin e Front.

app/
├── Presenters/
├── Modules/              ← directory with modules
│   ├── Admin/            ← module Admin
│   │   ├── Presenters/   ← its presenters
│   │   │   ├── DashboardPresenter.php
│   │   │   └── templates/
│   └── Front/            ← module Front
│       └── Presenters/   ← its presenters
│           └── ...

Esta estrutura de diretório será refletida pelos namespaces de classe, portanto, por exemplo DashboardPresenter estará no namespace App\Modules\Admin\Presenters:

namespace App\Modules\Admin\Presenters;

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

O Dashboard apresentador dentro do módulo Admin é referenciado dentro da aplicação usando a notação de cólon como Admin:Dashboard, e sua ação default como Admin:Dashboard:default. E como a Nette propriamente dita sabe que Admin:Dashboard representa a classe App\Modules\Admin\Presenters\DashboardPresenter? Isto é determinado pelo mapeamento na configuração. Assim, a estrutura dada não é difícil de definir e você pode modificá-la de acordo com suas necessidades.

Os módulos podem naturalmente conter todos os outros itens além de apresentadores e modelos, tais como componentes, classes de modelos, etc.

Módulos aninhados

Os módulos não precisam formar apenas uma estrutura plana, você também pode criar submódulos, por exemplo:

app/
├── Modules/              ← directory with modules
│   ├── Blog/             ← module Blog
│   │   ├── Admin/        ← submodule Admin
│   │   │   ├── Presenters/
│   │   │   └── ...
│   │   └── Front/        ← submodule Front
│   │       ├── Presenters/
│   │       └── ...
│   ├── Forum/            ← module Forum
│   │   └── ...

Assim, o módulo Blog está dividido em Admin e Front submódulos. Mais uma vez, isto se refletirá nos namespaces, que serão App\Modules\Blog\Admin\Presenters etc. O apresentador Dashboard dentro do submódulo é referido como Blog:Admin:Dashboard.

O ninho pode ir tão fundo quanto você desejar, de modo que sub-submódulos podem ser criados.

Os links nos modelos de apresentadores são relativos ao módulo atual. Assim, o link Foo:default leva ao apresentador Foo no mesmo módulo que o apresentador atual. Se o módulo atual é Front, por exemplo, então o link vai assim:

<a n:href="Product:show">link to Front:Product:show</a>

Um link é relativo mesmo que inclua o nome de um módulo, que é então considerado um submódulo:

<a n:href="Shop:Product:show">link to Front:Shop:Product:show</a>

Links absolutos são escritos analogamente a caminhos absolutos em disco, mas com colons ao invés de cortes. Assim, uma ligação absoluta começa com dois-pontos:

<a n:href=":Admin:Product:show">link to Admin:Product:show</a>

Para saber se estamos em um determinado módulo ou em seu submódulo, podemos usar a função isModuleCurrent(moduleName).

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

Roteiro

Ver capítulo sobre roteamento.

Mapeamento

Define as regras pelas quais o nome da classe é derivado do nome do apresentador. Nós as escrevemos na configuração sob a chave application › mapping.

Vamos começar com uma amostra que não utiliza módulos. Queremos apenas que as classes de apresentadores tenham o namespace App\Presenters. Isso significa que um apresentador como o Home deve mapear para a classe App\Presenters\HomePresenter. Isto pode ser conseguido através da seguinte configuração:

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

O nome do apresentador é substituído pelo asterisco na máscara da classe e o resultado é o nome da classe. Fácil!

Se dividirmos os apresentadores em módulos, podemos ter nosso próprio mapeamento para cada módulo:

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

Agora o apresentador Front:Home mapeia para a classe App\Modules\Front\Presenters\HomePresenter e o apresentador Admin:Dashboard para a classe App\Modules\Admin\Presenters\DashboardPresenter.

É mais prático criar uma regra geral (estrela) para substituir as duas primeiras. O asterisco extra será adicionado à máscara de classe apenas para o módulo:

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

Mas e se usarmos módulos aninhados e tivermos um apresentador Admin:User:Edit? Neste caso, o segmento com um asterisco representando o módulo para cada nível é simplesmente repetido e o resultado é a classe App\Modules\Admin\User\Presenters\EditPresenter.

Uma notação alternativa é utilizar um conjunto composto de três segmentos em vez de um fio. Esta notação é equivalente à anterior:

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

O valor padrão é *: *Module\*Presenter.