Nette Documentation Preview

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

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

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

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

У директорії `app/Model/` створимо наш модельний клас `PostFacade`, який буде відповідати за статті:

```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`, який ми змінимо так, щоб позбутися залежності від `Nette\Database\Explorer` і замінимо її на нову залежність від нашого нового класу.

```php .{file:app/Presentation/Home/HomePresenter.php}
<?php
namespace App\Presentation\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`. Цей об'єкт ми запитаємо в конструкторі, запишемо його у властивість `$facade` і використаємо в методі renderDefault.

Залишився останній крок — навчити 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`.


Підсумок
========

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

.[note]
Тут ви можете прочитати більше про [dependency injection |dependency-injection:introduction] та [конфігурацію |nette:configuring].

{{priority: -1}}
{{sitename: Nette Quickstart}}

Модель

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

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

Функцію розмістимо, наприклад, у класі PostFacade і назвемо її getPublicArticles().

У директорії app/Model/ створимо наш модельний клас PostFacade, який буде відповідати за статті:

<?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, який ми змінимо так, щоб позбутися залежності від Nette\Database\Explorer і замінимо її на нову залежність від нашого нового класу.

<?php
namespace App\Presentation\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. Цей об'єкт ми запитаємо в конструкторі, запишемо його у властивість $facade і використаємо в методі renderDefault.

Залишився останній крок — навчити 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-контейнер.

Тут ви можете прочитати більше про dependency injection та конфігурацію.