Updated configuration and DI guides Russian translation [skip ci]

This commit is contained in:
Alexander Makarov
2016-12-08 12:13:02 +03:00
parent 74f72ac663
commit 447c68bef7
2 changed files with 181 additions and 17 deletions

View File

@ -135,6 +135,29 @@ $config = [
За более подробной документацией о настройках свойства `components` в конфигурации приложения обратитесь к главам За более подробной документацией о настройках свойства `components` в конфигурации приложения обратитесь к главам
[приложения](structure-applications.md) и [Service Locator](concept-service-locator.md). [приложения](structure-applications.md) и [Service Locator](concept-service-locator.md).
Начиная с версии 2.0.11, можно настраивать [контейнер зависимостей](concept-di-container.md) через конфигурацию
приложения. Для этого используется свойство `container`:
```php
$config = [
'id' => 'basic',
'basePath' => dirname(__DIR__),
'extensions' => require(__DIR__ . '/../vendor/yiisoft/extensions.php'),
'container' => [
'definitions' => [
'yii\widgets\LinkPager' => ['maxButtonCount' => 5]
],
'singletons' => [
// Конфигурация для единожды создающихся объектов
]
]
];
```
Чтобы узнать о возможных значениях `definitions` и `singletons`, а также о реальных примерах использования,
прочитайте подраздел [более сложное практическое применение](concept-di-container.md#advanced-practical-usage) раздела
[Dependency Injection Container](concept-di-container.md).
### Конфигурации виджетов <span id="widget-configurations"></span> ### Конфигурации виджетов <span id="widget-configurations"></span>

View File

@ -104,6 +104,138 @@ $container->get('Foo', [], [
> Info: Метод [[yii\di\Container::get()]] третьим аргументом принимает массив конфигурации, которым инициализируется создаваемый объект. Если класс реализует интерфейс [[yii\base\Configurable]] (например, [[yii\base\Object]]), то массив конфигурации передается в последний параметр конструктора класса. Иначе конфигурация применяется уже *после* создания объекта. > Info: Метод [[yii\di\Container::get()]] третьим аргументом принимает массив конфигурации, которым инициализируется создаваемый объект. Если класс реализует интерфейс [[yii\base\Configurable]] (например, [[yii\base\Object]]), то массив конфигурации передается в последний параметр конструктора класса. Иначе конфигурация применяется уже *после* создания объекта.
Более сложное практическое применение <span id="advanced-practical-usage"></span>
---------------
Допустим, мы работаем над API и у нас есть:
- `app\components\Request`, наследуемый от `yii\web\Request` и реализующий дополнительные возможности.
- `app\components\Response`, наследуемый от `yii\web\Response` с свойством `format`, по умолчанию инициализируемом как `json`.
- `app\storage\FileStorage` и `app\storage\DocumentsReader`, где реализована некая логика для работы с документами в
неком файловом хранилище:
```php
class FileStorage
{
public function __contruct($root) {
// делаем что-то
}
}
class DocumentsReader
{
public function __contruct(FileStorage $fs) {
// делаем что-то
}
}
```
Возможно настроить несколько компонентов сразу передав массив конфигурации в метод
[[yii\di\Container::setDefinitions()|setDefinitions()]] или [[yii\di\Container::setSingletons()|setSingletons()]].
Внутри метода фреймворк обойдёт массив конфигурации и вызовет для каждого элемента [[yii\di\Container::set()|set()]] или
[[yii\di\Container::setSingleton()|setSingleton()]] соответственно.
Формат массива конфигурации следующий:
- Ключ: имя класса, интерфейса или псевдонима. Ключ передаётся в первый аргумент `$class` метода
[[yii\di\Container::set()|set()]].
- Значение: конфигурация для класса. Возможные значения описаны в документации параметра `$definition` метода
[[yii\di\Container::set()|set()]]. Значение передаётся в аргумент `$definition` метода [[set()]].
Для примера, давайте настроим наш контейнер:
```php
$container->setDefinitions([
'yii\web\Request' => 'app\components\Request',
'yii\web\Response' => [
'class' => 'app\components\Response',
'format' => 'json'
],
'app\storage\DocumentsReader' => function () {
$fs = new app\storage\FileStorage('/var/tempfiles');
return new app\storage\DocumentsReader($fs);
}
]);
$reader = $container->get('app\storage\DocumentsReader);
// Создаст объект DocumentReader со всеми зависимостями
```
> Tip: Начиная с версии 2.0.11 контейнер может быть настроен в декларативном стиле через конфигурацию приложения.
Как это сделать ищите в подразделе [Конфигурация приложения](concept-service-locator.md#application-configurations)
раздела [Конфигурации](concept-configurations.md).
Вроде всё работает, но если нам необходимо создать экземпляр класса `DocumentWriter`, придётся скопировать код,
создающий экземпляр`FileStorage`, что, очевидно, не является оптимальным.
Как описано в подразделе [Разрешение зависимостей](#resolving-dependencies), [[yii\di\Container::set()|set()]]
и [[yii\di\Container::setSingleton()|setSingleton()]] могут опционально принимать третьим аргументов параметры
для конструктора. Формат таков:
- Ключ: имя класса, интерфейса или псевдонима. Ключ передаётся в первый аргумент `$class` метода [[yii\di\Container::set()|set()]].
- Значение: массив из двух элементов. Первый элемент передаётся в метод [[yii\di\Container::set()|set()]] вторым
аргументом `$definition`, второй элемент — аргументом `$params`.
Исправим наш пример:
```php
$container->setDefinitions([
'tempFileStorage' => [ // для удобства мы задали псевдоним
['class' => 'app\storage\FileStorage'],
['/var/tempfiles']
],
'app\storage\DocumentsReader' => [
['class' => 'app\storage\DocumentsReader'],
[Instance::of('tempFileStorage')]
],
'app\storage\DocumentsWriter' => [
['class' => 'app\storage\DocumentsWriter'],
[Instance::of('tempFileStorage')]
]
]);
$reader = $container->get('app\storage\DocumentsReader);
// Код будет работать ровно так же, как и в предыдущем примере.
```
Вы могли заметить вызов `Instance::of('tempFileStorage')`. Он означает, что [[yii\di\Container|Container]]
наявно предоставит зависимость, зарегистрированную с именем `tempFileStorage` и передаст её первым аргументом
в конструктор `app\storage\DocumentsWriter`.
> Note: Методы [[yii\di\Container::setDefinitions()|setDefinitions()]] и [[yii\di\Container::setSingletons()|setSingletons()]]
доступны с версии 2.0.11.
Ещё один шаг по оптимизации конфигурации — регистрировать некоторые зависимости как синглтоны. Зависимость, регистрируемая
через метод [[yii\di\Container::set()|set()]] будет созаваться каждый раз при обращении к ней. Некоторые классы не меняют
своего состояния на протяжении всей работы приложения, поэтому могут быть зарегистрированы как синглтоны. Это увеличит
производительность приложения.
Хорошим примером может быть класс `app\storage\FileStorage`, который выполняет некие операции над файловой системой
через простой API: `$fs->read()`, `$fs->write()`. Обе операции не меняют внутреннее состояние класса, поэтому мы можем
создать класс один раз и далее использовать его.
```php
$container->setSingletons([
'tempFileStorage' => [
['class' => 'app\storage\FileStorage'],
['/var/tempfiles']
],
]);
$container->setDefinitions([
'app\storage\DocumentsReader' => [
['class' => 'app\storage\DocumentsReader'],
[Instance::of('tempFileStorage')]
],
'app\storage\DocumentsWriter' => [
['class' => 'app\storage\DocumentsWriter'],
[Instance::of('tempFileStorage')]
]
]);
$reader = $container->get('app\storage\DocumentsReader);
```
### Внедрение зависимости через PHP callback <span id="php-callable-injection"></span> ### Внедрение зависимости через PHP callback <span id="php-callable-injection"></span>
В данном случае, контейнер будет использовать зарегистрированный PHP callback для создания новых экземпляров класса. В данном случае, контейнер будет использовать зарегистрированный PHP callback для создания новых экземпляров класса.
@ -211,13 +343,16 @@ $container->setSingleton('yii\db\Connection', [
Разрешение зависимостей <span id="resolving-dependencies"></span> Разрешение зависимостей <span id="resolving-dependencies"></span>
---------------------- ----------------------
После регистрации зависимостей, вы можете использовать контейнер внедрения зависимостей для создания новых объектов, После регистрации зависимостей, вы можете использовать контейнер внедрения зависимостей для создания новых объектов,
и контейнер автоматически разрешит зависимости их экземпляра и их внедрений во вновь создаваемых объектах. Разрешение зависимостей рекурсивно, то есть и контейнер автоматически разрешит зависимости их экземпляра и их внедрений во вновь создаваемых объектах. Разрешение
если зависимость имеет другие зависимости, эти зависимости также будут автоматически разрешены. зависимостей рекурсивно, то есть если зависимость имеет другие зависимости, эти зависимости также будут автоматически
разрешены.
Вы можете использовать [[yii\di\Container::get()]] для создания новых объектов. Метод принимает имя зависимости, которым может быть имя класса, имя интерфейса или псевдоним. Вы можете использовать [[yii\di\Container::get()]] для создания или получения объектов. Метод принимает имя зависимости,
Имя зависимости может быть или не может быть зарегистрировано через `set()` или `setSingleton()`. которым может быть имя класса, имя интерфейса или псевдоним. Имя зависимости может быть зарегистрировано через
Вы можете опционально предоставить список параметров конструктора класса и [конфигурацию](concept-configurations.md) для настройки созданного объекта. `set()` или `setSingleton()`. Вы можете опционально предоставить список параметров конструктора класса и
Например, [конфигурацию](concept-configurations.md) для настройки созданного объекта.
Например:
```php ```php
// "db" ранее зарегистрированный псевдоним // "db" ранее зарегистрированный псевдоним
@ -228,11 +363,14 @@ $engine = $container->get('app\components\SearchEngine', [$apiKey], ['type' => 1
``` ```
За кулисами, контейнер внедрения зависимостей делает гораздо больше работы, чем просто создание нового объекта. За кулисами, контейнер внедрения зависимостей делает гораздо больше работы, чем просто создание нового объекта.
Прежде всего, контейнер, осмотрит конструктор класса, чтобы узнать имя зависимого класса или интерфейса, а затем автоматически разрешит эти зависимости рекурсивно. Прежде всего, контейнер, осмотрит конструктор класса, чтобы узнать имя зависимого класса или интерфейса, а затем
автоматически разрешит эти зависимости рекурсивно.
Следующий код демонстрирует более сложный пример. Класс `UserLister` зависит от объекта, реализующего интерфейс `UserFinderInterface`; класс `UserFinder` реализует этот интерфейс и зависит от Следующий код демонстрирует более сложный пример. Класс `UserLister` зависит от объекта, реализующего интерфейс
объекта `Connection`. Все эти зависимости были объявлены через тип подсказки параметров конструктора класса. `UserFinderInterface`; класс `UserFinder` реализует этот интерфейс и зависит от объекта `Connection`. Все эти зависимости
При регистрации зависимости через свойство, контейнер внедрения зависимостей позволяет автоматически разрешить эти зависимости и создаёт новый экземпляр `UserLister` простым вызовом `get('userLister')`. были объявлены через тип подсказки параметров конструктора класса. При регистрации зависимости через свойство, контейнер
внедрения зависимостей позволяет автоматически разрешить эти зависимости и создаёт новый экземпляр `UserLister` простым
вызовом `get('userLister')`.
```php ```php
namespace app\models; namespace app\models;
@ -291,17 +429,17 @@ $lister = new UserLister($finder);
``` ```
Практическое использование <span id="practical-usage"></span> Практическое применение <span id="practical-usage"></span>
--------------- ---------------
Yii создаёт контейнер внедрения зависимостей когда вы подключаете файл `Yii.php` во [входном скрипте](structure-entry-scripts.md) Yii создаёт контейнер внедрения зависимостей когда вы подключаете файл `Yii.php` во [входном скрипте](structure-entry-scripts.md)
вашего приложения. Контейнер внедрения зависимостей доступен через [[Yii::$container]]. При вызове [[Yii::createObject()]], вашего приложения. Контейнер внедрения зависимостей доступен через [[Yii::$container]]. При вызове [[Yii::createObject()]],
метод на самом деле вызовет метод контейнера [[yii\di\Container::get()|get()]], чтобы создать новый объект. метод на самом деле вызовет метод контейнера [[yii\di\Container::get()|get()]], чтобы создать новый объект.
Как упомянуто выше, контейнер внедрения зависимостей автоматически разрешит зависимости (если таковые имеются) и внедрит их в только что созданный объект. Как упомянуто выше, контейнер внедрения зависимостей автоматически разрешит зависимости (если таковые имеются) и внедрит их
Поскольку Yii использует [[Yii::createObject()]] в большей части кода своего ядра для создания новых объектов, это означает, получаемый объект. Поскольку Yii использует [[Yii::createObject()]] в большей части кода своего ядра для создания новых
что вы можете настроить глобальные объекты, имея дело с [[Yii::$container]]. объектов, это означает, что вы можете настроить глобальные объекты, имея дело с [[Yii::$container]].
Например, вы можете настроить по умолчанию глобальное количество кнопок в пейджере [[yii\widgets\LinkPager]]: Например, давайте настроим количество кнопок в пейджере [[yii\widgets\LinkPager]] по умолчанию глобально:
```php ```php
\Yii::$container->set('yii\widgets\LinkPager', ['maxButtonCount' => 5]); \Yii::$container->set('yii\widgets\LinkPager', ['maxButtonCount' => 5]);
@ -356,8 +494,11 @@ class HotelController extends Controller
Поскольку зависимости необходимы тогда, когда создаются новые объекты, то их регистрация должна быть сделана Поскольку зависимости необходимы тогда, когда создаются новые объекты, то их регистрация должна быть сделана
как можно раньше. Ниже приведены рекомендуемые практики: как можно раньше. Ниже приведены рекомендуемые практики:
* Если вы разработчик приложения, то вы можете зарегистрировать зависимости во [входном скрипте](structure-entry-scripts.md) вашего приложения или в скрипте, подключённого во входном скрипте. * Если вы разработчик приложения, то вы можете зарегистрировать зависимости в конфигурации вашего приложения.
* Если вы разработчик распространяемого [расширения](structure-extensions.md), то вы можете зарегистрировать зависимости в загрузочном классе расширения. Как это сделать описано в подразделе [Конфигурация приложения](concept-service-locator.md#application-configurations)
раздела [Конфигурации](concept-configurations.md).
* Если вы разработчик распространяемого [расширения](structure-extensions.md), то вы можете зарегистрировать зависимости
в загрузочном классе расширения.
Итог <span id="summary"></span> Итог <span id="summary"></span>