Modules
Modules bring clarity to Nette applications by facilitating easy division into logical units.
Similar to organizing files into folders on a hard drive, in Nette we can divide presenters, templates, and other auxiliary classes into modules. How does this work in practice? Simply by incorporating new subdirectories into the structure. Here’s an example of a structure with two modules, Front and Admin:
app/ ├── UI/ │ ├── Admin/ ← Admin module │ │ ├── @layout.latte │ │ ├── Dashboard/ │ │ │ ├── DashboardPresenter.php │ │ │ └── default.latte │ │ └── ... │ ├── Front/ ← Front module │ │ ├── @layout.latte │ │ ├── Home/ │ │ │ ├── HomePresenter.php │ │ │ └── default.latte │ │ └── ...
This directory structure is reflected in the namespaces of the classes, so for example, DashboardPresenter
is
located in the namespace App\UI\Admin\Dashboard
:
namespace App\UI\Admin\Dashboard;
class DashboardPresenter extends Nette\Application\UI\Presenter
{
// ...
}
In the application, we refer to the Dashboard
presenter within the Admin
module using colon notation
as Admin:Dashboard
. For its default
action, we refer to it as Admin:Dashboard:default
.
The structure presented is not rigid; you can fully customize it to your needs in the configuration.
Modules can include all other files, such as components and auxiliary classes, in addition to presenters and templates. If you
are considering where to place these, consider using an Accessory
folder:
app/ ├── UI/ │ ├── Admin/ │ │ ├── Accessory/ │ │ │ ├── FormFactory.php │ │ │ └── AdminLayout.php │ │ ├── Dashboard/ │ │ └── ...
Nested Modules
Modules can have multiple levels of nesting, similar to a directory structure on a disk:
app/ ├── UI/ │ ├── Blog/ ← Blog module │ │ ├── Admin/ ← Admin submodule │ │ │ ├── Dashboard/ │ │ │ └── ... │ │ ├── Front/ ← Front submodule │ │ │ ├── @layout.latte │ │ │ ├── Home/ │ │ │ └── ... │ ├── Forum/ ← Forum module │ │ └── ...
The Blog
module is divided into Admin
and Front
submodules. This is also reflected in
the namespaces, which then appear as App\UI\Blog\Admin
and similarly. To refer to the Dashboard
presenter within the Admin
submodule, we refer to it as Blog:Admin:Dashboard
.
Nesting can be as deep as needed, allowing the creation of sub-submodules.
For example, if in administration you have many presenters related to order management, such as OrderDetail
,
OrderEdit
, OrderDispatch
, etc., you might create an Order
module in which presenters like
Detail
, Edit
, Dispatch
, and others will be organized.
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:
<a n:href="Product:show">link to Front:Product:show</a>
A link is relative even if it includes the name of a module, which is then considered a submodule:
<a n:href="Shop:Product:show">link to Front:Shop:Product:show</a>
Absolute links are written analogously to absolute paths on disk, but with colons instead of slashes. Thus, an absolute link starts with a colon:
<a n:href=":Admin:Product:show">link to Admin:Product:show</a>
To find out if we are in a certain module or its submodule we can use isModuleCurrent(moduleName)
function.
<li n:class="isModuleCurrent('MyEshop:Users') ? active">
<a n:href="Product:">...</a>
</li>
Routing
See chapter on routing.
Mapping
Mapping defines the rules for deriving the class name from the presenter name. These rules are specified in the configuration under the key
application › mapping
.
The directory structures mentioned earlier on this page are based on the following mapping:
application:
mapping: App\UI\*\**Presenter
How does the mapping work? For a better understanding, let's first imagine an application without modules. We want the
presenter classes to fall under the namespace App\UI
, so that the Home
presenter maps to the class
App\UI\HomePresenter
. This can be achieved with this configuration:
application:
mapping: App\UI\*Presenter
This mapping works by replacing the asterisk in the mask App\UI\*Presenter
with the presenter name
Home
, resulting in the final class name App\UI\HomePresenter
. Simple!
However, as you can see in the examples in this and other chapters, we place presenter classes in eponymous subdirectories,
e.g., the Home
presenter is mapped to the class App\UI\Home\HomePresenter
. This is achieved by doubling
the asterisk (requires Nette Application 3.2):
application:
mapping: App\UI\**Presenter
Now, let's move on to mapping presenters into modules. We can define specific mappings for each module:
application:
mapping:
Front: App\UI\Front\**Presenter
Admin: App\UI\Admin\**Presenter
Api: App\Api\*Presenter
According to this configuration, the presenter Front:Home
maps to the class
App\UI\Front\Home\HomePresenter
, while the presenter Api:OAuth
maps to the class
App\Api\OAuthPresenter
.
Since the Front
and Admin
modules have a similar mapping approach and there are likely to be more
such modules, it is possible to create a general rule that replaces them. A new asterisk for the module is added to the
class mask:
application:
mapping:
*: App\UI\*\**Presenter
Api: App\Api\*Presenter
For multi-level nested modules, such as the presenter Admin:User:Edit
, the asterisk segment repeats for each
level, resulting in the class App\UI\Admin\User\Edit\EditPresenter
.
An alternative notation is to use an array composed of three segments instead of a string. This notation is equivalent to the previous one:
application:
mapping:
*: [App\UI, *, **Presenter]
Api: [App\Api, '', *Presenter]
If we have only one rule in the configuration, the general one, we can write briefly:
application:
mapping: App\UI\*\**Presenter