Nette Documentation Preview

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

По мере роста приложения мы скоро обнаружим, что в разных местах, в разных презентерах, нам нужно выполнять похожие операции с базой данных. Например, получать последние опубликованные статьи. Если мы улучшим приложение, например, добавив к статьям флаг, указывающий, является ли она черновиком, нам придется пройти по всем местам в приложении, где статьи извлекаются из базы данных, и добавить условие 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');
	}
}
```

В классе с помощью конструктора запросим передачу Database 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}}

Модель

По мере роста приложения мы скоро обнаружим, что в разных местах, в разных презентерах, нам нужно выполнять похожие операции с базой данных. Например, получать последние опубликованные статьи. Если мы улучшим приложение, например, добавив к статьям флаг, указывающий, является ли она черновиком, нам придется пройти по всем местам в приложении, где статьи извлекаются из базы данных, и добавить условие 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');
	}
}

В классе с помощью конструктора запросим передачу Database 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 и конфигурации.