---
title: "Использование WebAssetsManager Joomla 4 и добавление собственных пресетов с помощью плагина - WebTolk"
description: "Управление JavaScript и CSS в Joomla значительно упростилось, благодаря классу WebAssetManager. Как подключить свою Js-библиотеку или CSS-фреймворк в Joomla правильно с помощью плагина расскажет эта статья"
url: "https://web-tolk.ru/blog/ispolzovanie-webassetsmanager-joomla-4-i-dobavlenie-sobstvennykh-presetov-s-pomoshchyu-plagina"
date: "2022-07-28T10:06:51+00:00"
language: "ru-RU"
---

# Использование WebAssetsManager Joomla 4 и добавление собственных пресетов с помощью плагина

 Автор: Сергей Толкачев Создано: 28 июля 2022 Обновлено: 10 января 2026 Просмотров: 3348    ![Использование WebAssetsManager Joomla 4 и добавление собственных пресетов с помощью плагина Joomla 4](https://web-tolk.ru/blog/images/blog/ispolzovanie-webassetsmanager-joomla-4-i-dobavlenie-sobstvennykh-presetov-s-pomoshchyu-plagina/header-1280x720.jpg)

В мире фронтенда многие ресурсы (ассеты) связаны между собой. В Joomla никогда не было простого способа указать эту связь, но Joomla 4 изменила эту ситуацию, введя концепцию **Web Assets**. Управление JavaScript и CSS в Joomla значительно упростилось, благодаря классу `WebAssetManager`. Есть замечательная статья [Как правильно подключать JavaScript и CSS в Joomla 4](https://jpath.ru/docs/output/js-css/kak-pravilno-podklyuchat-javascript-i-css-v-joomla-4) ([на этом сайте](https://web-tolk.ru/blog/index.php?option=com_content&view=article&id=166&catid=10&lang=ru-RU)), в которой подробно и с примерами кода рассказывается об этой концепции и её применении. Рекомендую ознакомиться с ней для более полного понимания сути этой статьи. Статья эта первоначальна [была опубликована на Хабре](https://habr.com/ru/post/672020/). Копирую к себе.

Однако, в процессе разработки собственных решений я столкнулся с проблемой. Решение её в данной заметке будет небольшим дополнением к вышеупомянутой статье.

## Задача

Задача заключается в том, чтобы подключить в общий реестр скриптов, стилей и пресетов js-библиотеку (в моём случае - Swiper.js) таким образом, чтобы она была доступна из самых разных мест Joomla 4 и её можно было использовать для разных расширений (возможно не только моих), автономно её обновлять.

Например: js-библиотеку подключает плагин, а использовать её может и модуль, и компонент, и контент-плагин, и плагин поля.

Образцом поведения является встроенный в Joomla 4 Bootstrap 5. Он поддерживает модульное подключение с автоматическим подключением всех зависимостей, которые описаны в файле **media/vendor/joomla.asset.json**.

Joomla 4 будет искать определение ассетов автоматически во время выполнения в следующем порядке:

- media/vendor/joomla.asset.json (при первом обращении к WebAssetRegistry)
- media/system/joomla.asset.json
- media/legacy/joomla.asset.json
- media/{com_active_component}/joomla.asset.json ([Общая информация о принципе действия Joomla](https://jpath.ru/docs/basics/joomla-internals/obshchaya-informatsiya-o-printsipe-dejstviya-joomla#dispetcherizatsiya-dispatching) приложения)
- templates/{active_template}/joomla.asset.json

А затем загрузит их в реестр известных JavaScript и CSS файлов.

Редактировать файлы ядра - нельзя (хотя, к сожалению, это распространено среди разработчиков, желающих быстро решить какую-нибудь задачу). Значит нам нужен плагин, которым можно "подлезть" на этапе формирования реестра ассетов и добавить в него нужные нам веб ассеты. Это решение было очевидно сразу.

Однако, все примеры создания и подключения скриптов, стилей и пресетов предполагали, что регистрируется и начинает использоваться ассет в одном и том же месте. Попытки вынести подключение ассета в плагин, а использовать ассет в модуле через `$wa->usePreset,` `$wa->useScript` приводили к ошибке `"There is no "swiper-bundle" asset of a "script" type in the registry."`

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

## Пример из системного плагина jooally - плагина версии для слабовидящих

```
<?php
/**
 * @package     Joomla.Plugin
 * @subpackage  System.jooa11y
 *
 * @copyright   (C) 2021 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\Application\CMSApplicationInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;

/**
 * Jooa11y plugin to add an accessibility checker
 *
 * @since  4.1.0
 */
class PlgSystemJooa11y extends CMSPlugin implements SubscriberInterface
{
	/**
	 * Application object.
	 *
	 * @var    CMSApplicationInterface
	 * @since  4.1.0
	 */
	protected $app;

	/**
	 * Affects constructor behavior. If true, language files will be loaded automatically.
	 *
	 * @var    boolean
	 * @since  4.1.0
	 */
	protected $autoloadLanguage = true;

	/**
	 * Subscribe to certain events
	 *
	 * @return string[]  An array of event mappings
	 *
	 * @since 4.1.0
	 *
	 * @throws Exception
	 */
	public static function getSubscribedEvents(): array
	{
		$mapping = [];

		// Срабатываем только на фронте
		if (Factory::getApplication()->isClient('site'))
		{
    /**
     * Срабатываем на событие onBeforeCompileHead и вызываем функцию initJooa11y.
     * Можно по старинке упростить и использовать public function onBeforeCompileHead()
     */
			$mapping['onBeforeCompileHead'] = 'initJooa11y';
		}

		return $mapping;
	}
  }
```

Плагин срабатывает на событие `onBeforeCompileHead`. На этом событии возможно обработать всё, что составляет содержимое `<head>` страницы в Joomla 4: title, мета-теги и т.д.

В самой функции `initJooa11y` идут проверки на CLI, REST API - чтобы плагин срабатывал только при выводе HTML. В самом конце функции происходит регистрация и добавление скриптов и стилей для функционирования версии для слабовидящих:

```
// Get the document object.
		$document = $this->app->getDocument();

/** @var Joomla\CMS\WebAsset\WebAssetManager $wa*/
		$wa = $document->getWebAssetManager();

		$wa->getRegistry()->addRegistryFile('media/plg_system_jooa11y/joomla.asset.json');

		$wa->useScript('plg_system_jooa11y.jooa11y')
			->useStyle('plg_system_jooa11y.jooa11y');
```

В файле `media/plg_system_jooa11y/joomla.asset.json` описываются файлы и их зависимости для работы плагина. Обратите внимание на то, что сразу после регистрации ассета начинается его использование - **useScript** и **useStyle**.

Но такое поведение меня не устраивало, так как хотелось достичь большей универсальности и автономности элементов. Помогли собственные поиски и отклик Joomla-сообщества. Итак....

## Как добавить собственные js и css в Joomla 4 и сделать их доступными глобально?

Создаём плагин группы system. [Официальная документация для разработчиков Joomla 4](https://docs.joomla.org/J4.x:Creating_a_Plugin_for_Joomla) по созданию плагинов. Можно по старинке использовать методы вида `public function onBeforeCompileHead()`. В таком случае плагин можно будет использовать как в Joomla 3, так и в Joomla 4. А можно использовать новый способ, предложенный в Joomla 4.1.

```
<?php
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
use Joomla\CMS\WebAsset\WebAssetRegistry;

class Wtjswiper extends CMSPlugin implements SubscriberInterface
{
	/**
	 * Subscribe to certain events
	 *
	 * @return string[]  An array of event mappings
	 *
	 * @since 4.1.0
	 *
	 * @throws Exception
	 */
	public static function getSubscribedEvents(): array
	{

		return $mapping  = [
				'onAfterRoute' => 'addSwiperPreset'
			];
	}
  }
```

Такой способ при правильном применении и вызове события позволяет не запоминать порядок передаваемых аргументов.

Самым важным оказалось найти правильное системное событие, на этапе которого есть возможность добавить свои веб-ассеты глобально в Joomla 4 Web Assets Manager.

---

**Upd. 25.09.2023.**Ранее в этом месте статьи я рекомендовал подключать веб-ассеты на событие `onAfterInitialise`. Однако, это одно из самых ранних системных событий. На этом этапе Joomla ещё не знает какой именно тип документа требуется отобразить (HTML, Json и т.д.). Использование привычных методов `Factory::getDocument()` (устаревший вызов) или `Factory::getApplication()->getDocument()` приводили к слишком ранней инициализации типа документа и в случае использования документов типа Json могли вызывать ошибку. Об этом написано в переводе статьи [Распространенные ошибки при написании плагинов Joomla 4](https://web-tolk.ru/blog/index.php?option=com_content&view=article&id=55:rasprostranennye-oshibki-pri-napisanii-plaginov-joomla-4&catid=10:blog&lang=ru-RU&Itemid=114). В случае с типом документа HTML всё работало, но под капотом оно было неправильно.

Вторым нюансом в выборе правильного системного события оказалось то, что есть **WebAssetRegistry** - реестр ассетов. И **WebAssetManager** - менеджер по работе с ассетами. **WebAssetRegistry инициализируется и доступен раньше, чем WebAssetManager.** Его можно получить из контейнера например на событие `onAfterRoute` и здесь же добавить свой ассет. Как оказалось, событие `onBeforeCompileHead` - это одно из самых последних вызываемых событий и добавлять именно файл joomla.asset.json в этот момент жизненного цикла приложения уже слишком поздно. Поэтому, как я писал ранее, попытки зарегистрировать веб-ассет на событии `onBeforeCompileHead` приводили к тому, что joomla.asset.json добавлялся в реестр ассетов, но не парсился. Что равносильно тому, что его не существует. Примеры кода исправлены.

**Upd.23.10.2023:**В Joomla 5 появился новый системный триггер для плагинов, который идеально подходит для добавления пресетов в WebAssetRegistry: `onAfterInitialiseDocument`.

**Также еще один важный нюанс:** Вы можете добавлять пресет с помощью добавления файла `joomla.asset.json` в реестр ассетов методом `$wa->addRegistryFile()`. В таком случае нужно брать одно из ранних системных событий ДО `onAfterDispatch`. Так как перед триггером `onAfterDispatch` происходит рендер вывода компонента (не всей страницы с модулями, а именно области компонента), а в этот момент уже начинают работать плагины контента, которые в свою очередь могут использовать те или иные веб-ассеты. Если используемых веб-ассетов не окажется в реестре - будет ошибка **There is no "your-preset-name" asset of a "script" type in the registry**.

Мы могли бы проверить тип документа перед добавлением ассета и если он - "html" - добавить ассет. Но, проблемой в Joomla 4 является то, что на событиях до `onAfterDispatch` ещё не известен тип документа, который мы обычно получаем с помощью `Factory::getApplication()->getDocument()`. Веб ассеты нужны для отображения данных в документе типа HtmlDocument. Однако, в Joomla 9 типов документа (Document) и минимум 5 видов приложений (App), а также могут быть кастомные типы приложения со своими типами документов.

![Типы документа Document Joomla 5](https://web-tolk.ru/blog/images/blog/ispolzovanie-webassetsmanager-joomla-4-i-dobavlenie-sobstvennykh-presetov-s-pomoshchyu-plagina/joomla-document-types.png)

Определение типа документа происходит позже. Слишком ранняя инициализация типа документа путём `$app->getDocument()` не вызовет никаких последствий для Html-документа - обычных сайтов, но может вызвать ошибки в других типах документа - XML, Json, Feed etc., а так же в других типах приложений - CLI, Api и т.д.

В Joomla 5 конкретно эта проблема решена добавлением события для плагинов - `onAfterInitialiseDocument`, которым и рекомендуется пользоваться.

Также есть вариант регистрировать ассеты js/css файлами - методами `registerAndUseScript()` или `registerAndUseStyle()`. Тогда добавлять их можно и на более поздних событиях, но это уже не так удобно, так как описывать ассет нужно в PHP, а не в `joomla.asset.json`.

---

Следующий код позволяет зарегистрировать javascript-библиотеку

```
<?php
public function addSwiperPreset() : void
	{
		// Only trigger in frontend
		if (Factory::getApplication()->isClient('site'))
		{
			/** @var Joomla\CMS\WebAsset\WebAssetRegistry $wa*/
			$wa = Factory::getContainer()->get(WebAssetRegistry::class);
			$wa->addRegistryFile('media/plg_system_wtjswiper/joomla.asset.json');
		}
	}
```

Обратите внимание на метод `addRegistryFile()`, где указывается путь к файлу `joomla.assets.json` от корня сайта. Так же существует прокси-метод `addExtensionRegistryFile(string $name)`, который принимает в качестве параметра системное имя расширения, по которому доступны его веб-ассеты в папке `media`: **com_content**, **plg_system_jooally** и т.д. Тогда подключаться будет файл 'media/**com_content**/joomla.asset.json' и 'media/**plg_system_jooally**/joomla.asset.json' соответственно.

Содержимое файла `joomla.assets.json`

```
{
  "$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json",
  "name": "swiper",
  "version": "8.2.4",
  "description": "Swiper js library",
  "license": "GPL-2.0-or-later",
  "assets": [
    {
      "name": "swiper-bundle",
      "type": "script",
      "uri": "plg_system_wtjswiper/swiper-bundle.min.js",
      "attributes": {
        "defer": true
      },
      "package": "swiper",
      "version": "8.2.4"
    },
    {
      "name": "swiper-bundle",
      "type": "style",
      "uri": "plg_system_wtjswiper/swiper-bundle.min.css",
      "package": "swiper",
      "version": "8.2.4"
    },
    {
      "name": "swiper-bundle",
      "type": "preset",
      "uri": "",
      "dependencies": [
        "swiper-bundle#style",
        "swiper-bundle#script"
      ]
    }
  ]
}
```

Uri в json в зависимости от типа ассета автоматически дополняется 'js' или 'css'. Если вы подключаете файл `media/plg_system_wtjswiper/css/swiper-bundle.min.css`, то uri файла в `joomla.assets.json` будет `plg_system_wtjswiper/swiper-bundle.min.css`

Такой же принцип использовался раньше в Joomla 3 при подключении ресурсов с помощью HTMLHelper (ex. JHTML).

Выложил результат изысканий в виде [готового плагина](https://web-tolk.ru/blog/dev/joomla-plugins/wt-jswiper.html), интегрирующего Swiper.js в Joomla 4.

- [Оригинал моей публикации на Хабре](https://habr.com/ru/post/672020/)

## Об авторе

![Толкачев Сергей Юрьевич](https://web-tolk.ru/images/uslugi/sergey-tolkachyov-apr-2023.webp)

### Толкачев Сергей Юрьевич

Joomla-разработчик. [Контрибьютер ядра Joomla](https://github.com/joomla/joomla-cms/pulls?q=is%3Apr+author%3Asergeytolkachyov+). Один из ведущих Telegram-канала русскоязычного Joomla-сообщества [JoomlaFeed](https://t.me/joomlafeed), один из модераторов [чата русскоязычного Joomla-сообщества](https://t.me/joomlaru). Мои расширения в официальном маркетплейсе расширений Joomla - [Joomla Extensions Directory](https://extensions.joomla.org/profile/profile/details/528051/). Имею публикации в [официальном журнале международного Joomla-сообщества - Joomla Community Magazine](https://magazine.joomla.org/authors/sergeytolkachyov) и на [официальном сайте русскоязычного Joomla-сообщества](https://joomlaportal.ru/users/sergey-tolkachyov).

Муж. Отец 3 детей.

Россия, Саратов.

## JSON-LD Schema

```json
{
    "@context": "https://schema.org",
    "@type": "BreadcrumbList",
    "@id": "https://web-tolk.ru/#/schema/BreadcrumbList/17",
    "itemListElement": [
        {
            "@type": "ListItem",
            "position": 1,
            "item": {
                "@id": "https://web-tolk.ru/",
                "name": "Главная"
            }
        },
        {
            "@type": "ListItem",
            "position": 2,
            "item": {
                "@id": "https://web-tolk.ru/blog",
                "name": "Блог"
            }
        },
        {
            "@type": "ListItem",
            "position": 3,
            "item": {
                "name": "Использование WebAssetsManager Joomla 4 и добавление собственных пресетов с помощью плагина"
            }
        }
    ]
}
```

```json
{
    "@context": "https://schema.org",
    "@graph": [
        {
            "@type": "Organization",
            "@id": "https://web-tolk.ru/#/schema/Organization/base",
            "name": "WebTolk",
            "url": "https://web-tolk.ru/",
            "logo": {
                "@type": "ImageObject",
                "@id": "https://web-tolk.ru/#/schema/ImageObject/logo",
                "url": "images/webtolk-1080p.jpg",
                "contentUrl": "images/webtolk-1080p.jpg",
                "width": 1920,
                "height": 1080
            },
            "image": {
                "@id": "https://web-tolk.ru/#/schema/ImageObject/logo"
            },
            "sameAs": [
                "https://github.com/WebTolk",
                "https://github.com/sergeytolkachyov",
                "https://vk.com/web_tolk",
                "https://vk.com/webtolkru",
                "https://tenchat.ru/sergeytolkachyov",
                "https://t.me/sergeytolkachyov",
                "https://t.me/webtolkru"
            ]
        },
        {
            "@type": "WebSite",
            "@id": "https://web-tolk.ru/#/schema/WebSite/base",
            "url": "https://web-tolk.ru/",
            "name": "WebTolk",
            "publisher": {
                "@id": "https://web-tolk.ru/#/schema/Organization/base"
            }
        },
        {
            "@type": "WebPage",
            "@id": "https://web-tolk.ru/#/schema/WebPage/base",
            "url": "https://web-tolk.ru/blog/ispolzovanie-webassetsmanager-joomla-4-i-dobavlenie-sobstvennykh-presetov-s-pomoshchyu-plagina",
            "name": "Использование WebAssetsManager Joomla 4 и добавление собственных пресетов с помощью плагина - WebTolk",
            "description": "Управление JavaScript и CSS в Joomla значительно упростилось, благодаря классу WebAssetManager. Как подключить свою Js-библиотеку или CSS-фреймворк в Joomla правильно с помощью плагина расскажет эта статья",
            "isPartOf": {
                "@id": "https://web-tolk.ru/#/schema/WebSite/base"
            },
            "about": {
                "@id": "https://web-tolk.ru/#/schema/Organization/base"
            },
            "inLanguage": "ru-RU",
            "breadcrumb": {
                "@id": "https://web-tolk.ru/#/schema/BreadcrumbList/17"
            }
        },
        {
            "@type": "Article",
            "@id": "https://web-tolk.ru/#/schema/com_content/article/53",
            "isPartOf": {
                "@id": "https://web-tolk.ru/#/schema/WebPage/base"
            }
        }
    ]
}
```
