Request Lifecycle

We will learn how to create applications in the Nette Framework. After this chapter you will know:

  • MVC, directory structure and Bootstrap.php file
  • what are presenters and actions

Model-View-Controller (MVC)

Model-View-Controller is a software architecture that was created to separate utility code (controller) from application logic code (model) and from code for displaying data (view) in applications with a graphical user interface. With this approach the application is easier to understand, it will simplify future development and enables us to test each unit of the application separately.

Model

Model is data and it is the functional base of the whole application. It contains application logic. Any user action (login, insertion of goods to basket, a change of value in the database) represents a model action. The Model is managing its internal state and provides a stable interface to the outside. By calling methods on this interface, we can read or update its state. The Model doesn't know anything about View or Controller.

View

View is an application layer that is taking care of displaying. It usually uses the templating engine and knows how each component should be rendered by getting data from the model.

Controller

A controller handles requests from the user, calls relevant application logic (Model) and then asks the View to display data. In Nette Framework, controllers are represented by presenters.

Processing Presenter Action

The browser is sending all requests through one file located in public directory www/ and that is index.php. It passes the control to the application (that is the app/ directory), to a boot file Bootstrap.php, where configures the environment and DI container.

The DI container creates a so-called front controller, which is a Nette\Application\Application class object. But this object does not understand HTTP requests, so it will ask the router to translate the request. The router will look at the request and tells what presenter is needed, and what action it should execute. For example, the router answers, that the user wants action show of presenter Product (it's a good habit to write it like Product:show) and pass it parameter id = 123. You can read it like: user wants to show product with id 123.

This is finally understandable to the Application and it will proceed to fulfill the request. It will create an object of class ProductPresenter, that represents presenter Product. (To be completely accurate, the application asks the presenterFactory service for the creation of a presenter). This new presenter will be asked for execution of action (show with parameter id).

The presenter is an object that takes the request, translated by router, and computes the answer. This can be an HTML page, an image, an XML document, a file, JSON, redirection or anything that you need. In detail, the ProductPresenter will query the model for data and pass that data to the template for displaying. This is usually done in method renderShow, where the word Show must match the name of the action and request parameter id will be passed to the method as an argument:

class ProductPresenter extends Nette\Application\UI\Presenter
{
	public function renderShow(int $id): void
	{
		// we will get data from model and pass it to template
		$this->template->product = $this->productRepository->getProduct($id);
	}
}

If you really want, you can get an array of all the GET request parameters by calling $this->request->getParameters(), but usually you shouldn't need them, but instead use routing and action parameters.

Similarly you can get all received POST parameters by calling $this->request->getPost(). And normally, neither should you need this method. Mostly you're processing form requests and there is a form component for that.

Then the presenter will render the template.

The presenter will deduce the path to the template file from a few simple rules. It will try, for presenter Product and action show, if one of these files exists:

  • templates/Product/show.latte
  • templates/Product.show.latte

Presenter will also try to search for a layout (that is optional):

  • templates/Product/@layout.latte
  • templates/Product.@layout.latte
  • templates/@layout.latte layout shared by multiple presenters

You can read more details about template language Latte in a separate chapter.

Actually, it's not hard at all! When you're requesting an action, for example Homepage:default, then

  1. an object of class HomepagePresenter will be created
  2. method renderDefault() will be called (if it exists, but it doesn't have to)
  3. template, for example templates/Homepage/default.latte with layout, for example templates/@layout.latte, will be rendered

and in the template we can create a link to mentioned Product:show($id), roughly like this:

<a n:href="Product:show $productId">product detail</a>

It looks like creating applications in Nette Framework will be child's play.

Directory Structure

When you look at the sandbox after downloading the Nette Framework package you will see the following recommended directory structure. This directory structure is only a recommendation. You can easily change it to whatever you want. All you have to do is rename directories and change paths in Bootstrap.php.

sandbox/
├── app/                  ← directory with application
│   ├── config/           ← configuration files
│   │   ├── common.neon   ← main config file
│   │   └── local.neon
│   │
│   ├── forms/            ← form classes
│   ├── model/            ← model layer and its classes
│   ├── presenters/       ← presenter classes
│   │   ├── HomepagePresenter.php  ← Homepage presenter class
│   │   └── templates/    ← templates directory
│   │       ├── @layout.latte ← template of shared layout
│   │       └── Homepage/     ← templates for Homepage presenter
│   │           └── default.latte  ← template for default action
│   ├── router/           ← router classes
│   │
│   └── Bootstrap.php     ← application boot file
│
├── log/                  ← contains logs, errors, etc.
├── temp/                 ← for temporary files, cache, ...
│
├── vendor/               ← directory with libraries (for example 3rd party)
│   ├── nette/            ← all Nette Framework libraries
│   │   └── nette/Nette   ← Nette Framework itself installed by Composer
│   ├── ...
│   │
│   └── autoload.php      ← script that handles autoloading of all classes from installed packages
│
└── www/                  ← public directory, document root of project
    ├── .htaccess         ← rules for mod_rewrite
    ├── index.php         ← triggers the application
    └── images/           ← other directories, images, styles, ..

Moreover, in some directories, there are .htaccess and web.config files, which deny access from the browser (for Apache or IIS). Make sure that these are working, and that you cannot reach app/ and libs/ directories from your browser.

Don't forget to grant write privilege (chmod 0777) to directories log/ and temp/.

Modules

When developing complex applications, we can separate directories with presenters and templates to subdirectories. We call them modules. If our application would contain for example modules Front and Admin, its structure could look like this:

sandbox/
	app/                ← directory with application
		AdminModule/    ← Admin module
			presenters/ ← its presenters
			templates/  ← its templates

		FrontModule/    ← Front module
			presenters/ ← its presenters
			templates/  ← its templates

		...

Modules don't have to be arranged in flat structures, you can even create submodules.

If module Front would contain presenter Product. Action show can than be written as Front:Product:show and class ProductPresenter will be placed into namespace FrontModule:

namespace FrontModule;

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

Related blog posts