Nette Documentation Preview

syntax
Модель
******

У міру зростання нашого додатка ми незабаром виявляємо, що нам необхідно виконувати аналогічні операції з базою даних у різних місцях і в різних презентерах. Наприклад, отримувати найновіші опубліковані статті. Якщо ми покращимо наш застосунок, додавши до статей прапор, що вказує на стан готовності, ми також повинні пройтися всіма місцями в нашому застосунку та додати умову *where*, щоб переконатися, що вибираються тільки готові статті.

На цьому етапі прямої роботи з базою даних стає недостатньо, і розумніше буде допомогти собі новою функцією, що повертає опубліковані статті. І коли пізніше ми додамо ще один пункт (наприклад, не відображати статті з майбутньою датою), ми редагуватимемо наш код тільки в одному місці.

Ми помістимо функцію в клас `PostFacade` і назвемо її `getPublicArticles()`.

Створимо наш клас моделі `PostFacade` у директорії `app/Model/`, щоб подбати про наші статті. Давайте помістимо його у файл `PostFacade.php`.

```php .{file:app/Model/PostFacade.php}
<?php
namespace App\Model;

use Nette;

final class PostFacade
{
	public function __construct(
		private Nette\Database\Explorer $database,
	) {
	}

	public function getPublicArticles()
	{
		return $this->database
			->table('posts')
			->where('created_at < ', new \DateTime)
			->order('created_at DESC');
	}
}
```

У цьому класі ми передаємо базу даних Explorer:[api:Nette\Database\Explorer]. Це дасть змогу використовувати можливості [DI-контейнера |dependency-injection:passing-dependencies].

Перейдемо до файлу `HomePresenter.php`, який ми відредагуємо так, щоб позбутися залежності від `Nette\Database\Explorer`, замінивши її новою залежністю від нашого створеного класу.

```php .{file:app/UI/Home/HomePresenter.php}
<?php
namespace App\UI\Home;

use App\Model\PostFacade;
use Nette;

final class HomePresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private PostFacade $facade,
	) {
	}

	public function renderDefault(): void
	{
		$this->template->posts = $this->facade
			->getPublicArticles()
			->limit(5);
	}
}
```

У секції `use` ми використовуємо `App\Model\PostFacade`. Таким чином можна скоротити наш PHP-код тільки до PostFacade (не бійтеся, він працює навіть у коментарях, і ваша розумна IDE повинна бути в змозі впоратися з цим).

Залишився останній крок - навчити DI-контейнер створювати цей об'єкт. Зазвичай це робиться шляхом додавання пункту до файлу `config/services.neon` у секції `services` із зазначенням повного імені класу та параметрів конструктора.
Це, так би мовити, реєструє його, і об'єкт потім називається **сервіс**. Завдяки деякій магії, яка називається [autowiring |dependency-injection:autowiring], нам зазвичай не потрібно вказувати параметри конструктора, оскільки DI розпізнає їх і передає автоматично. Таким чином, достатньо просто вказати ім'я класу:

```neon .{file:config/services.neon}
...

services:
	- App\Model\PostFacade
```

Однак, додавати цей рядок також не обов'язково. У секції `search` на початку `services.neon` визначено, що всі класи, які закінчуються на `-Facade` або `-Factory`, шукатимуться DI автоматично, що також стосується і `PostFacade`.


Підіб'ємо підсумок .[#toc-summary]
==================================

Клас `PostFacade` запитує `Nette\Database\Explorer` у конструкторі, і оскільки цей клас зареєстрований у контейнері DI, контейнер створює цей екземпляр і передає його. DI таким чином створює для нас екземпляр PostFacade і передає його в конструкторі класу HomePresenter, який його запросив. Свого роду матрьошка коду. :) Усі компоненти запитують тільки те, що їм потрібно, і їх не хвилює, де і як це буде створено. Створенням займається DI-контейнер.

.[note]
Детальніше про впровадження залежностей можна прочитати [тут |dependency-injection:introduction], а про конфігурацію - [тут |nette:configuring]

{{priority: -1}}
{{sitename: Быстрый старт с Nette}}

Модель

У міру зростання нашого додатка ми незабаром виявляємо, що нам необхідно виконувати аналогічні операції з базою даних у різних місцях і в різних презентерах. Наприклад, отримувати найновіші опубліковані статті. Якщо ми покращимо наш застосунок, додавши до статей прапор, що вказує на стан готовності, ми також повинні пройтися всіма місцями в нашому застосунку та додати умову where, щоб переконатися, що вибираються тільки готові статті.

На цьому етапі прямої роботи з базою даних стає недостатньо, і розумніше буде допомогти собі новою функцією, що повертає опубліковані статті. І коли пізніше ми додамо ще один пункт (наприклад, не відображати статті з майбутньою датою), ми редагуватимемо наш код тільки в одному місці.

Ми помістимо функцію в клас PostFacade і назвемо її getPublicArticles().

Створимо наш клас моделі PostFacade у директорії app/Model/, щоб подбати про наші статті. Давайте помістимо його у файл PostFacade.php.

<?php
namespace App\Model;

use Nette;

final class PostFacade
{
	public function __construct(
		private Nette\Database\Explorer $database,
	) {
	}

	public function getPublicArticles()
	{
		return $this->database
			->table('posts')
			->where('created_at < ', new \DateTime)
			->order('created_at DESC');
	}
}

У цьому класі ми передаємо базу даних Explorer. Це дасть змогу використовувати можливості DI-контейнера.

Перейдемо до файлу HomePresenter.php, який ми відредагуємо так, щоб позбутися залежності від Nette\Database\Explorer, замінивши її новою залежністю від нашого створеного класу.

<?php
namespace App\UI\Home;

use App\Model\PostFacade;
use Nette;

final class HomePresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private PostFacade $facade,
	) {
	}

	public function renderDefault(): void
	{
		$this->template->posts = $this->facade
			->getPublicArticles()
			->limit(5);
	}
}

У секції use ми використовуємо App\Model\PostFacade. Таким чином можна скоротити наш PHP-код тільки до PostFacade (не бійтеся, він працює навіть у коментарях, і ваша розумна IDE повинна бути в змозі впоратися з цим).

Залишився останній крок – навчити DI-контейнер створювати цей об'єкт. Зазвичай це робиться шляхом додавання пункту до файлу config/services.neon у секції services із зазначенням повного імені класу та параметрів конструктора. Це, так би мовити, реєструє його, і об'єкт потім називається сервіс. Завдяки деякій магії, яка називається autowiring, нам зазвичай не потрібно вказувати параметри конструктора, оскільки DI розпізнає їх і передає автоматично. Таким чином, достатньо просто вказати ім'я класу:

...

services:
	- App\Model\PostFacade

Однак, додавати цей рядок також не обов'язково. У секції search на початку services.neon визначено, що всі класи, які закінчуються на -Facade або -Factory, шукатимуться DI автоматично, що також стосується і PostFacade.

Підіб'ємо підсумок

Клас PostFacade запитує Nette\Database\Explorer у конструкторі, і оскільки цей клас зареєстрований у контейнері DI, контейнер створює цей екземпляр і передає його. DI таким чином створює для нас екземпляр PostFacade і передає його в конструкторі класу HomePresenter, який його запросив. Свого роду матрьошка коду. :) Усі компоненти запитують тільки те, що їм потрібно, і їх не хвилює, де і як це буде створено. Створенням займається DI-контейнер.

Детальніше про впровадження залежностей можна прочитати тут, а про конфігурацію – тут