-
-
Notifications
You must be signed in to change notification settings - Fork 276
Expand file tree
/
Copy pathhow-it-works.texy
More file actions
200 lines (127 loc) Β· 15.6 KB
/
how-it-works.texy
File metadata and controls
200 lines (127 loc) Β· 15.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
How Do Applications Work?
*************************
<div class=perex>
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
</div>
Directory Structure
===================
Open the example skeleton of a web application called [WebProject|https://github.com/nette/web-project]. As you read, you can refer to the files being discussed.
The directory structure looks something like this:
/--pre
<b>web-project/</b>
βββ <b>app/</b> β application directory
β βββ <b>Core/</b> β core classes necessary for operation
β β βββ <b>RouterFactory.php</b> β URL address configuration
β βββ <b>Presentation/</b> β presenters, templates & co.
β β βββ <b>@layout.latte</b> β layout template
β β βββ <b>Home/</b> β Home presenter directory
β β βββ <b>HomePresenter.php</b> β Home presenter class
β β βββ <b>default.latte</b> β template for default action
β βββ <b>Bootstrap.php</b> β booting class Bootstrap
βββ <b>assets/</b> β resources (SCSS, TypeScript, source images)
βββ <b>bin/</b> β scripts executed from the command line
βββ <b>config/</b> β configuration files
β βββ <b>common.neon</b>
β βββ <b>services.neon</b>
βββ <b>log/</b> β logged errors
βββ <b>temp/</b> β temporary files, cache, β¦
βββ <b>vendor/</b> β libraries installed by Composer
β βββ ...
β βββ <b>autoload.php</b> β autoloading of all installed packages
βββ <b>www/</b> β public directory, document root of the project
β βββ <b>assets/</b> β compiled static files (CSS, JS, images, ...)
β βββ <b>.htaccess</b> β mod_rewrite rules
β βββ <b>index.php</b> β initial file that launches the application
βββ <b>.htaccess</b> β 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 |directory-structure#Presenters and Templates] 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 |nette:troubleshooting#How to Change or Remove www Directory from URL] so that the document-root points to this directory.
You can also download WebProject directly, including Nette, using [Composer |best-practices:composer]:
```shell
composer create-project nette/web-project
```
On Linux or macOS, set [write permissions |nette:troubleshooting#Setting Directory 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 |nette:troubleshooting#How to Configure a Server for Nice URLs], 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|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 |bootstrapping#Development vs Production Mode] 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|bootstrapping].
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|dependency-injection:introduction] 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 |bootstrapping#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 |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:
```php
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 |templates#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|presenters] page.
[* request-flow.svg *]
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 |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 |templates].
In the template, you [create links |creating-links] to other presenters & actions like this:
```latte
<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:
```latte
<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 |forms:in-presenter], [datagrids |https://componette.org/contributte/datagrid/], menus, pollsβessentially anything that makes sense to reuse. We can create our own components or utilize some from the [vast selection |https://componette.org] 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 |components#Hollywood Style].
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 |bootstrapping#DI Container Configuration], which use the [NEON|neon:format] 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 |http:configuration#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 |nette:configuring] and how to [define your own services |dependency-injection:services].
Once you delve a bit into service creation, you'll encounter the term [autowiring |dependency-injection: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|quickstart:] tutorial yet?
In addition to what's described above, Nette offers a whole arsenal of [useful classes|utils:], a [database layer|database:], etc. Try clicking through the documentation. Or visit the [blog|https://blog.nette.org]. You'll discover many interesting things.
May the framework bring you much joy π