Directory Structure of the Application
How to design a clear and scalable directory structure for projects in Nette Framework? We'll show you proven practices that will help you organize your code. You'll learn:
- how to logically structure the application into directories
- how to design the structure to scale well as the project grows
- what are the possible alternatives and their advantages or disadvantages
It's important to mention that Nette Framework itself doesn't insist on any specific structure. It's designed to be easily adaptable to any needs and preferences.
Basic Project Structure
Although Nette Framework doesn't dictate any fixed directory structure, there is a proven default arrangement in the form of Web Project:
web-project/ ├── app/ ← application directory ├── assets/ ← SCSS, JS files, images..., alternatively resources/ ├── bin/ ← command line scripts ├── config/ ← configuration ├── log/ ← logged errors ├── temp/ ← temporary files, cache ├── tests/ ← tests ├── vendor/ ← libraries installed by Composer └── www/ ← public directory (document-root)
You can freely modify this structure according to your needs – rename or move folders. Then you just need to adjust the
relative paths to directories in Bootstrap.php
and possibly composer.json
. Nothing else is needed, no
complex reconfiguration, no constant changes. Nette has smart autodetection and automatically recognizes the application location
including its URL base.
Modules
In Nette, modules represent the logical units that make up an application. They include presenters, templates, possibly also components and model classes.
One directory for presenters and one for templates would not be enough for real projects. Having dozens of files in one folder is at least unorganized. How to get out of it? We simply split them into subdirectories on disk and into namespaces in the code. And that's exactly what the Nette modules do.
So let's forget about a single folder for presenters and templates and instead create modules, for example Admin
and Front
.
app/ ├──presenters/├── modules/ ← directory with modules │ ├── Admin/ ← module Admin │ │ ├── presenters/ ← its presenters │ │ │ ├── DashboardPresenter.php │ │ │ └── templates/ │ └── Front/ ← module Front │ └── presenters/ ← its presenters │ └── ...
This directory structure will be reflected by the class namespaces, so for example DashboardPresenter
will be in
the App\Modules\Admin\Presenters
namespace:
namespace App\Modules\Admin\Presenters;
class DashboardPresenter extends Nette\Application\UI\Presenter
{
// ...
}
The Dashboard
presenter inside the Admin
module is referenced within the application using the colon
notation as Admin:Dashboard
, and its default
action as Admin:Dashboard:default
. And how
does Nette proper know that Admin:Dashboard
represents the
App\Modules\Admin\Presenters\DashboardPresenter
class? This is determined by mapping in
the configuration. Thus, the given structure is not hard set and you can modify it according to your needs.
Modules can of course contain all other items besides presenters and templates, such as components, model classes, etc.
Nested Modules
Modules don't have to form only a flat structure, you can also create submodules, for example:
app/ ├── modules/ ← directory with modules │ ├── Blog/ ← module Blog │ │ ├── Admin/ ← submodule Admin │ │ │ ├── presenters/ │ │ │ └── ... │ │ └── Front/ ← submodule Front │ │ ├── presenters/ │ │ └── ... │ ├── Forum/ ← module Forum │ │ └── ...
Thus, the Blog
module is divided into Admin
and Front
submodules. Again, this will be
reflected in the namespaces, which will be App\Modules\Blog\Admin\Presenters
etc. The presenter
Dashboard
inside the submodule is referred to as Blog:Admin:Dashboard
.
The nesting can go as deep as you like, so sub-submodules can be created.
Presenter Mapping
Defines the rules by which the class name is derived from the presenter name. We write them in configuration under the application › mapping
key.
Let's start with a sample that doesn't use modules. We'll just want the presenter classes to have the
App\Presenters
namespace. That means that a presenter such as Homepage
should map to the
App\Presenters\HomepagePresenter
class. This can be achieved by the following configuration:
application:
mapping:
*: App\Presenters\*Presenter
The presenter name is replaced with the asterisk in the class mask and the result is the class name. Easy!
If we divide presenters into modules, we can have our own mapping for each module:
application:
mapping:
Front: App\Modules\Front\Presenters\*Presenter
Admin: App\Modules\Admin\Presenters\*Presenter
Api: App\Api\*Presenter
Now presenter Front:Homepage
maps to class `App\Modules\Front\Presenters\HomepagePresenter
and
presenter Admin:Dashboard
to class App\Modules\Admin\Presenters\DashboardPresenter
.
It is more practical to create a general (star) rule to replace the first two. The extra asterisk will be added to the class mask just for the module:
application:
mapping:
*: App\Modules\*\Presenters\*Presenter
Api: App\Api\*Presenter
But what if we use nested modules and have a presenter Admin:User:Edit
? In this case, the segment with an asterisk
representing the module for each level is simply repeated and the result is class
App\Modules\Admin\User\Presenters\EditPresenter
.
An alternative notation is to use an array consisting of three segments instead of a string. This notation is equivalent to the previous one:
application:
mapping:
*: [App\Modules, *, Presenters\*Presenter]
The default value is *: *Module\*Presenter
.