Ресурсы
======

Ресурс в Yii это файл который может быть задан в Web странице. Это может быть CSS файл, JavaScript файл, изображение или видео файл и т.д. Ресурсы располагаются в Web доступных директориях и обслуживаются непосредственно Web серверами.

Желательно, управлять ресурсами программно. Например, при использовании виджета [[yii\jui\DatePicker]] в странице, автоматически включаются необходимые CSS и JavaScript файлы, вместо того чтобы просить Вас вручную найти эти файлы и включить их. И когда Вы обновляете виджет до новой версии, будут автоматически использованы новые версии файлов-ресурсов. В этом руководстве будет описана мощная возможность управления ресурсами представленная в Yii.

## Комплекты ресурсов <span id="asset-bundles"></span>

Yii управляет ресурсами как единицей *комплекта ресурсов*. Комплект ресурсов - это простой набор ресурсов расположенных в директории. Когда Вы регистрируете комплект ресурсов в [представлении](structure-views.md), в отображаемой Web странице включается набор CSS и JavaScript файлов.

## Задание Комплекта Ресурсов<span id="defining-asset-bundles"></span>

Комплект ресурсов определяется как PHP класс расширяющийся от [[yii\web\AssetBundle]]. Имя комплекта соответствует полному имени PHP класса (без ведущей обратной косой черты - backslash "\"). Класс комплекта ресурсов должен быть в состоянии [возможности автозагрузки](concept-autoloading.md). При задании комплекта ресурсов обычно указывается где ресурсы находятся, какие CSS и JavaScript файлы содержит комплект, и как комплект зависит от других комплектов.

Следующий код задаёт основной комплект ресурсов используемый в [шаблоне базового приложения](start-installation.md):

```php
<?php

namespace app\assets;

use yii\web\AssetBundle;

class AppAsset extends AssetBundle
{
    public $basePath = '@webroot';
    public $baseUrl = '@web';
    public $css = [
        'css/site.css',
    ];
    public $js = [
    ];
    public $depends = [
        'yii\web\YiiAsset',
        'yii\bootstrap\BootstrapAsset',
    ];
}
```

В коде выше класс `AppAsset` указывает, что файлы ресурса находятся в директории `@webroot`, которой соответствует URL `@web`; комплект содержит единственный CSS файл `css/site.css` и не содержит JavaScript файлов; комплект зависит от двух других комплектов: [[yii\web\YiiAsset]] и [[yii\bootstrap\BootstrapAsset]]. Более детальное объяснение о свойствах [[yii\web\AssetBundle]] может быть найдено ниже:

* [[yii\web\AssetBundle::sourcePath|sourcePath]]: задаёт корневую директорию содержащую файлы ресурса в этом комплекте. Это свойство должно быть установлено если корневая директория не доступна из Web. В противном случае, Вы должны установить [[yii\web\AssetBundle::basePath|basePath]] свойство и [[yii\web\AssetBundle::baseUrl|baseUrl]] свойство вместо текущего. Здесь могут быть использованы [псевдонимы путей](concept-aliases.md).
* [[yii\web\AssetBundle::basePath|basePath]]: задаёт Web доступную директорию, которая содержит файлы ресурсов текущего комплекта. Когда Вы задаёте свойство [[yii\web\AssetBundle::sourcePath|sourcePath]] [Менеджер ресурсов](#asset-manager) опубликует ресурсы текущего комплекта в Web доступную директорию и перезапишет соответственно данное свойство. Вы должны задать данное свойство если Ваши файлы ресурсов уже в Web доступной директории и не нужно опубликовывать ресурсы. Здесь могут быть использованы [псевдонимы путей](concept-aliases.md).
* [[yii\web\AssetBundle::baseUrl|baseUrl]]: задаёт URL соответствующий директории [[yii\web\AssetBundle::basePath|basePath]]. Также как и для [[yii\web\AssetBundle::basePath|basePath]], если Вы задаёте свойство [[yii\web\AssetBundle::sourcePath|sourcePath]] [Менеджер ресурсов](#asset-manager) опубликует ресурсы и перезапишет это свойство соответственно. Здесь могут быть использованы [псевдонимы путей](concept-aliases.md).
* [[yii\web\AssetBundle::js|js]]: массив, перечисляющий JavaScript файлы, содержащиеся в данном комплекте. Заметьте, что только прямая косая черта (forward slash - "/") может быть использована, как разделитель директорий. Каждый JavaScript файл может быть задан в одном из следующих форматов:
  - относительный путь, представленный локальным JavaScript файлом (например `js/main.js`). Актуальный путь файла может быть определён путём добавления [[yii\web\AssetManager::basePath]] к относительному пути, и актуальный URL файла может быть определён путём добавления [[yii\web\AssetManager::baseUrl]] к относительному пути.
  - абсолютный URL, представленный внешним JavaScript файлом. Например,
    `https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js` или
    `//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js`.
* [[yii\web\AssetBundle::css|css]]: массив, перечисляющий CSS файлы, содержащиеся в данном комплекте. Формат этого массива такой же, как и у [[yii\web\AssetBundle::js|js]].
* [[yii\web\AssetBundle::depends|depends]]: массив, перечисляющий имена комплектов ресурсов, от которых зависит данный комплект.
* [[yii\web\AssetBundle::jsOptions|jsOptions]]: задаёт параметры, которые будут относится к методу [[yii\web\View::registerJsFile()]], когда он вызывается для регистрации *каждого* JavaScript файла данного комплекта.
* [[yii\web\AssetBundle::cssOptions|cssOptions]]: задаёт параметры, которые будут приняты методом [[yii\web\View::registerCssFile()]], когда он вызывается для регистрации *каждого* CSS файла данного комплекта.
* [[yii\web\AssetBundle::publishOptions|publishOptions]]: задаёт параметры, которые будут приняты методом [[yii\web\AssetManager::publish()]], когда метод будет вызван, опубликуются исходные файлы ресурсов в Web директории. Этот параметр используется только в том случае, если задаётся свойство [[yii\web\AssetBundle::sourcePath|sourcePath]].

### Расположение ресурсов<span id="asset-locations"></span>

Ресурсы, в зависимости от их расположения, могут быть классифицированы как:

* исходные ресурсы: файлы ресурсов, расположенные вместе с исходным кодом PHP, которые не могут быть непосредственно доступны через Web. Для того, чтобы использовать исходные ресурсы на странице, они должны быть скопированы в Web директорию и превратиться в так называемые опубликованные ресурсы. Этот процесс называется *публикацией ресурсов*, который более подробно описан ниже
* опубликованные ресурсы: файлы ресурсов, расположенные в Web директории и, таким образом, могут быть напрямую доступны через Web.
* внешние ресурсы: файлы ресурсов, расположенные на другом Web сервере, отличного от веб-хостинга вашего приложения.

При определении класса комплекта ресурсов, если Вы задаёте свойство [[yii\web\AssetBundle::sourcePath|sourcePath]], это означает, что любые перечисленные ресурсы, используя относительные пути, будут рассматриваться как исходные ресурсы. Если Вы не задаёте данное свойство, это означает, что эти ресурсы - это опубликованные ресурсы (в этом случае Вам следует указать [[yii\web\AssetBundle::basePath|basePath]] и [[yii\web\AssetBundle::baseUrl|baseUrl]], чтобы дать знать Yii где ресурсы располагаются).

Рекомендуется размещать ресурсы, принадлежащие приложению, в Web директорию, для того, чтобы избежать не нужного процесса публикации ресурсов. Вот почему `AppAsset` в предыдущем примере задаёт [[yii\web\AssetBundle::basePath|basePath]] вместо [[yii\web\AssetBundle::sourcePath|sourcePath]].

Для [расширений](structure-extensions.md), в связи с тем, что их ресурсы располагаются вместе с их исходным кодом в директориях, которые не являются веб-доступными, необходимо указать свойство [[yii\web\AssetBundle::sourcePath|sourcePath]] при задании класса комплекта ресурсов для них.

> Note: Не используйте `@webroot/assets` как [[yii\web\AssetBundle::sourcePath|source path]]. Эта директория по умолчанию используется менеджером ресурсов [[yii\web\AssetManager|asset manager]] для сохранения файлов ресурсов, опубликованных из их исходного месторасположения. Любое содержимое этой директории расценивается как временное и может быть удалено.

### Зависимости ресурсов <span id="asset-dependencies"></span>

Когда Вы включаете несколько CSS или JavaScript файлов в Web страницу, они должны следовать в определенном порядке, <b> чтобы избежать переопределения при выдаче</b>. Например, если Вы используете виджет jQuery UI в Web странице, вы должны убедиться, что jQuery JavaScript файл был включен до jQuery UI JavaScript файла. Мы называем такой порядок зависимостью между ресурсами.

Зависимости ресурсов в основном указываются через свойство [[yii\web\AssetBundle::depends]]. Например в `AppAsset`, комплект ресурсов зависит от двух других комплектов ресурсов: [[yii\web\YiiAsset]] и [[yii\bootstrap\BootstrapAsset]], что обозначает, что CSS и JavaScript файлы `AppAsset` будут включены *после* файлов этих двух комплектов зависимостей.

Зависимости ресурсов являются также зависимыми. Это значит, что если комплект А зависит от В, который зависит от С, то А тоже зависит от С.

### Параметры ресурсов <span id="asset-options"></span>

Вы можете задать свойства [[yii\web\AssetBundle::cssOptions|cssOptions]] и [[yii\web\AssetBundle::jsOptions|jsOptions]], чтобы настроить путь для включения CSS и JavaScript файлов в страницу. Значения этих свойств будут приняты методами [[yii\web\View::registerCssFile()]] и [[yii\web\View::registerJsFile()]] соответственно, когда они (методы) вызываются [представлением](structure-views.md) происходит включение CSS и JavaScript файлов.

> Note: Параметры, заданные в комплекте класса применяются для *каждого* CSS/JavaScript-файла в комплекте. Если Вы хотите использовать различные параметры для разных файлов, Вы должны создать раздельные комплекты ресурсов, и использовать одну установку параметров для каждого комплекта.

Например, условно включим CSS файл для браузера IE9 или ниже. Для этого Вы можете использовать следующий параметр:

```php
public $cssOptions = ['condition' => 'lte IE9'];
```

Это вызовет CSS файл из комплекта, который будет включен в страницу, используя следующие HTML теги:

```html
<!--[if lte IE9]>
<link rel="stylesheet" href="path/to/foo.css">
<![endif]-->
```

Для того чтобы обернуть созданную CSS ссылку в тег `<noscript>`, Вы можете настроить `cssOptions` следующим образом:

```php
public $cssOptions = ['noscript' => true];
```

Для включения JavaScript файла в head раздел страницы (по умолчанию, JavaScript файлы включаются в конец раздела body) используйте следующий параметр:

```php
public $jsOptions = ['position' => \yii\web\View::POS_HEAD];
```

По умолчанию, когда комплект ресурсов публикуется, всё содержимое в заданной директории [[yii\web\AssetBundle::sourcePath]] будет опубликовано. Вы можете настроить это поведение, сконфигурировав свойство [[yii\web\AssetBundle::publishOptions|publishOptions]]. Например, опубликовать одну или несколько поддиректорий [[yii\web\AssetBundle::sourcePath]] в классе комплекта ресурсов Вы можете в следующим образом:

```php
<?php
namespace app\assets;

use yii\web\AssetBundle;

class FontAwesomeAsset extends AssetBundle 
{
    public $sourcePath = '@bower/font-awesome'; 
    public $css = [ 
        'css/font-awesome.min.css', 
    ]; 
    public $publishOptions = [
        'only' => [
            'fonts/*',
            'css/*',
        ]
    ];
}  
```

Более сложную логику можно реализовать с помощью переопределения `init()`. Ниже указан пример публикации поддиректорий этим способом:

```php
<?php
namespace app\assets;

use yii\web\AssetBundle;

class FontAwesomeAsset extends AssetBundle 
{
    public $sourcePath = '@bower/font-awesome'; 
    public $css = [ 
        'css/font-awesome.min.css', 
    ]; 
    
    public function init()
    {
        parent::init();
        $this->publishOptions['beforeCopy'] = function ($from, $to) {
            if (basename(dirname($from)) !== 'font-awesome') {
                return true;
            }
            $dirname = basename($from);
            return $dirname === 'fonts' || $dirname === 'css';
        };
    }
}  
```

В выше указанном примере определён комплект ресурсов для [пакета "fontawesome"](https://fontawesome.com/). Задан параметр публикации `beforeCopy`, здесь только `fonts` и `css` поддиректории будут опубликованы.

### Установка ресурсов Bower и NPM<span id="bower-npm-assets"></span>

Большинство JavaScript/CSS пакетов управляются [Bower](https://bower.io/) и/или [NPM](https://www.npmjs.com/).
В мире PHP мы испольуем Composer для управления зависимостями, но он не позволяет устанавливать пакеты Bower и NPM, просто указывая их в `composer.json`.

Чтобы получить такую возможность, нужно немного настроить Composer. Существует два варианта:

_____

##### Используя репозиторий asset-packagist

Этот способ удовлетворяет потребности большинства проектов, которым нужны Bower или NPM пакеты.

> Note: Начиная с версии 2.0.13, Basic и Advanced шаблоны приложений уже сконфигурированы для использования asset-packagist,
  так что этот раздел можно пропустить.

В файле `composer.json` вашего проекта, добавьте следующие строки:

```json
"repositories": [
    {
        "type": "composer",
        "url": "https://asset-packagist.org"
    }
]
```

Настройте [алиасы](concept-aliases.md) `@npm` и `@bower` в файле [конфигурации вашего приложения](concept-configurations.md):

```php
$config = [
    ...
    'aliases' => [
        '@bower' => '@vendor/bower-asset',
        '@npm'   => '@vendor/npm-asset',
    ],
    ...
];
```

Посетите [asset-packagist.org](https://asset-packagist.org) чтобы узнать, как это работает.

##### Используя fxp/composer-asset-plugin

По сравнению с asset-packagist, composer-asset-plugin не требует изменять конфигурацию приложения. Вместо этого, требуется
установить специальный глобальный пакет Composer, выполнив следующую команду:

```bash
composer global require "fxp/composer-asset-plugin:^1.4.1"
```

Эта команда устанавливает [composer asset plugin](https://github.com/fxpio/composer-asset-plugin) глобально,
что позволит устанавливать зависимости из Bower и NPM. После установки все проекты на вашем комьютере будут поддерживать
установку Bower и NPM пакетов, описанных в `composer.json`.

Добавьте следующие строки в `composer.json` вашего проекта, чтобы указать директории, в которые будут установлены
необходимые Bower и NPM пакеты:

```json
"extra": {
    "asset-installer-paths": {
        "npm-asset-library": "vendor/npm",
        "bower-asset-library": "vendor/bower"
    }
}
```

> Note: `fxp/composer-asset-plugin` выполняет команду `composer update` существенно дольше, по сравнению с asset-packagist.

____

После настройки Composer для поддержки Bower и NPM пакетов:

1. Исправьте файл `composer.json` Вашего приложения или расширения и включите пакет в список в раздел `require`. Следует использовать `bower-asset/PackageName` (для Bower пакетов) или `npm-asset/PackageName` (для NPM пакетов) для обращения к соответствующей библиотеке.
2. Выполните `composer update`
3. Создайте класс комплекта ресурсов и перечислите JavaScript/CSS файлы, которые Вы планируете использовать в Вашем приложении или расширении. Вы должны задать свойство [[yii\web\AssetBundle::sourcePath|sourcePath]] как `@bower/PackageName` или `@npm/PackageName`.

Это происходит потому, что Composer устанавливает Bower или NPM пакет в директорию, соответствующую этим псевдонимам.

> Note: В некоторых пакетах файлы дистрибутива могут находиться в поддиректории. В этом случае, Вы должны задать поддиректорию как значение [[yii\web\AssetBundle::sourcePath|sourcePath]]. Например, [[yii\web\JqueryAsset]] использует `@bower/jquery/dist` вместо `@bower/jquery`.


## Использование Комплекта Ресурсов<span id="using-asset-bundles"></span>

Для использования комплекта ресурсов, зарегистрируйте его в [представлении](structure-views.md) вызвав метод [[yii\web\AssetBundle::register()]]. Например, комплект ресурсов в представлении может быть зарегистрирован следующим образом:

```php
use app\assets\AppAsset;
AppAsset::register($this);  // $this - представляет собой объект представления
```

> Info: Метод [[yii\web\AssetBundle::register()]] возвращает объект комплекта ресурсов, содержащий информацию о публикуемых ресурсах, таких как [[yii\web\AssetBundle::basePath|basePath]] или [[yii\web\AssetBundle::baseUrl|baseUrl]].

Если Вы регистрируете комплект ресурсов в других местах (т.е. не в представлении), Вы должны обеспечить необходимый объект представления. Например, при регистрации комплекта ресурсов в классе [widget](structure-widgets.md), Вы можете взять за объект представления `$this->view`.

Когда комплект ресурсов регистрируется в представлении, Yii регистрирует все зависимые от него комплекты ресурсов. И, если комплект ресурсов расположен в директории не доступной из Web, то он будет опубликован в Web директории. Затем, когда представление отображает страницу, сгенерируются теги `<link>` и `<script>` для CSS и JavaScript файлов, перечисленных в регистрируемых комплектах. Порядок этих тегов определён зависимостью среди регистрируемых комплектов, и последовательность ресурсов перечислена в [[yii\web\AssetBundle::css]] и [[yii\web\AssetBundle::js]] свойствах.


### Динамические Комплекты Ресурсов <span id="dynamic-asset-bundles"></span>

Поскольку комплект ресурсов это обычный PHP класс, он может содержать дополнительную логику, связанную с ним, и может
корректировать свои внутренние параметры динамически. Например, вы можете использовать сложную JavaScript библиотеку,
которая предоставляет интернационализацию через отдельные исходные файлы: по одному на каждый поддерживаемый язык.
Таким образом, вам нужно добавить определенный '.js' файл на вашу страницу, чтобы применить перевод для библиотеки.
Этого можно достичь, переопределив метод [[yii\web\AssetBundle::init()]]:

```php
namespace app\assets;

use yii\web\AssetBundle;
use Yii;

class SophisticatedAssetBundle extends AssetBundle
{
    public $sourcePath = '/path/to/sophisticated/src';
    public $js = [
        'sophisticated.js' // file, which is always used
    ];

    public function init()
    {
        parent::init();
        $this->js[] = 'i18n/' . Yii::$app->language . '.js'; // dynamic file added
    }
}
```

Конкретный комплект ресурсов может быть также изменен через его экземпляр, возвращенный методом [[yii\web\AssetBundle::register()]].
Например:

```php
use app\assets\SophisticatedAssetBundle;
use Yii;

$bundle = SophisticatedAssetBundle::register(Yii::$app->view);
$bundle->js[] = 'i18n/' . Yii::$app->language . '.js'; // dynamic file added
```

> Замечание: несмотря на то, что динамическая корректировка комплекта ресурсов поддерживается, ее использование - это
  **плохая** практика, которая может привести к неожиданным побочных эффектам, и которой следует избегать.


### Настройка Комплектов Ресурсов <span id="customizing-asset-bundles"></span>

Yii управляет комплектами ресурсов через компонент приложения называемый `assetManager`, который реализован в [[yii\web\AssetManager]]. Путём настройки свойства [[yii\web\AssetManager::bundles]], возможно настроить поведение комплекта ресурсов. Например, комплект ресурсов [[yii\web\JqueryAsset]] по умолчанию использует `jquery.js` файл из установленного jquery Bower пакета. Для повышения доступности и производительности, можно использовать версию jquery на Google хостинге.
Это может быть достигнуто, настроив `assetManager` в конфигурации приложения следующим образом:

```php
return [
    // ...
    'components' => [
        'assetManager' => [
            'bundles' => [
                'yii\web\JqueryAsset' => [
                    'sourcePath' => null,   // не опубликовывать комплект
                    'js' => [
                        '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',
                    ]
                ],
            ],
        ],
    ],
];
```

Можно сконфигурировать несколько комплектов ресурсов аналогично через [[yii\web\AssetManager::bundles]]. Ключи массива должны быть именами класса (без впереди стоящей обратной косой черты) комплектов ресурсов, а значения массивов должны соответствовать [конфигурации массивов](concept-configurations.md).

> Совет: Можно условно выбрать, какой из ресурсов будет использован в комплекте ресурсов. Следующий пример показывает, как можно использовать в разработке окружения `jquery.js` или `jquery.min.js` в противном случае:

> ```php
> 'yii\web\JqueryAsset' => [
>     'js' => [
>         YII_ENV_DEV ? 'jquery.js' : 'jquery.min.js'
>     ]
> ],
> ```

Можно запретить один или несколько комплектов ресурсов, связав `false` с именами комплектов ресурсов, которые Вы хотите сделать недоступными. Когда Вы регистрируете недоступный комплект ресурсов в представлении, обратите внимание, что зависимость комплектов будет зарегистрирована, и представление также не включит ни один из ресурсов комплекта в отображаемую страницу. Например, для запрета [[yii\web\JqueryAsset]] можно использовать следующую конфигурацию: 


```php
return [
    // ...
    'components' => [
        'assetManager' => [
            'bundles' => [
                'yii\web\JqueryAsset' => false,
            ],
        ],
    ],
];
```

Можно также запретить *все* комплекты ресурсов, установив [[yii\web\AssetManager::bundles]] как `false`.

Имейте в виду, что настройки, установленные через [[yii\web\AssetManager::bundles]], применяются в момент создания комплекта
ресурсов, т.е. в момент срабатывания конструктора. Таким образом, любые изменения, которые произведены над экземпляром
комплекта ресурсов после этого, перекроют настройки, установленные на уровне [[yii\web\AssetManager::bundles]].
В частности, изменения, произведенные внутри метода [[yii\web\AssetBundle::init()]] или после регистрации комплекта ресурсов,
имеют приоритет над настройками `AssetManager`.
Ниже приведены примеры, в которых значения, установленные через [[yii\web\AssetManager::bundles]] не возымеют никакого эффекта:

```php
// Program source code:

namespace app\assets;

use yii\web\AssetBundle;
use Yii;

class LanguageAssetBundle extends AssetBundle
{
    // ...

    public function init()
    {
        parent::init();
        $this->baseUrl = '@web/i18n/' . Yii::$app->language; // can NOT be handled by `AssetManager`!
    }
}
// ...

$bundle = \app\assets\LargeFileAssetBundle::register(Yii::$app->view);
$bundle->baseUrl = YII_DEBUG ? '@web/large-files': '@web/large-files/minified'; // can NOT be handled by `AssetManager`!


// Application config :

return [
    // ...
    'components' => [
        'assetManager' => [
            'bundles' => [
                'app\assets\LanguageAssetBundle' => [
                    'baseUrl' => 'https://some.cdn.com/files/i18n/en' // makes NO effect!
                ],
                'app\assets\LargeFileAssetBundle' => [
                    'baseUrl' => 'https://some.cdn.com/files/large-files' // makes NO effect!
                ],
            ],
        ],
    ],
];
```


### Привязка ресурсов<span id="asset-mapping"></span>

Иногда необходимо исправить пути до файлов ресурсов, в нескольких комплектах ресурсов. Например, комплект А использует `jquery.min.js` версии 1.11.1, а комплект В использует `jquery.js` версии 2.1.1. Раньше Вы могли решить данную проблему, настраивая каждый комплект ресурсов по отдельности, но более простой способ - использовать *asset map* возможность, чтобы найти неверные ресурсы и исправить их. Сделать это можно, сконфигурировав свойство [[yii\web\AssetManager::assetMap]] следующим образом:

```php
return [
    // ...
    'components' => [
        'assetManager' => [
            'assetMap' => [
                'jquery.js' => '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',
            ],
        ],
    ],
];
```

Ключи [[yii\web\AssetManager::assetMap|assetMap]] - это имена ресурсов, которые Вы хотите исправить, а значения - это требуемые пути для ресурсов. Когда регистрируется комплект ресурсов в представлении, каждый соответствующий файл ресурса в [[yii\web\AssetBundle::css|css]] или [[yii\web\AssetBundle::js|js]] массивах будет рассмотрен в соответствии с этой привязкой. И, если какой-либо из ключей найден, как последняя часть пути до файла ресурса (путь на который начинается с [[yii\web\AssetBundle::sourcePath]] по возможности), то соответствующее значение заменит ресурс и будет зарегистрировано в представлении. Например, путь до файла ресурса `my/path/to/jquery.js` - это соответствует ключу `jquery.js`.

> Note: Ресурсы заданные только с использованием относительного пути могут использоваться в привязке ресурсов. Пути ресурсов должны быть абсолютные URLs или путь относительно [[yii\web\AssetManager::basePath]].


### Публикация Ресурсов<span id="asset-publishing"></span>

Как уже было сказано выше, если комплект ресурсов располагается в директории которая не доступна из Web, эти ресурсы будут скопированы в Web директорию, когда комплект будет зарегистрирован в представлении. Этот процесс называется *публикацией ресурсов*, его автоматически выполняет [[yii\web\AssetManager|asset manager]].

По умолчанию, ресурсы публикуются в директорию `@webroot/assets` которая соответствует URL `@web/assets`. Можно настроить это местоположение сконфигурировав свойства [[yii\web\AssetManager::basePath|basePath]] и [[yii\web\AssetManager::baseUrl|baseUrl]].

Вместо публикации ресурсов путём копирования файлов, можно рассмотреть использование символических ссылок, если Ваша операционная система или Web сервер это разрешают. Эта функция может быть включена путем установки [[yii\web\AssetManager::linkAssets|linkAssets]] в `true`.

```php
return [
    // ...
    'components' => [
        'assetManager' => [
            'linkAssets' => true,
        ],
    ],
];
```

С конфигурацией, установленной выше, менеджер ресурсов будет создавать символические ссылки на исходные пути комплекта ресурсов когда он будет публиковаться. Это быстрее, чем копирование файлов, а также может гарантировать, что опубликованные ресурсы всегда up-to-date(обновлённые/свежие).


### Перебор Кэша<span id="cache-busting"></span>

Для Web приложения запущенного в режиме продакшена, считается нормальной практикой разрешить HTTP кэширование для ресурсов и других статичных источников. Недостаток такой практики в том, что всякий раз, когда изменяется ресурс и разворачивается продакшен, пользователь может по-прежнему использовать старую версию ресурса вследствие HTTP кэширования. Чтобы избежать этого, можно использовать возможность перебора кэша, которая была добавлена в версии 2.0.3, для этого можно настроить [[yii\web\AssetManager]] следующим образом:
  
```php
return [
    // ...
    'components' => [
        'assetManager' => [
            'appendTimestamp' => true,
        ],
    ],
];
```

Делая таким образом, к URL каждого опубликованного ресурса будет добавляться временная метка его последней модификации. Например, URL для `yii.js` может выглядеть как `/assets/5515a87c/yii.js?v=1423448645"`, где параметр `v` представляет собой временную метку последней модификации файла `yii.js`. Теперь если изменить ресурс, его URL тоже будет изменен, это означает что клиент получит последнюю версию ресурса.

## Обычное Использование Комплекта Ресурсов<span id="common-asset-bundles"></span>

Код ядра Yii содержит большое количество комплектов ресурсов. Среди них, следующие комплекты широко используются и могут упоминаться в Вашем приложении или коде расширения:

- [[yii\web\YiiAsset]]: Включает основной `yii.js` файл который реализует механизм организации JavaScript кода в модулях. Также обеспечивает специальную поддержку для `data-method` и `data-confirm` атрибутов и содержит другие полезные функции.
- [[yii\web\JqueryAsset]]: Включает `jquery.js` файл из jQuery Bower пакета.
- [[yii\bootstrap\BootstrapAsset]]: Включает CSS файл из Twitter Bootstrap фреймворка.
- [[yii\bootstrap\BootstrapPluginAsset]]: Включает JavaScript файл из Twitter Bootstrap фреймворка для поддержки Bootstrap JavaScript плагинов.
- [[yii\jui\JuiAsset]]: Включает CSS и JavaScript файлы из jQuery UI библиотеки.

Если Ваш код зависит от jQuery, jQuery UI или Bootstrap, Вам необходимо использовать эти предопределенные комплекты ресурсов, а не создавать свои собственные варианты. Если параметры по умолчанию этих комплектов не удовлетворяют Вашим нуждам, Вы можете настроить их как описано в подразделе [Настройка Комплектов Ресурсов](#customizing-asset-bundles).

## Преобразование Ресурсов<span id="asset-conversion"></span>

Вместо того, чтобы напрямую писать CSS и/или JavaScript код, разработчики часто пишут его в некотором <b>расширенном синтаксисе</b> и используют специальные инструменты конвертации в CSS/JavaScript. Например, для CSS кода можно использовать [LESS](https://lesscss.org/) или [SCSS](https://sass-lang.com/); а для JavaScript можно использовать [TypeScript](https://www.typescriptlang.org/).

Можно перечислить файлы ресурсов в <b>расширенном синтаксисе</b> в [[yii\web\AssetBundle::css|css]] и [[yii\web\AssetBundle::js|js]] свойствах из комплекта ресурсов. Например,

```php
class AppAsset extends AssetBundle
{
    public $basePath = '@webroot';
    public $baseUrl = '@web';
    public $css = [
        'css/site.less',
    ];
    public $js = [
        'js/site.ts',
    ];
    public $depends = [
        'yii\web\YiiAsset',
        'yii\bootstrap\BootstrapAsset',
    ];
}
```

Когда Вы регистрируете такой комплект ресурсов в представлении, [[yii\web\AssetManager|asset manager]] автоматически запустит нужные инструменты препроцессора и конвертирует ресурсы в CSS/JavaScript, если их расширенный синтаксис распознан. Когда представление окончательно отобразит страницу, в неё будут включены файлы CSS/JavaScript, вместо оригинальных ресурсов в расширенном синтаксисе.

Yii использует имена расширений файлов для идентификации расширенного синтаксиса внутри ресурса. По умолчанию признаны следующие синтаксисы и имена расширений файлов:

- [LESS](https://lesscss.org/): `.less`
- [SCSS](https://sass-lang.com/): `.scss`
- [Stylus](https://stylus-lang.com/): `.styl`
- [CoffeeScript](https://coffeescript.org/): `.coffee`
- [TypeScript](https://www.typescriptlang.org/): `.ts`

Yii ориентируется на установленные инструменты конвертации ресурсов препроцессора. Например, используя [LESS](https://lesscss.org/), Вы должны установить команду `lessc` препроцессора.

Вы можете настроить команды препроцессора и поддерживать расширенный синтаксис сконфигурировав [[yii\web\AssetManager::converter]] следующим образом:

```php
return [
    'components' => [
        'assetManager' => [
            'converter' => [
                'class' => 'yii\web\AssetConverter',
                'commands' => [
                    'less' => ['css', 'lessc {from} {to} --no-color'],
                    'ts' => ['js', 'tsc --out {to} {from}'],
                ],
            ],
        ],
    ],
];
```

В примере выше, Вы задали поддержку расширенного синтаксиса через [[yii\web\AssetConverter::commands]] свойство.
Ключи массива - это имена расширений файлов (без ведущей точки), а значения массива - это образующийся файл ресурса имён расширений и команд для выполнения конвертации ресурса. Маркеры `{from}` и `{to}` в командах будут заменены соответственно исходным путём файла ресурсов и путём назначения файла ресурсов.

> Note: Существуют другие способы работы с ресурсами расширенного синтаксиса, кроме того, который указан выше.
Например, Вы можете использовать инструменты построения, такие как [grunt](https://gruntjs.com/) для отслеживания и автоматической конвертации ресурсов расширенного синтаксиса. В этом случае, Вы должны перечислить конечные CSS/JavaScript файлы в комплекте ресурсов вместо исходных файлов.

## Объединение и Сжатие Ресурсов<span id="combining-compressing-assets"></span>

Web страница может включать много CSS и/или JavaScript файлов. Чтобы сократить количество HTTP запросов и общий размер загрузки этих файлов, общепринятой практикой является объединение и сжатие нескольких CSS/JavaScript файлов в один или в более меньшее количество, а затем включение этих сжатых файлов вместо исходных в Web страницы.
 
> Note: Комбинирование и сжатие ресурсов обычно необходимо, когда приложение находится в режиме продакшена.
В режиме разработки, использование исходных CSS/JavaScript файлов часто более удобно для отладочных целей.

Далее, мы представим подход комбинирования и сжатия файлов ресурсов без необходимости изменения Вашего существующего кода приложения.

1. Найдите все комплекты ресурсов в Вашем приложении, которые Вы планируете скомбинировать и сжать.
2. Распределите эти комплекты в одну или несколько групп. Обратите внимание, что каждый комплект может принадлежать только одной группе.
3. Скомбинируйте/сожмите CSS файлы каждой группы в один файл. Сделайте то же самое для JavaScript файлов.
4. Определите новый комплект ресурсов для каждой группы:
* Или установите [[yii\web\AssetBundle::css|css]] и [[yii\web\AssetBundle::js|js]] свойства. Соответствующие CSS и JavaScript файлы будут объединены.
* Или настройте комплекты ресурсов каждой группы, установив их [[yii\web\AssetBundle::css|css]] и [[yii\web\AssetBundle::js|js]] свойства как пустые, и установите их [[yii\web\AssetBundle::depends|depends]] свойство как новый комплект ресурсов, созданный для группы.

Используя этот подход, при регистрации комплекта ресурсов в представлении, автоматически регистрируется новый комплект ресурсов для группы, к которому исходный комплект принадлежит. В результате скомбинированные/сжатые файлы ресурсов включаются в страницу вместо исходных.

### Пример <span id="example"></span>

Давайте рассмотрим пример, чтобы объяснить вышеуказанный подход.

Предположим, ваше приложение имеет две страницы, X и Y. Страница X использует комплект ресурсов A, B и C, в то время, как страница Y использует комплект ресурсов, B, C и D.

У Вас есть два пути, чтобы разделить эти комплекты ресурсов. Первый - использовать одну группу, включающую в себя все комплекты ресурсов. Другой путь - положить комплект А в группу Х, D в группу Y, а (B, C) в группу S. Какой из этих вариантов лучше? Первый способ имеет преимущество в том, что в обоих страницах одинаково скомбинированы файлы CSS и JavaScript, что делает HTTP кэширование более эффективным. С другой стороны, поскольку одна группа содержит все комплекты, размер скомбинированных CSS и JavaScript файлов будет больше, и таким образом увеличится время отдачи файла (загрузки страницы). Для простоты в этом примере, мы будем использовать первый способ, то есть использовать единую группу, содержащую все пакеты.

> Note: Разделение комплекта ресурсов на группы это не тривиальная задача. Это, как правило, требует анализа реальных данных о трафике различных ресурсов на разных страницах. В начале вы можете начать с одной группы, для простоты.

Используйте существующие инструменты (например [Closure Compiler](https://developers.google.com/closure/compiler/), 
[YUI Compressor](https://github.com/yui/yuicompressor/)) для объединения и сжатия CSS и JavaScript файлов во всех комплектах. Обратите внимание, что файлы должны быть объединены в том порядке, который удовлетворяет зависимости между комплектами. Например, если комплект A зависит от В, который зависит от С и D, то Вы должны перечислить файлы ресурсов начиная с С и D, затем B, и только после этого А.

После объединения и сжатия, Вы получите один CSS файл и один JavaScript файл. Предположим, они названы как `all-xyz.css` и `all-xyz.js`, где `xyz` это временная метка или хэш, который используется, чтобы создать уникальное имя файла, чтобы избежать проблем с HTTP кэшированием.

Сейчас мы находимся на последнем шаге. Настройте [[yii\web\AssetManager|asset manager]] в конфигурации вашего приложения, как показано ниже:

```php
return [
    'components' => [
        'assetManager' => [
            'bundles' => [
                'all' => [
                    'class' => 'yii\web\AssetBundle',
                    'basePath' => '@webroot/assets',
                    'baseUrl' => '@web/assets',
                    'css' => ['all-xyz.css'],
                    'js' => ['all-xyz.js'],
                ],
                'A' => ['css' => [], 'js' => [], 'depends' => ['all']],
                'B' => ['css' => [], 'js' => [], 'depends' => ['all']],
                'C' => ['css' => [], 'js' => [], 'depends' => ['all']],
                'D' => ['css' => [], 'js' => [], 'depends' => ['all']],
            ],
        ],
    ],
];
```

Как объяснено в подразделе [Настройка Комплектов Ресурсов](#customizing-asset-bundles), приведенная выше конфигурация
изменяет поведение по умолчанию каждого комплекта. В частности, комплекты A, B, C и D не имеют больше никаких файлов ресурсов. Теперь они все зависят от `all` комплекта, который содержит скомбинированные `all-xyz.css` и `all-xyz.js` файлы. Следовательно, для страницы X, вместо включения исходных файлов ресурсов из комплектов A, B и C, только два этих объединённых файла будут включены, то же самое произойдёт и со страницей Y.

Есть еще один трюк, чтобы сделать работу вышеуказанного подхода более отлаженной. Вместо изменения конфигурационного файла приложения напрямую, можно поставить комплект массива настроек в отдельный файл, и условно включить этот файл в конфигурацию приложения. Например,

```php
return [
    'components' => [
        'assetManager' => [
            'bundles' => require __DIR__ . '/' . (YII_ENV_PROD ? 'assets-prod.php' : 'assets-dev.php'),  
        ],
    ],
];
```

То есть, массив конфигурации комплекта ресурсов сохраняется в `assets-prod.php` для режима продакшена, и в `assets-dev.php` для режима не продакшена (разработки).

> Замечание: этот механизм объединения комплектов ресурсов основан на способности [[yii\web\AssetManager::bundles]] перекрывать
  поля регистрируемых комплектов ресурсов. Однако, как уже было сказано выше, эта возможность не распространяется на
  изменения, внесенные в комплекты ресурсов на уровне метода [[yii\web\AssetBundle::init()]] или после регистрации. Вам
  следует избегать использования динамических комплектов ресурсов в процессе объединения.


### Использование команды `asset`<span id="using-asset-command"></span>

Yii предоставляет консольную команду с именем `asset` для автоматизации подхода, который мы только что описали.

Чтобы использовать эту команду, Вы должны сначала создать файл конфигурации для описания того, как комплекты ресурсов должны быть скомбинированы, и как они должны быть сгруппированы. Затем Вы можете использовать подкоманду `asset/template`, чтобы сгенерировать первый шаблон и затем отредактировать его под свои нужды.

```
yii asset/template assets.php
```

Данная команда сгенерирует файл с именем `assets.php` в текущей директории. Содержание этого файла можно увидеть ниже:

```php
<?php
/**
 * Файл конфигурации команды консоли "yii asset".
 * Обратите внимание, что в консольной среде, некоторые псевдонимы путей, такие как "@webroot' и '@web ",
 * не могут быть использованы.
 * Пожалуйста, определите отсутствующие псевдонимы путей.
 */
return [
    // Настроить команду/обратный вызов для сжатия файлов JavaScript:
    'jsCompressor' => 'java -jar compiler.jar --js {from} --js_output_file {to}',
    // Настроить команду/обратный вызов для сжатия файлов CSS:
    'cssCompressor' => 'java -jar yuicompressor.jar --type css {from} -o {to}',
    // Whether to delete asset source after compression:
    'deleteSource' => false,
    // Список комплектов ресурсов для сжатия:
    'bundles' => [
        // 'yii\web\YiiAsset',
        // 'yii\web\JqueryAsset',
    ],
    // Комплект ресурса после сжатия:
    'targets' => [
        'all' => [
            'class' => 'yii\web\AssetBundle',
            'basePath' => '@webroot/assets',
            'baseUrl' => '@web/assets',
            'js' => 'js/all-{hash}.js',
            'css' => 'css/all-{hash}.css',
        ],
    ],
    // Настройка менеджера ресурсов:
    'assetManager' => [
    ],
];
```

Вы должны изменить этот файл и указать в `bundles` параметре, какие комплекты Вы планируете объединить. В параметре `targets` вы должны указать, как комплекты должны быть поделены в группы. Вы можете указать одну или несколько групп, как уже было сказано выше.

> Note: Так как псевдонимы путей `@webroot` и `@web` не могут быть использованы в консольном приложении, Вы должны явно задать их в файле конфигурации.

JavaScript файлы объединены, сжаты и записаны в `js/all-{hash}.js`, где {hash} перенесён из хэша результирующего файла.

Параметры `jsCompressor` и `cssCompressor` указывают на консольные команды или обратный вызов PHP, выполняющие JavaScript и CSS объединение/сжатие. По умолчанию Yii использует [Closure Compiler](https://developers.google.com/closure/compiler/) для объединения JavaScript файлов и [YUI Compressor](https://github.com/yui/yuicompressor/) для объединения CSS файлов. Вы должны установить эти инструменты вручную или настроить данные параметры, чтобы использовать ваши любимые инструменты.

Вы можете запустить команду `asset` с файлом конфигурации для объединения и сжатия файлов ресурсов, а затем создать новый файл конфигурации комплекта ресурса `assets-prod.php`:
 
```
yii asset assets.php config/assets-prod.php
```

Сгенерированный файл конфигурации может быть включен в конфигурацию приложения, как описано в последнем подразделе.

> Примечание: в случае если вы перенастраиваете комплекты ресурсов через [[yii\web\AssetManager::bundles]] или
  [[yii\web\AssetManager::assetMap]], и хотите, чтобы эти настройки применились для исходных файлов для сжатия,
  вы должны занести эти опции в раздел `assetManager` файла кофигурации для команды `asset`.

> Замечание: составляя набор исходных комплектов ресурсов для сжатия, следует избегать использования таких, чьи параметры
  могут изменяться динамически (т.е. на уровне метода `init()` или после регистрации), поскольку они могут функционировать
  неправильно после сжатия.


> Для справки: Команда `asset` является не единственной опцией для автоматического процесса объединения и сжатия ресурсов.
  Вы можете также использовать такой замечательный инструмент запуска приложений как [grunt](https://gruntjs.com/) для достижения той же цели.


### Группировка Комплектов Ресурсов <span id="grouping-asset-bundles"></span>

В последнем подразделе, мы пояснили, как объединять все комплекты ресурсов в единый в целях минимизации HTTP запросов для файлов ресурсов, упоминавшихся в приложении. Это не всегда желательно на практике. Например, представьте себе, что Ваше приложение содержит "front end", а также и "back end", каждый из которых использует свой набор JavaScript и CSS файлов. В этом случае, объединение всех комплектов ресурсов с обеих сторон в один не имеет смысла потому, что комплекты ресурсов для "front end" не используются в "back end", и это будет бесполезной тратой трафика - отправлять "back end" ресурсы, когда страница из "front end" будет запрошена.

Для решения вышеуказанной проблемы, вы можете разделить комплекты по группам и объединить комплекты ресурсов для каждой группы. Следующая конфигурация показывает, как Вы можете объединять комплекты ресурсов:

```php
return [
    ...
    // Укажите выходной комплект для групп:
    'targets' => [
        'allShared' => [
            'js' => 'js/all-shared-{hash}.js',
            'css' => 'css/all-shared-{hash}.css',
            'depends' => [
                // Включаем все ресурсы поделённые между 'backend' и 'frontend'
                'yii\web\YiiAsset',
                'app\assets\SharedAsset',
            ],
        ],
        'allBackEnd' => [
            'js' => 'js/all-{hash}.js',
            'css' => 'css/all-{hash}.css',
            'depends' => [
                // Включаем только 'backend' ресурсы:
                'app\assets\AdminAsset'
            ],
        ],
        'allFrontEnd' => [
            'js' => 'js/all-{hash}.js',
            'css' => 'css/all-{hash}.css',
            'depends' => [], // Включаем все оставшиеся ресурсы
        ],
    ],
    ...
];
```

Как вы можете видеть, комплекты ресурсов поделены на три группы: `allShared`, `allBackEnd` и `allFrontEnd`. Каждая из которых зависит от соответствующего набора комплектов ресурсов. Например, `allBackEnd` зависит от `app\assets\AdminAsset`. При запуске команды `asset` с данной конфигурацией будут объединены комплекты ресурсов согласно приведенной выше спецификации.

> Для справки: Вы можете оставить `depends` конфигурацию пустой для одного из намеченных комплектов. Поступая таким образом, данный комплект ресурсов будет зависеть от всех остальных комплектов ресурсов, от которых другие целевые комплекты не зависят.
