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

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

В мире фронтенда многие ресурсы (ассеты) связаны между собой. В Joomla никогда не было простого способа указать эту связь, но Joomla 4 изменила эту ситуацию, введя концепцию Web Assets. Управление JavaScript и CSS в Joomla значительно упростилось, благодаря классу WebAssetManager. Есть замечательная статья Как правильно подключать JavaScript и CSS в Joomla 4, в которой подробно и с примерами кода рассказывается об этой концепции и её применении. Рекомендую ознакомиться с ней для более полного понимания сути этой статьи.

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

Задача

Задача заключается в том, чтобы подключить в общий реестр скриптов, стилей и пресетов 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 приложения)

  • 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 по созданию плагинов. Можно по старинке использовать методы вида public function onBeforeCompileHead(). В таком случае плагин можно будет использовать как в Joomla 3, так и в Joomla 4. А можно использовать новый способ, предложенный в Joomla 4.1.

defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;

class PlgSystemWtjswiper 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
	{
		$mapping = [];

		// Only trigger in frontend
		if (Factory::getApplication()->isClient('site'))
		{
			$mapping['onAfterInitialise'] = 'addSwiperPreset';
		}

		return $mapping;
	}
  }

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

Самым важным оказалось найти правильное системное событие, на этапе которого есть возможность добавить свои веб-ассеты глобально в Joomla 4 Web Assets Manager. Таким событием оказалось onAfterInitialise. В моём случае попытки зарегистрировать веб-ассет на событии onBeforeCompileHead приводили к тому, что joomla.asset.json добавлялся в реестр ассетов, но не парсился. Что равносильно тому, что его не существует.

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

public function addSwiperPreset()
	{
		// Only trigger in frontend
		if (Factory::getApplication()->isClient('site'))
		{
			/** @var Joomla\CMS\WebAsset\WebAssetManager $wa*/
			$wa = Factory::getDocument()->getWebAssetManager();
			$wa->getRegistry()->addRegistryFile('media/plg_system_wtjswiper/joomla.asset.json');
			return true;
		}
	}

Обратите внимание на метод 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).

Так же полезные ресурсы

Ресурсы сообщества:

  • форум русской поддержки Joomla.

  • интернет-портал Joomla-сообщества.

  • https://vc.ru/s/1146097-joomla - Сообщество Joomla на VC.

Telegram:

  • чат сообщества «Joomla! по-русски».

  • Joomla для профессионалов, разработчики Joomla.

  • Новости о Joomla! и веб-разработке по-русски.

  • вакансии и предложения работы по Joomla: фуллтайм, частичная занятость и разовые подработки. Размещение вакансий здесь.

  • Англоязычный чат сообщества.

Источник: https://habr.com/ru/post/672020/


Интересные статьи

Интересные статьи

В этой статье я продемонстрирую, как можно использовать паттерн Flow Coordinator (далее флоу-координатор) в SwiftUI, чтобы отделить логику навигации от логики представления.
Каждое приложение использует компоненты с выпадающими блоками. Такие панели используются в выпадающем списке, Autocomplete, Tooltip и т.д. В Material CDK есть инструмент ...
Вы использовали когда-нибудь Hygen, автоматический генератор кода? Если еще нет, возможно, наш перевод откроет для вас новый полезный инструмент. Читать дальше → ...
Legacy технологииПредупреждение: ASP.NET MVC уже устарел. Рекомендуется использовать ASP.NET Core. Но если вам интересно, то читайте. Решил немного расширить предыдущую статью про ASP.NET MVC ...
Перевод статьи подготовлен для студентов курса «Математика для Data Science» Аннотация В этой статье рассматривается задача выравнивания лица для одного изображения. Мы покажем, как ансамбл...