How Do Applications Work?

You are currently reading the foundational chapter of the Nette documentation. You will learn the complete principles behind how web applications work, from A to Z, from the moment a request is born until the PHP script completes execution. After reading, you will understand:

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

Directory Structure

Open the example skeleton of a web application called WebProject. As you read, you can refer to the files being discussed.

The directory structure looks something like this:

web-project/
├── app/                      ← application directory
│   ├── Core/                 ← core classes necessary for operation
│   │   └── RouterFactory.php ← URL address configuration
│   ├── Presentation/         ← presenters, templates & co.
│   │   ├── @layout.latte     ← layout template
│   │   └── Home/             ← Home presenter directory
│   │       ├── HomePresenter.php ← Home presenter class
│   │       └── default.latte ← template for default action
│   └── Bootstrap.php         ← booting class Bootstrap
├── bin/                      ← scripts executed from the command line
├── config/                   ← configuration files
│   ├── common.neon
│   └── services.neon
├── log/                      ← logged errors
├── temp/                     ← temporary files, cache, …
├── vendor/                   ← libraries installed by Composer
│   ├── ...
│   └── autoload.php          ← autoloading of all installed packages
├── www/                      ← public directory, document root of the project
│   ├── .htaccess             ← mod_rewrite rules
│   └── 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; it is completely flexible. Nette also features smart autodetection and automatically recognizes the application's location, including its URL base.

For slightly larger applications, we can organize presenter and template folders into subdirectories and group classes into namespaces, which we call modules.

The www/ directory represents the public directory or document-root of the project. You can rename it without needing to configure anything else on the application side. You just need to configure the hosting so that the document-root points to this directory.

You can also download WebProject directly, including Nette, using Composer:

composer create-project nette/web-project

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

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

HTTP Request

Everything starts when a user opens a page in their browser. The browser sends an HTTP request to the server. This request targets a single PHP file located in the public directory www/, which is index.php. Let's assume the request is for the address https://example.com/product/123. Thanks to appropriate server configuration, even this URL is mapped to the index.php file, which is then executed.

Its task is to:

  1. initialize the environment
  2. obtain the factory
  3. run the Nette application, which handles the request

What factory? We're not producing tractors, we're building websites! Hold on, it will be explained shortly.

By ‘environment initialization’, we mean, for example, activating Tracy, which is an amazing tool for logging or visualizing errors. On a production server, it logs errors; in a development environment, it displays them directly. Thus, initialization also includes determining whether the site is running in production or development mode. Nette uses smart autodetection for this: if you run the site on localhost, it operates in development mode. You don't need to configure anything, and the application is immediately ready for both development and live deployment. These steps are performed and described in detail in the chapter about the Bootstrap class.

The third point (yes, we skipped the second, but we'll return to it) is launching the application. Handling HTTP requests in Nette is the responsibility of the Nette\Application\Application class (hereafter Application). So, when we say run the application, we specifically mean calling the aptly named run() method on an object of this class.

Nette acts as a mentor, guiding you to write clean applications according to proven methodologies. One of the most established of these is dependency injection, abbreviated as DI. We don't want to burden you with explaining DI right now; there's a dedicated chapter for that. The essential consequence is that key objects are typically created by an object factory known as the DI container (or DIC). Yes, this is the factory mentioned earlier. It also produces the Application object for us, which is why we need the container first. We obtain it using the Configurator class, let it create the Application object, call the run() method on it, and thus the Nette application starts. This is precisely what happens in the index.php file.

Nette Application

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

Applications written in Nette are divided into many so-called presenters (you might encounter the term ‘controller’ in other frameworks, which is essentially the same thing). These are classes, each representing a specific page of the website: e.g., the homepage, a product in an e-shop, a login form, a sitemap feed, etc. An application can have anywhere from one to thousands of presenters.

The Application starts by asking the so-called router to decide which presenter should handle the current request. The router determines the responsibility. It examines the input URL https://example.com/product/123 and, based on its configuration, decides that this task belongs, for example, to the Product presenter, which should perform the show action for the product with id: 123. It's good practice to write the presenter + action pair separated by a colon, like Product:show.

Thus, the router transformed the URL into the pair Presenter:action + parameters, in our case Product:show + id: 123. You can see what such a router looks like in the file app/Core/RouterFactory.php, and we describe it in detail in the Routing chapter.

Let's move on. The Application now knows the name of the presenter and can proceed. It does this by creating an instance of the ProductPresenter class, which contains the code for the Product presenter. More precisely, it asks the DI container to create the presenter, because creating objects is its responsibility.

The presenter might look like this:

class ProductPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private ProductRepository $repository,
	) {
	}

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

The presenter takes over handling the request. The task is clear: execute the show action with id: 123. In presenter terminology, this means the renderShow() method is called, receiving 123 in the $id parameter.

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

So, the renderShow(123) method was called. Its code is a fictional example, but it demonstrates how data is passed to the template, specifically by writing to $this->template.

Subsequently, the presenter returns a response. This could be an HTML page, an image, an XML document, sending a file from the disk, JSON, or perhaps a redirect to another page. Importantly, if we don't explicitly specify how to respond (which is the case with ProductPresenter), the response will be to render a template into an HTML page. Why? Because in 99% of cases, we want to render a template. Therefore, the presenter adopts this behavior as the default to simplify our work. That's the essence of Nette.

We don't even need to specify which template to render; the framework deduces the path automatically. In the case of the show action, it simply attempts to load the show.latte template located in the same directory as the ProductPresenter class. It also tries to find the layout in the @layout.latte file (more details on template lookup).

Then, the templates are rendered. This completes the task of the presenter and the entire application. If the template doesn't exist, a 404 error page is returned. You can learn more about presenters on the Presenters page.

To be sure, let's recap the entire process with a slightly different URL:

  1. The URL is https://example.com
  2. The application boots, the DI container is created, and Application::run() is executed.
  3. The router decodes the URL into the pair Home:default.
  4. An instance of the HomePresenter class is created.
  5. The renderDefault() method is called (if it exists).
  6. The template, e.g., default.latte, is rendered along with the layout, e.g., @layout.latte.

You might have encountered many new concepts just now, but we believe they make sense. Developing applications in Nette is remarkably straightforward.

Templates

Speaking of templates, Nette uses the Latte templating system. That's why template files have the .latte extension. Latte is used primarily because it's the most secure templating system for PHP, and also the most intuitive. You don't need to learn much new; knowledge of PHP and a few tags is sufficient. You'll find everything you need in the documentation.

In the template, you create links to other presenters & actions like this:

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

Simply write the familiar Presenter:action pair instead of the actual URL and include any necessary parameters. The trick lies in n:href, which tells Nette to process this attribute. It will then generate:

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

URL generation is handled by the aforementioned router. Routers in Nette are exceptional because they can perform not only the transformation from a URL to a Presenter:action pair but also the reverse: generating a URL from the presenter name, action, and parameters. Thanks to this, you can completely change the URL format throughout your entire finished application in Nette without altering a single character in the templates or presenters—simply by modifying the router. This also enables so-called canonization, another unique Nette feature that enhances SEO (Search Engine Optimization) by automatically preventing duplicate content from existing on different URLs. Many programmers find this capability astounding.

Interactive Components

We need to tell you one more thing about presenters: they have a built-in component system. Those with more experience might recall something similar from Delphi or ASP.NET Web Forms; React or Vue.js are built on somewhat related concepts. In the world of PHP frameworks, this is a completely unique feature.

Components are independent, reusable units that we embed into pages (i.e., presenters). These can be forms, datagrids, menus, polls—essentially anything that makes sense to reuse. We can create our own components or utilize some from the vast selection of open-source components.

Components fundamentally influence the approach to application development. They open up new possibilities for composing pages from pre-prepared units. And they also have something in common with Hollywood.

DI Container and Configuration

The DI container, or object factory, is the heart of the entire application.

Don't worry, it's not some magical black box, as the preceding lines might suggest. In reality, it's a rather mundane PHP class generated by Nette and stored in the cache directory. It contains many methods named like createServiceAbcd(), each capable of creating and returning a specific object. Yes, there's also a createServiceApplication() method that produces the Nette\Application\Application instance we needed in index.php to run the application. There are also methods for creating individual presenters, and so on.

The objects created by the DI container are, for some reason, called services.

What's truly special about this class is that you don't program it—the framework does. It actually generates the PHP code and saves it to disk. You simply provide instructions on which objects the container should be able to create and how exactly. These instructions are written in configuration files, which use the NEON format and thus have the .neon extension.

Configuration files serve purely to instruct the DI container. So, for example, if you 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 setExpiration('14 days') method, thereby making the configuration a reality.

There's an entire chapter prepared for you describing what can be configured and how to define your own services.

Once you delve a bit into service creation, you'll encounter the term autowiring. This is a feature that will simplify your life incredibly. It can automatically pass objects where you need them (for example, in the constructors of your classes) without you having to do anything. You'll discover that the DI container in Nette is a small miracle.

What Next?

We've covered the fundamental principles of Nette applications. While it's been a surface-level overview so far, you'll soon delve deeper and, in time, create amazing web applications. Where to go next? Have you tried the Create Your First Application tutorial yet?

In addition to what's described above, Nette offers a whole arsenal of useful classes, a database layer, etc. Try clicking through the documentation. Or visit the blog. You'll discover many interesting things.

May the framework bring you much joy 💙

version: 4.0 3.x 2.x