MVC Applications & Presenters

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

  • MVC, directory structure and bootstrap.php file
  • what are presenters and actions
  • how to use templates
  • what are persistent parameters

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.

Concept of Model represents whole layer, not only a single class.

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.

Directory structure

When you look at sandbox after downloading the Nette Framework package you will see the following recommended directory structure:

sandbox/
├── app/                  ← directory with application
│   ├── config/           ← configuration files
│   │   ├── config.neon   ← main config file
│   │   └── config.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 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/.

index.php & bootstrap.php

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

Nette Framework saves all its paths consistently, without the right trailing slash.

The described 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.

Firstly, the file bootstrap.php loads Nette Framework and all libraries that we depend on:

require __DIR__ . '/../vendor/autoload.php';

The class Configurator creates so called DI context and handles application initialization.

$configurator = new Nette\Configurator;

Activates debugger and logger in strict mode:

//$configurator->setDebugMode(TRUE);
$configurator->enableTracy(__DIR__ . '/../log');

Setup directory for temporary files

$configurator->setTempDirectory(__DIR__ . '/../temp');

Activate autoloading, that will automatically load all the files with our classes:

$configurator->createRobotLoader()
    ->addDirectory(__DIR__)
    ->addDirectory(__DIR__ . '/../vendor/others')
    ->register();

And according to the configuration files it generates a DI container. Everything else depends on it.

We will return this DI Container as a result of calling app/boostrap.php

$configurator->addConfig(__DIR__ . '/config/config.neon');
$configurator->addConfig(__DIR__ . '/config/config.local.neon');
return $configurator->createContainer();

and we will store it as a local variable in www/index.php and run the application:

$container = require __DIR__ . '/../app/bootstrap.php';
$container->getService('application')->run();

That's it.

Processing presenter action

So how are requests handled? Every request to our application will pass via index.php and bootstrap.php to the $application 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 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 argument:

class ProductPresenter extends Nette\Application\UI\Presenter
{
    public function renderShow($id)
    {
        // we will get data from model and pass it to template
        $this->template->product = $this->model->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, bu 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.

Templates

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 change the way of searching the templates by overriding the formatTemplateFiles or formatLayoutTemplateFiles methods.

Presenters and its components pass a few useful variables to the templates:

  • $basePath is an absolute URL path to root dir (for example /CD-collection)
  • $baseUrl is an absolute URL to root dir (for example http://localhost/CD-collection)
  • $user is a object representing the user
  • $presenter is the current presenter
  • $control is the current component or presenter
  • $flashes list of messages sent by method flashMessage()

You can read more details about template rendering 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 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.

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
{
    // ...
}

Creation of links belongs to the strongest features of Nette Framework. Thanks to two-way routing you don't have to hardcode your URLs or nastily assemble them. You can just refer to actions of presenters, and pass them parameters and the framework will generate URLs by itself. Creating links is as easy as calling a function. You will really like it!

When programming and coding templates, we don't have to care about URLs design, we will refer directly to action of presenter, that's for example the already mentioned Product:show.

Most often we create links in templates. To make it as easy as possible the framework offers three macros. The smartest of them is n:href

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

Note, that instead of the HTML attribute href we've used n:macro n:href. Its value isn't a URL, as you are used to with the href attribute, but directly an action of a presenter. The syntax is

[Presenter:]action [,] [arg1] [, arg2] [, ...]

After clicking on the link, method ProductPresenter::renderShow() will get its word and as parameter $id will get the value of $productId. We can pass even more parameters in the same way, just like we call a method. Could it get any easier?

Best practise is to write the presenter with a capital letter and the action without. The separator is a colon.

Besides that, it's even possible to pass named parameters. The next link passes the parameter lang with value cs:

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

Although the method renderShow doesn't have $lang in its declaration, it can read the value of this parameter by calling $lang = $this->getParameter('lang').

If we have all parameters in an array, we can expand them with (expand) operator:

{var $args = [$productId, lang => cs]}
<a n:href="Product:show (expand) $args">product detail</a>

If the template, in which we are creating links, is part of the Product presenter, we can omit the name of the presenter and write directly n:href="show $productId". Similarly, if a link leads to the an action named default, you can skip that and write n:href="Product: $id" (don't forget the colon).

Links can even refer to other modules. Here we distinguish, if it's refering “relatively” to a submodule, or “absolutely” to a different module – then the path begins with a colon. Let's assume that the actual presenter is part of module Front, then we will write:

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

A special case is linking to itself. Here we'll write this as the target.

The generated link is in absolute path format. When you want to generate an absolute link including the domain, for example http://example.com, simply supply two slashes at the beginning n:href="//show $productId". If we set the property $absoluteUrls in presenter to TRUE, all the links will be absolute by default.

We can refer to specific parts on the page using so called fragments, or anchors, with the hash # symbol:

<a n:href="show#comments">link to Product:show and fragment #comments</a>

The macro n:href is realy handy if we are creating an HTML tag <a>. When we want to have this link elsewhere, for example in the text of the template, we can use the {link} macro with the same internal syntax:

The address is: {link Product:show $productId}

{ifCurrent $link}...{/ifCurrent} is a conditional statement. If the link is referring to the current page, the block inside the tags is executed; otherwise it is discarded. Typical use case is adding CSS classes to the active link.

<!-- use cases -->
<a href="{link edit, 10}">edit</a>
<ul class="menu">
    <li><a href="{link Default:default}">...</a></li>
    <li><a href="{link}">...</a></li>
    ...
    <li {ifCurrent Default:default}class="active"{/ifCurrent}><a href="{link Default:default}">...</a></li>

    <!-- scope expanding -->
    <li {ifCurrent Default:*}class="active"{/ifCurrent}><a href="{link Default:default}">...</a></li>
</ul>
<!-- negation -->
{ifCurrent Admin:login}{else}<a href="{link Admin:login}">Sign in!</a>{/ifCurrent}

Read more details about the syntax of Latte templates.

Linking in presenter

Presenter and a component have the method link, which can be used to create links just like in a template. The first argument is presenters target action, followed by passed arguments:

$url = $this->link(destination [,arg [,arg ...]]);

They can also be passed using an array. Examples:

$url = $this->link('Product:show', $productId);

$url = $this->link('Product:show', [$productId, 'lang' => 'en']);

If you pass FALSE as method parameter when generating a link, it won't be added to the link at all. The solution is to define this parameter with default value TRUE or FALSE. Nette will then understand that its type is boolean, and it will be passed to the url as 1 or 0, and converted back to boolean when processed by a presenter.

It may happen that we create an invalid link – either because it refers to a non-existing presenter, or because it passes more parameters that the target method receives in its definition, or when there can't be a generated URL for the targeted action. What to do with invalid links is determined by the static variable Presenter::$invalidLinkMode. It can have one of these values (constants):

  • Presenter::INVALID_LINK_SILENT – silent mode, returns symbol # as URL
  • Presenter::INVALID_LINK_WARNING – E_USER_WARNING will be produced
  • Presenter::INVALID_LINK_TEXTUAL – visual warning, the error text is displayed in the link
  • Presenter::INVALID_LINK_EXCEPTION – InvalidLinkException will be thrown

Default setup in production mode is INVALID_LINK_WARNING and in development mode is INVALID_LINK_WARNING | INVALID_LINK_TEXTUAL. INVALID_LINK_WARNING doesn't kill the script in the production environment, but the warning will be logged. In the development environment, Tracy will intercept the warning and display the error bluescreen. If the INVALID_LINK_TEXTUAL is set, presenter and components returns error message as URL which stars with #error:. To make such links visible, we can add a CSS rule to our stylesheet:

a[href^="#error:"] {
    background: red;
    color: white;
}

If we don't want warnings to be produced in the development enviroment we can turn on silent invalid link mode in the configuration.

application:
    silentLinks: yes

Persistent parameters

Except classic parameters, that we know by now, there are so called persistent parameters. Those are different in a very essential way: they are passed through requests automatically. That means, we don't have to explicitly mention them in links, but they are passed anyway.

When your application has multiple language variants passing the actual language in every link will be incredibly tiring. But that's not needed with Nette Framework. You can simply mark the $lang parameter as persistent just like this:

class ProductPresenter extends Presenter
{
    /** @persistent */
    public $lang;

    ...

If the actual value of parameter $lang will be 'en', the into the link

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

the parameter lang => en will be passed automatically. Great!

But we can also add parameter lang and by that, change its value:

<a n:href="Product:show $productId, lang => cs">detail in Czech language</a>

The parameter will be accessible in the class variable $lang in objects of ProductPresenter and it can be accessed through $this->lang. We can even set the default value of the persistent parameter to presenter class. If the parameter value will correspond to the default value, it will not be passed in a URL.

A persistent variable must be declared as public.

Persistence reflects hierarchy of presenter classes, thus parameter defined in specific presenter will be automaticaly taken into account in every single presenter inheriting from it.

Life cycle of presenter

We know by now that a presenter action causes invoking render<Action> method, thus for example renderShow. But it's not the only method that gets invoked. When writing presenters, we can write the following methods:

Life cycle of presenter

startup()

Method startup() gets invoked immediately after the presenter is created. It initializes variables or checks user privileges.

If you write your own startup() method, don't forget to call its parent parent::startup().

action<Action>()

Analogous to the method render<View>(). There are situations when the presenter executes some specific action (authenticates the user, writes to database) and then redirects to somewhere else. To call it render would be inappropriate, because nothing is rendered. Because of that, there is an alternative called action.

It's important to know that the method action<Action>() gets called before render<View>(). It can even decide to change what render method gets invoked, by calling $this->setView('otherView') (renderOtherView() will get invoked).

handle<Signal>()

Method handles so called signals aka subrequests. Designed mainly for components and handling of AJAX requests. Processing of forms is also performed on this level.

beforeRender()

Method beforeRender, as the name suggests, gets invoked before render<View>() and it can contain for example setup of template, passing variables to template common for more views and so on.

render<View>()

Usualy passes required data to templates.

shutdown()

Gets invoked at the end of the presenters life cycle.

More accurate would be to say, that we were talking about life cycle of the class Nette\Application\UI\Presenter, from which presenters are inherited most often. In general, a presenter is any class implementing the simple interface Nette\Application\IPresenter.

Termination of presenter

We can terminate a presenter anytime during its life cycle. We usualy do this to prevent rendering a template.

  • termination of presenter directly by method: $presenter->terminate()
  • termination of presenter and immediate render the template: $presenter->sendTemplate()
  • termination of presenter and dispatch of the payload: $presenter->sendPayload() (for AJAX)
  • termination of presenter and dispatch of own response: $presenter->sendResponse($response)

A presenter can be terminated also by redirecting or by throwing a BadRequestException.

Redirection

There are two methods redirect() and forward() for jumping to another presenter, which have similar syntax as mentioned link(). For example, after submitting a form and writing to the database, we will redirect to the product's detail by calling:

$this->redirect('Product:show', $id);

While forward() will pass to new action without redirecting, method redirect() will redirect the browser with HTTP code 302 or 303. If we would want to use a different code, we will put it before the name of the action of a presenter:

$this->redirect(301, 'Product:show', $id);

To go to URLs outside of your application, you can redirect using redirectUrl():

$this->redirectUrl('https://nette.org');

Redirection immediately terminates activity of the presenter by throwing the so called terminating exception Nette\Application\AbortException.

Sometimes, before redirection, we want to send a so called flash message. That's a message that will appear after redirection in template.

Error 404, etc.

When we can't fulfill the request, because for example the record is missing in the database, we will throw an exception Nette\Application\BadRequestException, which represents HTTP error 404:

public function renderShow($id)
{
    $product = $this->model->getProduct($id);
    if (!$product) {
        throw new Nette\Application\BadRequestException;
    }

    // ...
}

When a user is not authorized to see the page, we will throw a Nette\Application\ForbiddenRequestException exception (error 403). Other error codes can be returned using second argument of Nette\Application\BadRequestException.

Flash messages

These are messages informing about a result of some operation. An important feature of flash messages is that they are available in template, even after redirection. Even when displayed, they are available for three seconds – in case that a user would unintentionally refresh the page – thus messages will not be lost.

Just call the flashMessage() method and presenter will take care about passing the message to template. First argument is the text of the message and second optional argument is its type (error, warning, info etc.). The method flashMessage() returns an instance of flash message, to allow us to add more information.

public function deleteFormSubmitted($form)
{
    // ... model asks for deletion of record ...

    // we will pass the flash message
    $this->flashMessage('Item was removed.');

    $this->redirect(...); // and redirect
}

These messages are available in templates variable $flashes as anonymous objects, that contains message and type properties and they can even contain previously mentioned user-defined information. They can be rendered for example like this:

{foreach $flashes as $flash}
<div class="flash {$flash->type}">{$flash->message}</div>
{/foreach}

Usage of model classes

As we've said before, model is a whole layer composed of many classes, where each one is taking care of one part of application logic. If we'd have for example a model App\Articles, which would take care of loading and saving articles, we may ask for it in a presenter using Dependency Injection, assuming we've properly registered it as a service in DI Container.

class ArticlePresenter extends Presenter
{

    /** @var \App\Articles @inject */
    public $articles;

    public function renderShow($id)
    {
        $this->template->article = $this->articles->find($id);
    }

}

What just happened? Let's go through it together. As we've said before, instances of presenters are created using the PresenterFactory and what this class does after creation, is that it checks if the presenter has some public properties, that are annotated with @inject and if so, it tries to find a service that implements or is instance of the type, that is in @var and passes this service to the property from DI Container.

Thanks to this mechanism, we can use the service right away, without caring where it came from or trying to instantiate it, because we're sure that it will be passed to the property, as long as it's correctly registered in the DI Container.

You can only inject into public properties of classes.

Presenter and components

When we talk about presenters, then under the term components we usually mean descendants of class Control. More accurate would be to use term “controls”, but it has a different meaning in czech and “components” did better catch on.

Presenter Nette\Application\UI\Presenter by itself, is descendant of class Control, so there is major similarity between components and presenter. But UI\Control (and thus even UI\Presenter) is primarily a so called component container. Which means that you can add to it other components. Just like we add to form inputs (text field, button, …). And just like with forms, you can access them through brackets:

// attach component to presenter
$presenter['mymenu'] = new MenuControl;

// get component from presenter and render it
$presenter['mymenu']->render();

Attaching the component to the presenter (binding them) you will be able to:

  • create links in component
  • use signals
  • use persistent parameters in components

When you don't need any of these features, you don't have to bind the component with the presenter.

Component factories

Component factories are an elegant way to create components only when they are realy needed (lazy / on demand). The whole magic is in implementation of a method called createComponent<Name>(), where <Name> is the name of component, that will create and return the desired component. This component is then attached to the presenter. The method createComponent<Name>() is optionaly passed the $name of the component, that is being created, as an argument.

class DefaultPresenter extends Nette\Application\UI\Presenter
{
    public function renderDefault()
    {
        $menu = $this['menu']; // access to component
        // if this was the first access, method createComponentMenu() will be called
        // ...
    }

    protected function createComponentMenu()
    {
        // create and configure component
        $menu = new MenuControl;
        $menu->items = $this->item;
        // and return it
        return $menu;
    }
}

The first letter of a component name is always lower case, despite that the first letter in the factory name is upper case.

Because all components are created in separate methods, the code is cleaner and easier to read.

We never call factories directly, they get called automatically, when we use components for the first time. Thanks to that, a component is created at the right moment, and only if it's really needed. If we wouldn't use the component (for example on some AJAX request, where we return only part of the page, or when parts are cached), it wouldn't even be created and we save performance of the server.

You can access and render component using macro {control}. So there is no need of manualy passing the components to template.

<h2>Edit form</h2>

{control editForm}

You can read more detailed information about components on a separate page.

Persistent components

Not only parameters, but also components can be persistent. Their state is then passed around when jumping to another presenter, just like with persistent parameters. We mark persistent components with this annotation (here we mark components calendar and menu):

/**
 * @persistent(calendar, menu)
 */
class DefaultPresenter extends Presenter
{
    // ...
}

You don't have to mark subcomponents as persistent, they are persistent automatically.

Where can i get some components?

On page Add-ons, plugins and components you can find some open-source components that were made and shared by community of Nette Framework. Nette Foundation is not liable for them.

Configuration

We can modify application settings in the config.neon configuration file, under application section.

errorPresenter

Default value: Nette:Error

This parameter specifies the presenter which is invoked when an error occurs. We can find an example in the sandbox.

If an error has occurred while the error presenter is being executed, Tracy will show only a simple error page.

catchExceptions

Default value: TRUE in production mode, FALSE in debug mode

This parameter decides if the error presenter will be invoked. By changing this value to TRUE in the debug mode we can test functionality of the error presenter.

mapping

Default value: *: *Module\*Presenter (in the sandbox *: App\*Module\Presenters\*Presenter)

This parameter contains an array with rules for converting presenter name to the class name. In the key we define a module name or * as a fallback for other modules. The value contains the rule mask:

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

The presenter Front:Blog:Article is contained inside the Front module. Since this module has no explicit mapping, the mapping with asterisk will be used and the class will be called App\FrontModule\BlogModule\Presenters\ArticlePresenter. The presenter Admin:User:Edit is part of the Admin module which has separate mapping so the name of the presenter class will be App\Admin\UserModule\EditPresenter (note that since the Admin is already in the mapping definition, it will be stripped from the resulting class name).

Default value: FALSE

This option specifies Nette behaviour in the debug mode when a link generation fails. With FALSE value Nette will trigger E_USER_WARNING. Changing this configuration option to TRUE will suppress this error. In production environment, E_USER_WARNING will be always triggered.

We can also adjust this behaviour using class property Presenter::$invalidLinkMode.

scan*

Nette allows automatic registration of presenters as services. This step improves presenter creation speed. There are few configuration options that adjust where and how Nette looks for them.

scanDirs

Default value: %appDir% (/app)

This value contains an array with directories where Nette looks for the presenters. If we specify another value in the configuration, it will be merged with the default one:

application:
    scanDirs:
        - foo

In this case scanDirs will contain values %appDir% and foo. If we want to disable or overwrite this configuration option, we need to use ! modifier:

application:
    scanDirs!:
        - foo

Now scanDirs will contain only foo.

scanComposer

Default value: TRUE

This parameter specifies if Nette should look for presenters in the Composer classmap.

scanFilter

Default value: Presenter

Specifies the string which must be included in the file and class name.