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.
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/
.
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 creates an DI container.
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
.
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(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, 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 examplehttp://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 methodflashMessage()
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
- an object of class
HomepagePresenter
will be created - method
renderDefault()
will be called (if it exists, but it doesn't have to) - template, for example
templates/Homepage/default.latte
with layout, for exampletemplates/@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
{
// ...
}
Link Creation
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
.
Links in Templates
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 referring “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 really 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.
Invalid Links
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 URLPresenter::INVALID_LINK_WARNING
– E_USER_WARNING will be producedPresenter::INVALID_LINK_TEXTUAL
– visual warning, the error text is displayed in the linkPresenter::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 environment we can turn on silent invalid link mode in the configuration.
application:
silentLinks: true
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 Nette\Application\UI\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 automatically 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>()
Usually 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.
Sending Responses
We can terminate a presenter at any time during his life cycle and send a different response than the default rendering of the template.
- terminate presenter by method:
$presenter->terminate()
- terminate presenter and immediate render the template:
$presenter->sendTemplate()
- terminate presenter and dispatch of the payload:
$presenter->sendPayload()
(for AJAX) - terminate presenter and dispatch of own response:
$presenter->sendResponse($response)
- terminate presenter and dispatch of JSON:
$presenter->sendJson($data)
- terminate presenter and dispatch of error message:
$presenter->error($message, $httpCode)
Example of sending JSON:
public function actionData(): void
{
$data = ['hello' => 'nette'];
$this->sendJson($data);
}
Examples of using the sendResponse
method:
// Plain text
$this->sendResponse(new Nette\Application\Responses\TextResponse('Hello Nette!'));
// Sends a file
$this->sendResponse(new Nette\Application\Responses\FileResponse(__DIR__ . '/invoice.pdf', 'Invoice13.pdf'));
// Response with callback
$this->sendResponse(new Nette\Application\Responses\CallbackResponse(function (Http\IRequest $httpRequest, Http\IResponse $httpResponse) {
if ($httpResponse->getHeader('Content-Type') === 'text/html')) {
echo '<h1>Hello</h1>';
}
});
A presenter can be terminated also by redirecting or forwarding, see below.
You can find a repository of presenter responses on Componette.
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. This way we will redirect permanently with code 301:
$this->redirectPermanent('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
using method error()
, which represents HTTP error 404:
public function renderShow(int $id): void
{
$product = $this->productRepository->getProduct($id);
if (!$product) {
$this->error();
}
// ...
}
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(): void
{
// ... 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\ArticleRepository
, 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 Nette\Application\UI\Presenter
{
/** @var ArticleRepository */
public $articles;
public function __construct(ArticleRepository $articles)
{
$this->articles = $articles;
}
public function renderShow(int $id): void
{
$this->template->article = $this->articles->find($id);
}
}
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 really 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(): void
{
$menu = $this['menu']; // access to component
// if this was the first access, method createComponentMenu() will be called
// ...
}
protected function createComponentMenu(): MenuControl
{
// 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 manually 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 Nette\Application\UI\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.
See application configuration.