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

В момента четете основния документ на Nette. Ще научите всички принципи на работа на уеб приложенията. Всички подробности от А до Я, от момента на раждането до последния дъх на PHP скрипта. След като я прочетете, ще разберете:

  • Как работи всичко това.
  • какво представляват контейнерите Bootstrap, Presenter и DI
  • как изглежда структурата на директорията

Структура на директорията

Отворете пример за скелет на уеб приложение, наречено WebProject, и можете да наблюдавате как се записват файловете.

Структурата на директорията изглежда по следния начин:

web-project/
├── app/                      ← каталог с приложением
│   ├── Presenters/           ← классы презентеров
│   │   ├── HomePresenter.php  ← Класс презентера главной страницы
│   │   └── templates/        ← директория шаблонов
│   │       ├── @layout.latte ← шаблон общего макета
│   │       └── Home/         ← шаблоны презентера главной страницы
│   │           └── default.latte  ← шаблон действия `default`
│   ├── Router/               ← конфигурация URL-адресов
│   └── 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 и ще бъде изпълнен.

Задачата му е следната:

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

Какъв вид фабрика? Ние не произвеждаме трактори, а уебсайтове! Изчакайте, след малко ще бъде обяснено.

Под “инициализиране на средата” разбираме например активирането на услугата Tracy, която е невероятен инструмент за регистриране или визуализиране на грешки. Той регистрира грешките на производствения сървър и ги показва директно на сървъра за разработка. Затова по време на инициализацията трябва да решите дали сайтът ще работи в производствен режим или в режим за разработчици. Nette използва автоматично откриване за това: ако стартирате сайта на localhost, той се стартира в режим за разработчици. Не е необходимо да конфигурирате каквото и да било и приложението е готово както за разработка, така и за внедряване в производството. Тези стъпки се следват и са описани подробно в главата Bootstrap.

Третата точка (да, пропуснахме втората, но ще се върнем към нея) е да стартирате приложението. Класът Nette\Application\Application (наричан по-нататък Application) обработва HTTP заявките в Nette, така че когато казваме “стартиране на приложение”, имаме предвид извикване на метод с име run() върху обект от този клас.

Nette е наставник, който ви напътства да пишете чисти приложения според доказани методологии. Най-проверяваният от тях се нарича имплементиране на зависимости, накратко DI. На този етап не искаме да ви натоварваме с обяснение на DI, тъй като това е разгледано в отделна глава, важното тук е, че ключовите обекти обикновено се създават от фабрика за обекти, наречена DI контейнер (съкратено DIC). Да, това е същата фабрика, която беше спомената преди време. Освен това той създава обекта Application за нас, така че първо се нуждаем от контейнер. Получаваме го с класа Configurator и му позволяваме да създаде обекта Application, да извика метода run() и това стартира приложението Nette. Точно това се случва във файла index.php.

Приложението Nette

Класът Application има една единствена задача: да отговори на HTTP заявка.

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

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

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

Да продължим. Приложението вече знае името на водещия и може да продължи. Чрез създаване на обект 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. На езика preenter това означава да се извика методът renderShow() с параметър $id, равен на 123.

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

Така че методът renderShow(123), чийто код е измислен пример, е извикан, но на него можете да видите как данните се прехвърлят към шаблона, т.е. чрез запис в $this->template.

След това водещият връща отговор. Това може да бъде HTML страница, изображение, XML документ, файл, изпратен от диска, JSON или пренасочване към друга страница. Важно е да се отбележи, че ако не посочим изрично как да се отговори (какъвто е случаят с ProductPresenter), отговорът ще бъде шаблон, показващ HTML страница. Защо? Ами защото в 99% от случаите искаме да покажем шаблон, водещият приема това поведение по подразбиране и иска да улесни работата ни. Това е гледната точка на Нете.

Дори не е необходимо да указваме кой шаблон да се покаже, той сам извежда пътя до него според проста логика. В случая с водещия Product и действието show, той се опитва да провери дали някой от тези файлове с шаблони съществува спрямо директорията, в която се намира класът ProductPresenter:

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

След това се показва шаблонът. Задачата на водещия и на цялото приложение вече е изпълнена. Ако шаблонът не съществува, ще бъде върната страница за грешка 404. Можете да прочетете повече за водещите на страницата Водещи.

За да сме сигурни, нека повторим целия процес, като използваме малко по-различен URL адрес:

  1. URL адресът ще https://example.com
  2. изтегляме приложението, създаваме контейнер и стартираме Application::run()
  3. маршрутизаторът декодира URL адреса като двойка Home:default
  4. обектът е създаден HomePresenter
  5. извиква се методът renderDefault() (ако съществува)
  6. шаблонът templates/Home/default.latte с оформлението templates/@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 в раздела за сесията, контейнерът DI ще извика своя метод setExpiration('14 days') при създаването на обекта Nette\Http\Session, представляващ сесията, и по този начин конфигурацията ще стане реалност.

За вас е подготвена цяла глава, в която се описва какво можете да конфигурирате и как да дефинирате собствени услуги.

След като стигнете до създаването на услуги, ще срещнете думата autowiring. Това е притурка, която ще улесни живота ви изключително много. Той може автоматично да предава обекти, когато са ви необходими (например към конструкторите на класа), без да е необходимо да правите каквото и да било. Ще видите, че контейнерът DI в Nette е малко чудо.

Какво следва?

Обхванахме основите на работата на приложенията в Nette. Засега много повърхностно, но скоро ще навлезете в дълбочина и ще създадете страхотни уеб приложения. Къде да продължим? Опитахте ли урока Създаване на първото ви приложение?

В допълнение към горното Nette разполага с цял арсенал от полезни класове, слой за бази данни и др. Опитайте се целенасочено да прегледате документацията. Или посетете блога. Ще откриете много интересни неща.

Нека тази рамка ви донесе много радост 💙.

версия: 4.0