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

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

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

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

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

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

web-project/
├── app/                      ← каталог с приложением
│   ├── Основни/              ← основни необходими класове
│   │   └── RouterFactory.php ← конфигуриране на URL адреси
│   ├── UI/                   ← презентатори, шаблони и др.
│   │   ├── @layout.latte     ← шаблон на споделено оформление
│   │   └── Home/             ← Директория за водещи
│   │       ├── HomePresenter.php ← Клас на Home Presenter
│   │       └── 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 и ще бъде изпълнен.

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

  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/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
	{
		// получаваме данни от модела и ги предаваме на шаблона
		$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% от случаите искаме да покажем шаблон, водещият приема това поведение по подразбиране и иска да улесни работата ни. Това е гледната точка на Нете.

Дори не е необходимо да посочваме кой шаблон да се визуализира; рамката сама ще определи пътя. В случая с действието 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 в раздела за сесията, контейнерът DI ще извика своя метод setExpiration('14 days') при създаването на обекта Nette\Http\Session, представляващ сесията, и по този начин конфигурацията ще стане реалност.

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

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

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

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

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

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

версия: 4.0