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

Подробнее о внедрении зависимостей можно прочитать здесь,а о конфигурации — здесь