Nette Documentation Preview

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

<div class=perex>

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

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

</div>


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

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

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

/--pre
<b>web-project/</b>
├── <b>app/</b>                      ← каталог приложения
│   ├── <b>Core/</b>                 ← базовые классы, необходимые для работы
│   │   └── <b>RouterFactory.php</b> ← настройка URL-адресов
│   ├── <b>Presentation/</b>         ← презентеры, шаблоны и прочее
│   │   ├── <b>@layout.latte</b>     ← шаблон макета
│   │   └── <b>Home/</b>             ← каталог презентера Home
│   │       ├── <b>HomePresenter.php</b> ← класс презентера Home
│   │       └── <b>default.latte</b> ← шаблон для действия default
│   └── <b>Bootstrap.php</b>         ← стартовый класс Bootstrap
├── <b>assets/</b>                   ← ресурсы (SCSS, TypeScript, исходные изображения)
├── <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>          ← автозагрузка всех установленных пакетов
├── <b>www/</b>                      ← публичный каталог, document-root проекта
│   ├── <b>assets/</b>               ← скомпилированные статические файлы (CSS, JS, изображения, ...)
│   ├── <b>.htaccess</b>             ← правила для mod_rewrite
│   └── <b>index.php</b>             ← начальный файл, запускающий приложение
└── <b>.htaccess</b>                 ← запрещает доступ ко всем каталогам, кроме www
\--

Структуру каталогов можно изменять как угодно, переименовывать или переносить папки, она полностью гибкая. У Nette есть и умное автоопределение, которое само распознаёт расположение приложения, включая базовый URL.

Для чуть более крупных приложений мы можем разложить каталоги презентеров и шаблонов по [подкаталогам |directory-structure#Презентеры и шаблоны] и сгруппировать классы в пространства имён, которые мы называем модулями.

Каталог `www/` представляет собой публичный каталог, или document-root проекта. Вы можете его переименовать, ничего больше на стороне приложения настраивать не придётся. Нужно лишь [настроить хостинг |nette:troubleshooting#Как изменить или убрать из URL каталог www?] так, чтобы document-root указывал на этот каталог.

WebProject можно скачать сразу вместе с Nette через [Composer |best-practices:composer]:

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

В Linux или macOS задайте каталогам `log/` и `temp/` [права на запись |nette:troubleshooting#Задание прав на каталоги].

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


HTTP-запрос
===========

Всё начинается с того, что пользователь открывает страницу в браузере. Браузер отправляет на сервер HTTP-запрос. Этот запрос нацелен на единственный PHP-файл в публичном каталоге `www/`, а именно на `index.php`. Допустим, запрос идёт по адресу `https://example.com/product/123`. Благодаря подходящей [настройке сервера |nette:troubleshooting#Как настроить сервер для красивых URL?] даже такой URL сопоставляется файлу `index.php`, который и выполняется.

Его задача:

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

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

Под "инициализацией окружения" мы понимаем, например, включение [Tracy|tracy:] - потрясающего инструмента для записи в лог и наглядного показа ошибок. На производственном сервере она записывает ошибки в лог, а в среде разработки показывает их прямо на экране. Поэтому инициализация включает и определение того, работает сайт в производственном режиме или в режиме разработки. Nette использует для этого [умное автоопределение |bootstrapping#Режим разработки и производственный режим]: если вы запускаете сайт на localhost, он работает в режиме разработки. Настраивать ничего не нужно, и приложение сразу готово и к разработке, и к боевому развёртыванию. Эти шаги выполняются и подробно описываются в главе о [классе Bootstrap|bootstrapping].

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

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


Nette Application
=================

У класса `Application` единственная задача: ответить на HTTP-запрос.

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

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

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

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

Презентер может выглядеть так:

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

	public function renderShow(int $id): void
	{
		// получаем данные из модели и передаём их в шаблон
		$this->template->product = $this->repository->getProduct($id);
	}
}
```

Презентер берёт обработку запроса на себя. Задача ясна: выполнить действие `show` с `id: 123`. В терминологии презентеров это значит, что вызывается метод `renderShow()`, получающий `123` в параметре `$id`.

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

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

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

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

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

[* request-flow.svg *]

Для верности повторим весь ход событий с чуть другим URL:

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


Возможно, вы только что столкнулись со множеством новых понятий, но мы верим, что они осмысленны. Разрабатывать приложения на Nette на удивление просто.


Шаблоны
=======

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

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

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

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

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

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


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

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

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

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


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

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

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

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

По-настоящему особенное в этом классе то, что вы его не программируете - это делает фреймворк. Он действительно порождает PHP-код и сохраняет его на диск. Вы лишь даёте указания, какие объекты контейнер должен уметь создавать и как именно. Эти указания записываются в [конфигурационных файлах |bootstrapping#Конфигурация DI-контейнера], которые используют формат [NEON|neon:format] и потому имеют расширение `.neon`.

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

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

Как только вы немного погрузитесь в создание сервисов, вы столкнётесь с термином [autowiring |dependency-injection:autowiring]. Это возможность, которая невероятно упростит вам жизнь. Она умеет автоматически передавать объекты туда, где они вам нужны (например, в конструкторы ваших классов), без каких-либо действий с вашей стороны. Вы обнаружите, что DI-контейнер в Nette - маленькое чудо.


Что дальше?
===========

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

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

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

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

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

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

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

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

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

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

Структуру каталогов можно изменять как угодно, переименовывать или переносить папки, она полностью гибкая. У Nette есть и умное автоопределение, которое само распознаёт расположение приложения, включая базовый URL.

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

Каталог www/ представляет собой публичный каталог, или document-root проекта. Вы можете его переименовать, ничего больше на стороне приложения настраивать не придётся. Нужно лишь настроить хостинг так, чтобы 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. Благодаря подходящей настройке сервера даже такой 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

У класса Application единственная задача: ответить на HTTP-запрос.

Приложения, написанные на Nette, делятся на множество так называемых презентеров (в других фреймворках вы можете встретить термин „контроллер“, это по сути одно и то же). Это классы, каждый из которых представляет определённую страницу сайта: например, главную страницу, товар в интернет-магазине, форму входа, ленту карты сайта и так далее. У приложения может быть от одного до тысяч презентеров.

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

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

Пойдём дальше. Application теперь знает имя презентера и может действовать. Он создаёт экземпляр класса ProductPresenter, содержащего код презентера Product. Точнее, он просит DI-контейнер создать презентер, потому что создание объектов – его обязанность.

Презентер может выглядеть так:

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

	public function renderShow(int $id): void
	{
		// получаем данные из модели и передаём их в шаблон
		$this->template->product = $this->repository->getProduct($id);
	}
}

Презентер берёт обработку запроса на себя. Задача ясна: выполнить действие show с id: 123. В терминологии презентеров это значит, что вызывается метод renderShow(), получающий 123 в параметре $id.

Презентер может обрабатывать несколько действий, то есть у него может быть несколько методов 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. Приложение стартует, создаётся DI-контейнер и выполняется Application::run().
  3. Маршрутизатор расшифровывает URL в пару Home:default.
  4. Создаётся экземпляр класса HomePresenter.
  5. Вызывается метод renderDefault() (если он существует).
  6. Отрисовывается шаблон, например default.latte, вместе с макетом, например @layout.latte.

Возможно, вы только что столкнулись со множеством новых понятий, но мы верим, что они осмысленны. Разрабатывать приложения на Nette на удивление просто.

Шаблоны

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

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

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

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

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

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

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

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

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

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

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

DI-контейнер, то есть фабрика объектов, – сердце всего приложения.

Не переживайте, это не какой-то волшебный чёрный ящик, как могли бы навести на мысль предыдущие строки. На деле это довольно обыденный PHP-класс, порождённый Nette и сохранённый в каталоге кеша. В нём много методов с именами вроде createServiceAbcd(), каждый из которых умеет создать и вернуть определённый объект. Да, там есть и метод createServiceApplication__application(), порождающий экземпляр 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 предлагает целый арсенал полезных классов, слой работы с базой данных и многое другое. Попробуйте походить по документации. Или загляните в блог. Вы обнаружите много интересного.

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