Módulos

Os módulos dão clareza aos aplicativos Nette, facilitando a divisão em unidades lógicas.

Da mesma forma que organizamos os arquivos em pastas em um disco rígido, na Nette podemos dividir apresentadores, modelos e outras classes auxiliares em módulos. Como isso funciona na prática? Simplesmente incorporando novos subdiretórios à estrutura. Aqui está um exemplo de uma estrutura com dois módulos, Front e Admin:

app/
├── UI/
│   ├── Admin/            ← Admin module
│   │   ├── @layout.latte
│   │   ├── Dashboard/
│   │   │   ├── DashboardPresenter.php
│   │   │   └── default.latte
│   │   └── ...
│   ├── Front/            ← Front module
│   │   ├── @layout.latte
│   │   ├── Home/
│   │   │   ├── HomePresenter.php
│   │   │   └── default.latte
│   │   └── ...

Essa estrutura de diretórios é refletida nos namespaces das classes, portanto, por exemplo, DashboardPresenter está localizado no namespace App\UI\Admin\Dashboard:

namespace App\UI\Admin\Dashboard;

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

No aplicativo, nos referimos ao apresentador Dashboard dentro do módulo Admin usando a notação de dois pontos como Admin:Dashboard. Para sua ação default, nos referimos a ela como Admin:Dashboard:default.

A estrutura apresentada não é rígida; você pode personalizá-la totalmente de acordo com suas necessidades na configuração.

Os módulos podem incluir todos os outros arquivos, como componentes e classes auxiliares, além de apresentadores e modelos. Se estiver pensando em onde colocá-los, considere o uso de uma pasta Accessory:

app/
├── UI/
│   ├── Admin/
│   │   ├── Accessory/
│   │   │   ├── FormFactory.php
│   │   │   └── AdminLayout.php
│   │   ├── Dashboard/
│   │   └── ...

Módulos aninhados

Os módulos podem ter vários níveis de aninhamento, semelhante a uma estrutura de diretórios em um disco:

app/
├── UI/
│   ├── Blog/             ← Blog module
│   │   ├── Admin/        ← Admin submodule
│   │   │   ├── Dashboard/
│   │   │   └── ...
│   │   ├── Front/        ← Front submodule
│   │   │   ├── @layout.latte
│   │   │   ├── Home/
│   │   │   └── ...
│   ├── Forum/            ← Forum module
│   │   └── ...

O módulo Blog é dividido em submódulos Admin e Front. Isso também se reflete nos namespaces, que aparecem como App\UI\Blog\Admin e similares. Para nos referirmos ao apresentador Dashboard dentro do submódulo Admin, nos referimos a ele como Blog:Admin:Dashboard.

O aninhamento pode ser tão profundo quanto necessário, permitindo a criação de sub-submódulos.

Por exemplo, se na administração você tem muitos apresentadores relacionados ao gerenciamento de pedidos, como OrderDetail, OrderEdit, OrderDispatch, etc., você pode criar um módulo Order no qual apresentadores como Detail, Edit, Dispatch e outros serão organizados.

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

O mapeamento define as regras para derivar o nome da classe do nome do apresentador. Essas regras são especificadas na configuração sob a chave application › mapping.

As estruturas de diretório mencionadas anteriormente nesta página são baseadas no seguinte mapeamento:

application:
	mapping: App\UI\*\**Presenter

Como funciona o mapeamento? Para entender melhor, vamos primeiro imaginar um aplicativo sem módulos. Queremos que as classes do apresentador se enquadrem no namespace App\UI, de modo que o apresentador Home mapeie para a classe App\UI\HomePresenter. Isso pode ser obtido com esta configuração:

application:
	mapping: App\UI\*Presenter

Esse mapeamento funciona substituindo o asterisco na máscara App\UI\*Presenter pelo nome do apresentador Home, resultando no nome final da classe App\UI\HomePresenter. Simples!

Entretanto, como você pode ver nos exemplos deste e de outros capítulos, colocamos as classes de apresentador em subdiretórios homônimos, por exemplo, o apresentador Home é mapeado para a classe App\UI\Home\HomePresenter. Isso é feito duplicando-se o asterisco (requer o Nette Application 3.2):

application:
	mapping: App\UI\**Presenter

Agora, vamos passar a mapear os apresentadores em módulos. Podemos definir mapeamentos específicos para cada módulo:

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

De acordo com essa configuração, o apresentador Front:Home mapeia para a classe App\UI\Front\Home\HomePresenter, enquanto o apresentador Api:OAuth mapeia para a classe App\Api\OAuthPresenter.

Como os módulos Front e Admin têm uma abordagem de mapeamento semelhante e é provável que haja mais módulos desse tipo, é possível criar uma regra geral que os substitua. Um novo asterisco para o módulo é adicionado à máscara de classe:

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

Para módulos aninhados em vários níveis, como o apresentador Admin:User:Edit, o segmento de asterisco se repete para cada nível, resultando na classe App\UI\Admin\User\Edit\EditPresenter.

Uma notação alternativa é usar uma matriz composta de três segmentos em vez de uma cadeia de caracteres. Essa notação é equivalente à anterior:

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

Se tivermos apenas uma regra na configuração, a geral, podemos escrever de forma resumida:

application:
	mapping: App\UI\*\**Presenter