How do applications work?

You are currently reading the basic document of the Nette documentation. You will learn all the principle of web applications. Nice from A to Z, from the moment of birth until the last breath of the PHP script. After reading you will know:

  • how it all works
  • what is Bootstrap, Presenter and DI container
  • what the directory structure looks like

Directory Structure

Open an example web application called Sandbox and as you read, you can watch the files being written about.

The directory structure looks something like this:

sandbox/
├── app/                      ← directory with application
│   ├── config/               ← configuration files
│   │   ├── common.neon
│   │   └── local.neon
│   ├── 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 action `default`
│   ├── Router/               ← configuration of URL addresses
│   └── Bootstrap.php         ← booting class Bootstrap
├── bin/                      ← scripts for the command line
├── log/                      ← error logs
├── temp/                     ← temporary files, cache, …
├── vendor/                   ← libraries installed by Composer
│   ├── ...
│   └── autoload.php          ← autoloading of libs installed by Composer
├── www/                      ← public directory, document root of project
│   ├── images/               ← další adresáře, třeba pro obrázky
│   ├── .htaccess             ← mod_rewrite rules etc
│   └── index.php             ← initial file that launches the application
└── .htaccess                 ← prohibits access to all directories except www

You can change the directory structure in any way, rename or move folders, and then just edit the paths to log/ and temp/ in the Bootstrap.php file and the path to this file in composer.json in the autoload section. Nothing more, no complicated reconfiguration, no constant changes. Nette has a smart autodetection.

The public directory www/ can be changed without having to set anything else. In fact, it is common that due to the specifics of your hosting, you will need to rename it or, conversely, set the so-called document-root to this directory in the hosting configuration. If your hosting does not allow you to create folders one level above the public directory, we advise you to look for another hosting. Otherwise, you would run a significant security risk.

You can also download the Sandbox directly, including Nette, using Composer:

composer create-project nette/sandbox

On Linux or macOS, set the write permissions for directories log/ and temp/.

The Sandbox application is ready to run, there is no need to configure anything else at all and you can view it directly in the browser by accessing the folder www/.

HTTP request

It all begins when a user opens the page in a browser and browser knocks on the server with an HTTP request. The request goes to a PHP file located in the public directory www/, which is index.php. Let's suppose that this is a request to https://example.com/product/123. Thanks to the appropriate server settings, this URL is also mapped to the index.php file and will be executed.

Its task is:

  1. initialize the environment
  2. get the factory
  3. launch the Nette application that handles the request

What kind of factory? We do not produce tractors, but websites! Hold on, it'll be explained right away.

By “initialize the environment” we mean, for example, that Tracy is activated, which is an amazing tool for logging or visualizing errors. It logs errors on the production server and displays them directly on the development server. Therefore, initialization also needs to decide whether the site is running in production or developer mode. To do this, Nette uses autodetection: if you run the site on localhost, it runs in developer mode. You don't have to configure anything and the application is ready for both development and production deployment. These steps are performed and described in detail in the chapter about Bootstrap class.

The third point (yes, we skipped the second, but we will return to it) is to start the application. The handling of HTTP requests in Nette is done by the class Nette\Application\Application (hereinafter referred to as the Application), so when we say “run an application”, we mean to call a method with the name run() on an object of this class.

Now we have to recall a sentence from preface that Nette is a mentor who guides you to write clean applications by proven methodologies. And the most proven is called dependency injection, abbreviated DI. At the moment we don't want to burden you with explaining DI, since there is a separate chapter, the important thing here is that the key objects will usually be created by a factory for objects called DI container (abbreviated DIC). Yes, this is the factory that was mentioned a while ago. And it also creates the Application object for us, so we need a container first. We get it using the Configurator class and let it produce Application object, call the method run() and this starts Nette application. This is exactly what happens in the index.php file.

Nette Application

The Application class has a single task: to respond to an HTTP request.

Applications written in Nette are divided into many so-called presenters (in other frameworks you may come across the term controller, which is the same), which are classes representing a specific website page: eg homepage; product in e-shop; sign-in form; sitemap feed, etc. The application can have from one to thousands of presenters.

The application starts by asking the so-called router to decide which of the presenters to pass the current request for processing. The router decides whose responsibility it is. It looks at the input URL https://example.com/product/123 and, based on how it is set up, decides that this is a job, for example, for presenter Product, who wants to show a product with id => 123 as an action. It is a good habit to write a pairs of presenter + action separated by a colon as Product:show.

So the router transformed the URL into a pair Presenter:action + parameters, in our case Product:show + id => 123. You can see how a router looks like in file app/Router/RouterFactory.php and we will describe it in detail in chapter Routing.

Let's move on. The application already knows the name of the presenter and can continue. By creating an object ProductPresenter, which is the code of presenter Product. More precisely, it asks the DI container for creating the presenter, because producting objects is its job.

The presenter might look like this:

class ProductPresenter extends Nette\Application\UI\Presenter
{
	private $repository;

	public function __construct(ProductRepository $repository)
	{
		$this->repository = $repository;
	}

	public function renderShow(int $id): void
	{
		// we obtain data from the model and pass it to the template
		$this->template->product = $this->repository->getProduct($id);
	}
}

The request is handled by the presenter. And the task is clear: do action show with id => 123. Which in the language of presenters means that the method renderShow() is called and in the parameter $id it gets 123.

A presenter can handle multiple actions, ie have multiple methods render<Action>(). But we recommend designing presenters with one or as few actions as possible.

So, the method renderShow(123) was called, whose code is fictional example, but you can see on it how the data is passed to the template, ie by writing to $this->template.

Subsequently, the presenter returns the answer. This can be an HTML page, an image, an XML document, sending a file from disk, JSON or redirecting to another page. Importantly, if we do not explicitly say how to respond (which is the case of ProductPresenter), the answer will be to render the template with an HTML page. Why? Well, because in 99% of cases we want to draw a template, so the presenter takes this behavior as the default and wants to make our work easier. That's Nette's point.

We don't even have to state which template to draw, he derives the path to it according to simple logic. In the case of presenter Product and action show, it tries to see if one of these template files exists relative to the directory where class ProductPresenter is located:

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

It will also try to find the layout in these locations:

  • templates/Product/@layout.latte
  • templates/Product.@layout.latte
  • templates/@layout.latte – layout common to multiple presenters

And then it renders the template. Now the task of the presenter and the entire application is completed. If the template does not exist, a page with error 404 will be returned. You can read more about presenters on the Presenters page.

Just to be sure, let's try to recap the whole process with a slightly different URL:

  1. the URL will be https://example.com
  2. we boot the application, create a container and run Application::run()
  3. the router decodes the URL as a pair Homepage:default
  4. an HomepagePresenter object is created
  5. method renderDefault() is called (if exists)
  6. a template templates/Homepage/default.latte with a layout templates/@layout.latte is rendered

You may have come across a lot of new concepts now, but we believe they make sense. Creating applications in Nette is a breeze.

Templates

When it comes to the templates, Nette uses the Latte template system. That's why the files with templates ends with .latte. Latte is used because it is the most secure template system for PHP, and at the same time the most intuitive system. You don't have to learn much new, you just need to know PHP and a few Latte tags. You will find out everything in the documentation.

In template we create a links to other presenters & actions as follows:

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

Simply write the familiar Presenter:action pair instead of the real URL and include any parameters. The trick is n:href, which says that this attribute will be processed by Nette. And it will generate:

<a href="/product/456">product detail</a>

The previously mentioned router is in charge of generating the URL. In fact, routers in Nette are unique in that they can perform not only transformations from a URL to a pair of presenter:action, but also vice versa generate a URL from the name of the presenter + action + parameters. Thanks to this, in Nette you can completely change the form of the URL in the whole finished application without changing a single character in the template or presenter just by modifying the router. And thanks to this, the so-called canonization works, which is another unique feature of Nette, which improves SEO (optimization of searchability on the internet) by automatically preventing the existence of duplicate content at different URLs. Many programmers find this amazing.

Components

We have one more thing to tell you about presenters: they have a built-in component system. Older of you may remember something similar from Delphi or ASP.NET Web Forms. React or Vue.js is built on something remotely similar. In the world of PHP frameworks, this is a completely unique feature.

Components are separate reusable units that we place into pages (ie presenters). They can be forms, datagrids, menus, polls, in fact anything that makes sense to use repeatedly. We can create our own components or use some of the huge range of opensource components.

Components fundamentally change the approach to application development. They will open up new possibilities for composing pages from pre-defined units.

DI Container and Configuration

DI container (factory for objects) is the heart of the whole application.

Don't worry, it's not a magical black box, as it might seem from the previous words. Actually, it's one pretty boring PHP class generated by Nette and stored in a cache directory. It has a lot of methods named as createServiceAbcd() and each of them creates and returns an object. Yes, there is also a method createServiceApplication() that will produce Nette\Application\Application, which we needed in the file index.php to run the application. And there are methods for producing individual presenters. And so on.

The objects that the DI container creates are called services for some reason.

What is really special about this class is that it is not programmed by you, but by the framework. It actually generates the PHP code and saves it to disk. You just give instructions on what objects the container should be able to produce and how exactly. And these instructions are written in configuration files in the NEON format and therefore have the extension .neon.

The configuration files are used purely to instruct the DI container. So, for example, if I specify the expiration: 14 days option in the session section, the DI container when creating the Nette\Http\Session object representing the session will call its method setExpiration('14 days'), and thus configuration becomes a reality.

There is a whole chapter ready for you, describing what can be configured and how to define your own services.

Once you get into the creation of services, you will come across the word autowiring. This is a gadget that will make your life incredibly easier. It can automatically pass objects where you need them (in the constructors of your classes, for example) without having to do anything. You will find that the DI container in Nette is a small miracle.

Modules

For slightly larger applications, we can divide folders with presenters and templates into subdirectories (on disk) and into namespaces (in code), which we call modules. If our application contained, for example, modules Front and Admin, its structure could look like this:

app/
├── Modules/              ← directory with modules
│   ├── Admin/            ← Admin module
│   │   └── Presenters    ← its presenters
│   │       └── templates ← and templates
│   └── Front/            ← Front module
│       └── Presenters    ← its presenters
│           └── templates ← and templates

Modules do not have to be only a flat structure, also submodules can be created.

If the module Front included a presenter Product, then the action show in this presenter would be written as Front:Product:show and the class ProductPresenter would be placed in the namespace, eg App\Modules\Front:

namespace App\Modules\Front;

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

What next?

We went through the basic principles of applications in Nette. So far, very superficially, but you will soon delve into the depths and eventually create wonderful web applications. Where to continue? Have you tried the tutorial Create Your First Application?

In addition to the above, Nette has a whole arsenal of useful classes, database layer, etc. Try purposely just click through documentation. Or visit blog. You will discover a lot of interesting things.

Let the framework bring you a lot of joy 💙


Related blog posts