Nette Documentation Preview

syntax
Как работают приложения?
************************

<div class=perex>

Сейчас вы читаете основной документ документации Nette. Вы узнаете все принципы работы веб-приложений. Все мелочи от А до Я, от момента рождения до последнего вздоха PHP-скрипта. После прочтения вы будете знать:

- как все это работает
- что такое Bootstrap, Presenter и DI контейнер
- как выглядит структура каталогов

</div>


Структура каталога .[#toc-directory-structure]
==============================================

Откройте скелетный пример веб-приложения под названием [WebProject|https://github.com/nette/web-project], и вы сможете наблюдать, как происходит запись файлов.

Структура каталогов выглядит примерно так:

/--pre
<b>web-project/</b>
├── <b>app/</b>                      ← каталог с приложением
│   ├── <b>Core/</b>                 ← основные необходимые классы
│   │   └── <b>RouterFactory.php</b> ← настройка URL-адресов
│   ├── <b>UI/</b>                   ← презентаторы, шаблоны и др.
│   │   ├── <b>@layout.latte</b>     ← шаблон общего макета
│   │   └── <b>Home/</b>             ← Главная директория ведущих
│   │       ├── <b>HomePresenter.php</b> ← Класс презентера дома
│   │       └── <b>default.latte</b> ← шаблон для действий default
│   └── <b>Bootstrap.php</b>         ← загрузочный класс Bootstrap
├── <b>bin/</b>                      ← скрипты командной строки
├── <b>config/</b>                   ← файлы конфигурации
│   ├── <b>common.neon</b>
│   └── <b>services.neon</b>
├── <b>log/</b>                      ← журналы ошибок
├── <b>temp/</b>                     ← временные файлы, кэш, …
├── <b>vendor/</b>                   ← библиотеки, установленные через Composer
│   ├── ...
│   └── <b>autoload.php</b>          ← автозагрузчик библиотек, установленных через Composer
├── <b>www/</b>                      ← публичный корневой каталог проекта
│   ├── <b>.htaccess</b>             ← правила mod_rewrite и т. д.
│   └── <b>index.php</b>             ← начальный файл, запускающий приложение
└── <b>.htaccess</b>                 ← запрещает доступ ко всем каталогам, кроме www
\--

Вы можете изменить структуру каталогов любым способом, переименовать или переместить папки, а затем просто отредактировать пути к `log/` и `temp/` в файле `Bootstrap.php` и путь к этому файлу в `composer.json` в секции `autoload`. Ничего больше, никакой сложной перенастройки, никаких постоянных изменений. Nette имеет [интеллектуальное автоопределение|bootstrap#development-vs-production-mode].

Для немного больших приложений мы можем разделить папки с ведущими и шаблонами на подкаталоги (на диске) и на пространства имен (в коде), которые мы называем [модулями|modules].

Публичный каталог `www/` может быть изменен без необходимости устанавливать что-либо ещё. На самом деле, часто бывает, что из-за специфики вашего хостинга вам придется переименовать его или, наоборот, установить так называемый document-root на этот каталог в конфигурации хостинга. Если ваш хостинг не позволяет создавать папки на один уровень выше публичного каталога, советуем вам поискать другой хостинг. В противном случае вы подвергнете себя значительному риску безопасности.

Вы также можете загрузить WebProject напрямую, включая Nette, используя [Composer |best-practices:composer]:

```shell
composer create-project nette/web-project
```

В Linux или macOS установите [разрешения на запись |nette:troubleshooting#Setting-Directory-Permissions] для каталогов `log/` и `temp/`.

Приложение WebProject готово к запуску, больше ничего настраивать не нужно, и вы можете просмотреть его прямо в браузере, обратившись к папке `www/`.


HTTP-запрос .[#toc-http-request]
================================

Все начинается с того, что пользователь открывает страницу в браузере, а браузер стучится на сервер с HTTP-запросом. Запрос идет к PHP-файлу, расположенному в публичном каталоге `www/`, который называется `index.php`. Предположим, что это запрос на `https://example.com/product/123`. Благодаря соответствующим настройкам [server settings |nette:troubleshooting#how-to-configure-a-server-for-nice-urls], этот URL также сопоставлен с файлом `index.php` и будет выполнен.

Его задача состоит в следующем:

1) инициализация среды
2) получение фабрики
3) запуск приложения Nette, которое обрабатывает запрос

Что за фабрика? Мы производим не тракторы, а веб-сайты! Подождите, сейчас всё будет объяснено.

Под «инициализацией среды» подразумевается, например, что активирован сервис [Tracy |tracy:], который является удивительным инструментом для регистрации или визуализации ошибок. Он регистрирует ошибки на рабочем сервере и отображает их непосредственно на сервере разработки. Поэтому при инициализации также необходимо решить, работает ли сайт в производственном режиме или в режиме разработчика. Для этого Nette использует автоопределение: если вы запускаете сайт на localhost, он работает в режиме разработчика. Вам не нужно ничего настраивать, и приложение готово как для разработки, так и для производственного развертывания. Эти шаги выполняются и подробно описываются в главе [Bootstrap |bootstrap].

Третий пункт (да, мы пропустили второй, но мы к нему вернемся) — это запуск приложения. Обработкой HTTP-запросов в Nette занимается класс `Nette\Application\Application` (далее `Application`), поэтому когда мы говорим «запустить приложение», мы подразумеваем вызов метода с именем `run()` на объекте этого класса.

Nette — это наставник, который направляет вас к написанию чистых приложений по проверенным методологиям. И самая проверенная из них называется **внедрение зависимостей**, сокращенно DI. В данный момент мы не хотим обременять вас объяснением DI, поскольку этому посвящена [отдельная глава |dependency-injection:introduction], здесь важно то, что ключевые объекты обычно создаются фабрикой объектов, называемой **DI-контейнер** (сокращенно DIC). Да, это та самая фабрика, о которой говорилось некоторое время назад. И она также создает для нас объект `Application`, поэтому сначала нам нужен контейнер. Мы получаем его с помощью класса `Configurator` и позволяем ему создать объект `Application`, вызываем метод `run()` и это запускает приложение Nette. Именно это и происходит в файле [index.php |bootstrap#index-php].


Приложение Nette .[#toc-nette-application]
==========================================

Класс Application имеет единственную задачу: для ответа на HTTP-запрос.

Приложения, написанные на Nette, разделены на множество так называемых презентеров (в других фреймворках вы можете встретить термин *контроллер*, что одно и то же), которые являются классами, представляющими конкретную страницу сайта: например, домашняя страница; товар в электронном магазине; регистрационная форма; rss-карта и т. д. В приложении может быть от одного до тысячи презентеров.

Приложение начинает работу с того, что просит так называемый маршрутизатор решить, какому из презентеров передать текущий запрос на обработку. Маршрутизатор решает, чья это ответственность. Он просматривает входной URL `https://example.com/product/123` и, основываясь на том, как он настроен, решает, что это задание, например, для **презентера** `Product`, который хочет `показать` продукт с `id: 123` как действие. Хорошей привычкой является написание пар презентер + действие, разделенных двоеточием: `Продукт:показать`.

Поэтому маршрутизатор преобразовал URL в пару `Presenter:action` + параметры, в нашем случае `Product:show` + `id`: 123`. Вы можете увидеть, как выглядит маршрутизатор в файле `app/Core/RouterFactory.php`, и мы подробно опишем его в главе [Маршрутизация |routing].

Давайте двигаться дальше. Приложение уже знает имя презентера и может продолжить работу. Путем создания объекта `ProductPresenter`, который является кодом презентера `Product`. Точнее, он просит контейнер DI создать презентера, потому что создание объектов — это его работа.

Презентер может выглядеть следующим образом:

```php
class ProductPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private ProductRepository $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);
	}
}
```

Запрос обрабатывается презентером. И задача ясна: выполнить действие `show` с `id: 123`. Что на языке презентеров означает — вызвать метод `renderShow()` с параметрем `$id` равным `123`.

Презентер может обрабатывать несколько действий, то есть иметь несколько методов `render<Action>()`. Но мы рекомендуем разрабатывать презентеры с одним или как можно меньшим количеством действий.

Итак, был вызван метод `renderShow(123)`, код которого является вымышленным примером, но на нем можно увидеть, как данные передаются в шаблон, то есть путем записи в `$this->template`.

После этого презентер возвращает ответ. Это может быть HTML-страница, изображение, XML-документ, отправка файла с диска, JSON или перенаправление на другую страницу. Важно отметить, что если мы явно не указываем, как реагировать (что имеет место в случае с `ProductPresenter`), ответом будет отображение шаблона с HTML-страницей. Почему? Ну, потому что в 99% случаев мы хотим отобразить шаблон, поэтому презентер принимает такое поведение по умолчанию и хочет облегчить нашу работу. Это точка зрения Nette.

Нам даже не нужно указывать, какой шаблон рендерить, фреймворк сам определит путь. В случае с действием `show` он просто попытается загрузить шаблон `show.latte` в директории с классом `ProductPresenter`. Он также попытается найти макет в файле `@layout.latte` (подробнее о [поиске шаблонов |templates#Template Lookup]).

Затем происходит рендеринг шаблонов. На этом задача ведущего и всего приложения завершена, и работа закончена. Если шаблон не существует, будет возвращена страница с ошибкой 404. Подробнее о презентерах можно прочитать на странице [Презентеры |presenters].

[* request-flow.svg *]

Чтобы убедиться в этом, давайте попробуем повторить весь процесс, используя немного другой URL:

1) URL будет `https://example.com`
2) мы загружаем приложение, создаем контейнер и запускаем `Application::run()`
3) маршрутизатор декодирует URL как пару `Home:default`
4) создается объект `HomePresenter`
5) вызывается метод `renderDefault()` (если существует)
6) шаблон `default.latte` с макетом `@layout.latte` отрисован


Возможно, сейчас вы столкнулись с множеством новых понятий, но мы считаем, что они имеют смысл. Создавать приложения в Nette — проще простого.


Шаблоны .[#toc-templates]
=========================

Что касается шаблонов, Nette использует систему шаблонов [Latte |latte:]. Поэтому файлы с шаблонами заканчиваются на `.latte`. Latte используется потому, что это самая безопасная система шаблонов для PHP, и в то же время самая интуитивно понятная. Вам не нужно изучать много нового, достаточно знать PHP и несколько тегов Latte. Вы узнаете всё [в документации |templates].

В шаблоне мы [создаем ссылки |creating-links] на других презентеров и действия следующим образом:

```latte
<a n:href="Product:show $productId">страница товара</a>
```

Просто напишите знакомую пару `Presenter:action` вместо реального URL и включите любые параметры. Хитрость заключается в `n:href`, который говорит, что этот атрибут будет обрабатываться Nette. И он будет генерировать:

```latte
<a href="/product/456">страница товара</a>
```

Ранее упомянутый маршрутизатор отвечает за генерацию URL. Фактически, маршрутизаторы в Nette уникальны тем, что они могут выполнять не только преобразования из URL в пару презентер:действие, но и наоборот — генерировать URL из имени презентер + действие + параметры.
Благодаря этому в Nette вы можете полностью изменить форму URL во всем готовом приложении, не меняя ни одного символа в шаблоне или презентере, просто модифицировав маршрутизатор.
И благодаря этому работает так называемая канонизация — ещё одна уникальная особенность Nette, которая улучшает SEO путем автоматического предотвращения существования дублированного контента на разных URL.
Многие программисты находят это удивительным.


Интерактивные компоненты .[#toc-interactive-components]
=======================================================

Мы хотим рассказать вам ещё кое-что о презентерах: они имеют встроенную систему компонентов. Те, кто постарше, могут помнить нечто подобное из Delphi или ASP.NET Web Forms. React или Vue.js построены на чем-то отдаленно похожем. В мире PHP-фреймворков это совершенно уникальная функция.

Компоненты — это отдельные многократно используемые блоки, которые мы помещаем в страницы (т. е. в презентеры). Это могут быть [формы |forms:in-presenter], [сетки данных |https://componette.org/contributte/datagrid/], меню, опросы, в общем, всё, что имеет смысл использовать многократно. Мы можем создавать собственные компоненты или использовать некоторые из [огромного количества |https://componette.org] компонентов с открытым исходным кодом.

Компоненты в корне меняют подход к разработке приложений. Они откроют новые возможности для создания страниц из заранее заданных блоков. И у них есть что-то общее с [Голливудом|components#Hollywood-Style].


Контейнер DI и конфигурация .[#toc-di-container-and-configuration]
==================================================================

DI-контейнер (фабрика для объектов) — это сердце всего приложения.

Не волнуйтесь, это не магический черный ящик, как может показаться из предыдущих слов. На самом деле, это один довольно скучный PHP-класс, сгенерированный Nette и хранящийся в каталоге кэша. Он имеет множество методов, названных `createServiceAbcd()`, и каждый из них создает и возвращает объект. Да, есть также метод `createServiceApplication()`, который создаёт `Nette\Application\Application`, который нам понадобился в файле `index.php` для запуска приложения. Существуют также методы подготовки индивидуальных презентеров. И так далее.

Объекты, которые создает контейнер DI, по какой-то причине называются сервисами.

Особенность этого класса в том, что он программируется не вами, а фреймворком. Он фактически генерирует PHP-код и сохраняет его на диске. Вы просто даете инструкции о том, какие объекты и как именно должен производить контейнер. И эти инструкции записаны в [конфигурационных файлах |bootstrap#di-container-configuration] в [формате NEON|neon:format] и поэтому имеют расширение `.neon`.

Конфигурационные файлы используются исключительно для обучения DI-контейнера. Так, например, если я укажу `expiration: 14 days` в секции [session|http:configuration#session], контейнер DI при создании объекта `Nette\Http\Session`, представляющего сессию, вызовет его метод `setExpiration('14 days')`, и таким образом конфигурация станет реальностью.

Для вас подготовлена целая глава, описывающая, что можно [настроить |nette:configuring] и как [определить свои собственные сервисы |dependency-injection:services].

Как только вы перейдете к созданию сервисов, вы столкнетесь со словом *autowiring* (*автоподключение*). Это гаджет, который невероятно облегчит вашу жизнь. Он может автоматически передавать объекты туда, куда вам нужно (например, в конструкторы ваших классов) без необходимости что-либо делать. Вы увидите, что контейнер DI в Nette — это маленькое чудо.


Что дальше? .[#toc-what-next]
=============================

Мы рассмотрели основные принципы работы приложений в Nette. Пока очень поверхностно, но вскоре вы погрузитесь в глубины и в итоге создадите замечательные веб-приложения. Где продолжить? Вы пробовали изучить учебник [Создайте свое первое приложение |quickstart:]?

В дополнение к вышеперечисленному, Nette имеет целый арсенал [полезных классов |utils:], [слой базы данных |database:] и т. д. Попробуйте целенаправленно просто просмотреть документацию. Или посетите [блог |https://blog.nette.org]. Вы откроете для себя много интересного.

Пусть этот фреймворк принесёт вам много радости 💙.

Как работают приложения?

Сейчас вы читаете основной документ документации Nette. Вы узнаете все принципы работы веб-приложений. Все мелочи от А до Я, от момента рождения до последнего вздоха PHP-скрипта. После прочтения вы будете знать:

  • как все это работает
  • что такое Bootstrap, Presenter и DI контейнер
  • как выглядит структура каталогов

Структура каталога

Откройте скелетный пример веб-приложения под названием WebProject, и вы сможете наблюдать, как происходит запись файлов.

Структура каталогов выглядит примерно так:

web-project/
├── app/                      ← каталог с приложением
│   ├── Core/                 ← основные необходимые классы
│   │   └── RouterFactory.php ← настройка URL-адресов
│   ├── UI/                   ← презентаторы, шаблоны и др.
│   │   ├── @layout.latte     ← шаблон общего макета
│   │   └── Home/             ← Главная директория ведущих
│   │       ├── HomePresenter.php ← Класс презентера дома
│   │       └── default.latte ← шаблон для действий default
│   └── Bootstrap.php         ← загрузочный класс Bootstrap
├── bin/                      ← скрипты командной строки
├── config/                   ← файлы конфигурации
│   ├── common.neon
│   └── services.neon
├── log/                      ← журналы ошибок
├── temp/                     ← временные файлы, кэш, …
├── vendor/                   ← библиотеки, установленные через Composer
│   ├── ...
│   └── autoload.php          ← автозагрузчик библиотек, установленных через Composer
├── www/                      ← публичный корневой каталог проекта
│   ├── .htaccess             ← правила mod_rewrite и т. д.
│   └── index.php             ← начальный файл, запускающий приложение
└── .htaccess                 ← запрещает доступ ко всем каталогам, кроме www

Вы можете изменить структуру каталогов любым способом, переименовать или переместить папки, а затем просто отредактировать пути к log/ и temp/ в файле Bootstrap.php и путь к этому файлу в composer.json в секции autoload. Ничего больше, никакой сложной перенастройки, никаких постоянных изменений. Nette имеет интеллектуальное автоопределение.

Для немного больших приложений мы можем разделить папки с ведущими и шаблонами на подкаталоги (на диске) и на пространства имен (в коде), которые мы называем модулями.

Публичный каталог www/ может быть изменен без необходимости устанавливать что-либо ещё. На самом деле, часто бывает, что из-за специфики вашего хостинга вам придется переименовать его или, наоборот, установить так называемый document-root на этот каталог в конфигурации хостинга. Если ваш хостинг не позволяет создавать папки на один уровень выше публичного каталога, советуем вам поискать другой хостинг. В противном случае вы подвергнете себя значительному риску безопасности.

Вы также можете загрузить WebProject напрямую, включая Nette, используя Composer:

composer create-project nette/web-project

В Linux или macOS установите разрешения на запись для каталогов log/ и temp/.

Приложение WebProject готово к запуску, больше ничего настраивать не нужно, и вы можете просмотреть его прямо в браузере, обратившись к папке www/.

HTTP-запрос

Все начинается с того, что пользователь открывает страницу в браузере, а браузер стучится на сервер с HTTP-запросом. Запрос идет к PHP-файлу, расположенному в публичном каталоге www/, который называется index.php. Предположим, что это запрос на https://example.com/product/123. Благодаря соответствующим настройкам server settings, этот URL также сопоставлен с файлом index.php и будет выполнен.

Его задача состоит в следующем:

  1. инициализация среды
  2. получение фабрики
  3. запуск приложения Nette, которое обрабатывает запрос

Что за фабрика? Мы производим не тракторы, а веб-сайты! Подождите, сейчас всё будет объяснено.

Под «инициализацией среды» подразумевается, например, что активирован сервис Tracy, который является удивительным инструментом для регистрации или визуализации ошибок. Он регистрирует ошибки на рабочем сервере и отображает их непосредственно на сервере разработки. Поэтому при инициализации также необходимо решить, работает ли сайт в производственном режиме или в режиме разработчика. Для этого Nette использует автоопределение: если вы запускаете сайт на localhost, он работает в режиме разработчика. Вам не нужно ничего настраивать, и приложение готово как для разработки, так и для производственного развертывания. Эти шаги выполняются и подробно описываются в главе Bootstrap.

Третий пункт (да, мы пропустили второй, но мы к нему вернемся) — это запуск приложения. Обработкой HTTP-запросов в Nette занимается класс Nette\Application\Application (далее Application), поэтому когда мы говорим «запустить приложение», мы подразумеваем вызов метода с именем run() на объекте этого класса.

Nette — это наставник, который направляет вас к написанию чистых приложений по проверенным методологиям. И самая проверенная из них называется внедрение зависимостей, сокращенно DI. В данный момент мы не хотим обременять вас объяснением DI, поскольку этому посвящена отдельная глава, здесь важно то, что ключевые объекты обычно создаются фабрикой объектов, называемой DI-контейнер (сокращенно DIC). Да, это та самая фабрика, о которой говорилось некоторое время назад. И она также создает для нас объект Application, поэтому сначала нам нужен контейнер. Мы получаем его с помощью класса Configurator и позволяем ему создать объект Application, вызываем метод run() и это запускает приложение Nette. Именно это и происходит в файле index.php.

Приложение Nette

Класс Application имеет единственную задачу: для ответа на HTTP-запрос.

Приложения, написанные на Nette, разделены на множество так называемых презентеров (в других фреймворках вы можете встретить термин контроллер, что одно и то же), которые являются классами, представляющими конкретную страницу сайта: например, домашняя страница; товар в электронном магазине; регистрационная форма; rss-карта и т. д. В приложении может быть от одного до тысячи презентеров.

Приложение начинает работу с того, что просит так называемый маршрутизатор решить, какому из презентеров передать текущий запрос на обработку. Маршрутизатор решает, чья это ответственность. Он просматривает входной URL https://example.com/product/123 и, основываясь на том, как он настроен, решает, что это задание, например, для презентера Product, который хочет показать продукт с id: 123 как действие. Хорошей привычкой является написание пар презентер + действие, разделенных двоеточием: Продукт:показать.

Поэтому маршрутизатор преобразовал URL в пару Presenter:action + параметры, в нашем случае Product:show + id: 123. Вы можете увидеть, как выглядит маршрутизатор в файле `app/Core/RouterFactory.php, и мы подробно опишем его в главе Маршрутизация.

Давайте двигаться дальше. Приложение уже знает имя презентера и может продолжить работу. Путем создания объекта ProductPresenter, который является кодом презентера Product. Точнее, он просит контейнер DI создать презентера, потому что создание объектов — это его работа.

Презентер может выглядеть следующим образом:

class ProductPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private ProductRepository $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);
	}
}

Запрос обрабатывается презентером. И задача ясна: выполнить действие show с id: 123. Что на языке презентеров означает — вызвать метод renderShow() с параметрем $id равным 123.

Презентер может обрабатывать несколько действий, то есть иметь несколько методов render<Action>(). Но мы рекомендуем разрабатывать презентеры с одним или как можно меньшим количеством действий.

Итак, был вызван метод renderShow(123), код которого является вымышленным примером, но на нем можно увидеть, как данные передаются в шаблон, то есть путем записи в $this->template.

После этого презентер возвращает ответ. Это может быть HTML-страница, изображение, XML-документ, отправка файла с диска, JSON или перенаправление на другую страницу. Важно отметить, что если мы явно не указываем, как реагировать (что имеет место в случае с ProductPresenter), ответом будет отображение шаблона с HTML-страницей. Почему? Ну, потому что в 99% случаев мы хотим отобразить шаблон, поэтому презентер принимает такое поведение по умолчанию и хочет облегчить нашу работу. Это точка зрения Nette.

Нам даже не нужно указывать, какой шаблон рендерить, фреймворк сам определит путь. В случае с действием show он просто попытается загрузить шаблон show.latte в директории с классом ProductPresenter. Он также попытается найти макет в файле @layout.latte (подробнее о поиске шаблонов).

Затем происходит рендеринг шаблонов. На этом задача ведущего и всего приложения завершена, и работа закончена. Если шаблон не существует, будет возвращена страница с ошибкой 404. Подробнее о презентерах можно прочитать на странице Презентеры.

Чтобы убедиться в этом, давайте попробуем повторить весь процесс, используя немного другой URL:

  1. URL будет https://example.com
  2. мы загружаем приложение, создаем контейнер и запускаем Application::run()
  3. маршрутизатор декодирует URL как пару Home:default
  4. создается объект HomePresenter
  5. вызывается метод renderDefault() (если существует)
  6. шаблон default.latte с макетом @layout.latte отрисован

Возможно, сейчас вы столкнулись с множеством новых понятий, но мы считаем, что они имеют смысл. Создавать приложения в Nette — проще простого.

Шаблоны

Что касается шаблонов, Nette использует систему шаблонов Latte. Поэтому файлы с шаблонами заканчиваются на .latte. Latte используется потому, что это самая безопасная система шаблонов для PHP, и в то же время самая интуитивно понятная. Вам не нужно изучать много нового, достаточно знать PHP и несколько тегов Latte. Вы узнаете всё в документации.

В шаблоне мы создаем ссылки на других презентеров и действия следующим образом:

<a n:href="Product:show $productId">страница товара</a>

Просто напишите знакомую пару Presenter:action вместо реального URL и включите любые параметры. Хитрость заключается в n:href, который говорит, что этот атрибут будет обрабатываться Nette. И он будет генерировать:

<a href="/product/456">страница товара</a>

Ранее упомянутый маршрутизатор отвечает за генерацию URL. Фактически, маршрутизаторы в Nette уникальны тем, что они могут выполнять не только преобразования из URL в пару презентер:действие, но и наоборот — генерировать URL из имени презентер + действие + параметры. Благодаря этому в Nette вы можете полностью изменить форму URL во всем готовом приложении, не меняя ни одного символа в шаблоне или презентере, просто модифицировав маршрутизатор. И благодаря этому работает так называемая канонизация — ещё одна уникальная особенность Nette, которая улучшает SEO путем автоматического предотвращения существования дублированного контента на разных URL. Многие программисты находят это удивительным.

Интерактивные компоненты

Мы хотим рассказать вам ещё кое-что о презентерах: они имеют встроенную систему компонентов. Те, кто постарше, могут помнить нечто подобное из Delphi или ASP.NET Web Forms. React или Vue.js построены на чем-то отдаленно похожем. В мире PHP-фреймворков это совершенно уникальная функция.

Компоненты — это отдельные многократно используемые блоки, которые мы помещаем в страницы (т. е. в презентеры). Это могут быть формы, сетки данных, меню, опросы, в общем, всё, что имеет смысл использовать многократно. Мы можем создавать собственные компоненты или использовать некоторые из огромного количества компонентов с открытым исходным кодом.

Компоненты в корне меняют подход к разработке приложений. Они откроют новые возможности для создания страниц из заранее заданных блоков. И у них есть что-то общее с Голливудом.

Контейнер DI и конфигурация

DI-контейнер (фабрика для объектов) — это сердце всего приложения.

Не волнуйтесь, это не магический черный ящик, как может показаться из предыдущих слов. На самом деле, это один довольно скучный PHP-класс, сгенерированный Nette и хранящийся в каталоге кэша. Он имеет множество методов, названных createServiceAbcd(), и каждый из них создает и возвращает объект. Да, есть также метод createServiceApplication(), который создаёт Nette\Application\Application, который нам понадобился в файле index.php для запуска приложения. Существуют также методы подготовки индивидуальных презентеров. И так далее.

Объекты, которые создает контейнер DI, по какой-то причине называются сервисами.

Особенность этого класса в том, что он программируется не вами, а фреймворком. Он фактически генерирует PHP-код и сохраняет его на диске. Вы просто даете инструкции о том, какие объекты и как именно должен производить контейнер. И эти инструкции записаны в конфигурационных файлах в формате NEON и поэтому имеют расширение .neon.

Конфигурационные файлы используются исключительно для обучения DI-контейнера. Так, например, если я укажу expiration: 14 days в секции session, контейнер DI при создании объекта Nette\Http\Session, представляющего сессию, вызовет его метод setExpiration('14 days'), и таким образом конфигурация станет реальностью.

Для вас подготовлена целая глава, описывающая, что можно настроить и как определить свои собственные сервисы.

Как только вы перейдете к созданию сервисов, вы столкнетесь со словом autowiring (автоподключение). Это гаджет, который невероятно облегчит вашу жизнь. Он может автоматически передавать объекты туда, куда вам нужно (например, в конструкторы ваших классов) без необходимости что-либо делать. Вы увидите, что контейнер DI в Nette — это маленькое чудо.

Что дальше?

Мы рассмотрели основные принципы работы приложений в Nette. Пока очень поверхностно, но вскоре вы погрузитесь в глубины и в итоге создадите замечательные веб-приложения. Где продолжить? Вы пробовали изучить учебник Создайте свое первое приложение?

В дополнение к вышеперечисленному, Nette имеет целый арсенал полезных классов, слой базы данных и т. д. Попробуйте целенаправленно просто просмотреть документацию. Или посетите блог. Вы откроете для себя много интересного.

Пусть этот фреймворк принесёт вам много радости 💙.