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-контейнера.

Можете да прочетете повече за разгръщането на зависимостите тук и за конфигурацията тук.