J4.x

J4.x:Создание Плагина для Joomla

From Joomla! Documentation

This page is a translated version of the page J4.x:Creating a Plugin for Joomla and the translation is 88% complete.
Outdated translations are marked like this.
Other languages:
Bahasa Indonesia • ‎Deutsch • ‎English • ‎Nederlands • ‎Türkçe • ‎español • ‎français • ‎italiano • ‎português do Brasil • ‎русский • ‎فارسی • ‎中文(台灣)‎
Руководство
Как создать Плагин для Joomla 4
Joomla! 
4.x
series

Структура плагина в Joomla! 1.5, 2.5 и 3.x была очень гибкой и мощной. Плагины могут быть использованы не только для обработки вызовов событий приложения и расширений ядра, но и для того, чтобы сделать сторонние расширения более мощными. В Joomla 4.x система диспетчера была переписана практически полностью. Это ещё больше увеличило гибкость, когда вы изменяете параметры, переданные как события, а также увеличило производительность плагинов.

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

Создание установочного файла

Так же как и все расширения Joomla, плагины могут быть легко установлены как .zip (поддерживается и .tar.gz), но при этом в архив должен быть включён корректно отформатированный XML файл.
В качестве примера приведём установочный XML файл для поискового плагина категорий:

<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="search" method="upgrade">
	<name>plg_search_categories</name>
	<author>Joomla! Project</author>
	<creationDate>November 2005</creationDate>
	<copyright>Copyright (C) 2005 - 2018 Open Source Matters. All rights reserved.</copyright>
	<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
	<authorEmail>admin@joomla.org</authorEmail>
	<authorUrl>www.joomla.org</authorUrl>
	<version>3.0.0</version>
	<description>PLG_SEARCH_CATEGORIES_XML_DESCRIPTION</description>
	<files>
		<filename plugin="categories">categories.php</filename>
	</files>
	<languages>
		<language tag="en-GB">en-GB.plg_search_categories.ini</language>
		<language tag="en-GB">en-GB.plg_search_categories.sys.ini</language>
	</languages>
	<config>
		<fields name="params">

			<fieldset name="basic">
				<field
					name="search_limit"
					type="number"
					label="JFIELD_PLG_SEARCH_SEARCHLIMIT_LABEL"
					default="50"
				/>

				<field
					name="search_content"
					type="radio"
					label="JFIELD_PLG_SEARCH_ALL_LABEL"
					layout="joomla.form.field.radio.switcher"
					default="0"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>

				<field
					name="search_archived"
					type="radio"
					label="JFIELD_PLG_SEARCH_ARCHIVED_LABEL"
					layout="joomla.form.field.radio.switcher"
					default="0"
					>
					<option value="1">JYES</option>
					<option value="0">JNO</option>
				</field>
			</fieldset>

		</fields>
	</config>
</extension>

Как вы можете видеть, система схожа с другими установочными XML файлами Joomla. Вы только должны обратить внимание на запись group="xxx", тег <extension> и расширенную информацию в теге <filename>. Эта информация говорит Joomla, в какую папку скопировать файл и в какую группу должен быть добавлен плагин.

Если вы создаете плагин, который реагирует на существующие события ядра, атрибут group="xxx" должен быть изменен в соответствии с именем уже существующей папки для события, которое вы хотите использовать. Например, group="authentication" или group="user". Смотрите Plugin/Events полный список существующих категорий событий. При создании нового плагина для событий ядра важно, чтобы его название было уникальным, и чтобы он не конфликтовал с уже существующими плагинами, которые могут реагировать на то же событие, какое используете вы.

Если вы создаёте плагин для событий не ядра, то тег group="xxx" должен отличаться от существующих категорий ядра.

Совет: Если вы добавляете атрибут method="upgrade" к тэгу extension, то плагин можно будет устанавливать без удаления предыдущей версии. Все существующие файлы будут перезаписаны, но оставшиеся файлы удалены не будут.


Создание плагина

In Joomla 4 you can also have namespaced code in your plugin - please read Namespace Conventions in Joomla 4. Note the main entry file is not namespaced but fields and any other supporting code is.

Объектно-ориентированный подход к написанию плагинов включает в себя создание подклассов от CMSPlugin, базового класса, реализующего основные свойства плагинов. Для ваших методов доступны следующие свойства:

  • $this->params: параметры, устанавливаемые администратором для этого плагина
  • $this->_name: название плагина
  • $this->_type: группа (тип) плагина
  • $this->db: объект базы данных (db)
  • $this->app: объект приложения (application)

Совет: Для использования $this->db и $this->app, CMSPlugin проверяет, существует ли свойство и что оно не приватное. Если вы хотите использовать дефолтные объекты, создайте неинстанцированные свойства класса плагина (например protected $db; protected $app;, там же где расположено protected $autoloadLanguage = true;). Свойства не будут существовать, пока не будут явно созданы.

In the following code example, <PluginGroup> represents the group (type) of the plugin, and <PluginName> represents its name. Note that class and function names in PHP are case-insensitive.

Здесь мы также реализуем SubscriberInterface, что является самым большим изменением по сравнению с Joomla 1.5-3.x. Вместо привычного подхода, когда имя функции автоматически определялось и было таким же, как имя события, этот подход позволяет задавать свои имена функции. Это позволяет нам показать, какие плагины реализуют какие функции, а так как парсинг публичных методов в PHP довольно медленный, это даёт значительное повышение производительности.

Здесь стоит заметить, что в линейке Joomla 4 существует слой совместимости, позволяющий работать плагинам, которые используют старую стратегию именования и не реализуют SubscriberInterface.

<?php
// no direct access
defined( '_JEXEC' ) or die;

use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\Event;
use Joomla\Event\SubscriberInterface;

class Plg<PluginGroup><PluginName> extends CMSPlugin implements SubscriberInterface
{
	/**
	 * Load the language file on instantiation
	 *
	 * @var    boolean
	 * @since  3.1
	 */
	protected $autoloadLanguage = true;

	/**
	 * Returns an array of events this subscriber will listen to.
	 *
	 * @return  array
	 */
	public static function getSubscribedEvents(): array
	{
		return [
			'<EventName>' => 'myFunctionName',
		];
	}

	/**
	 * Plugin method is the array value in the getSubscribedEvents method
	 * The plugin then modifies the Event object (if it's not immutable)
	 */
	 public function myFunctionName(Event $event)
	 {
		/*
		 * Plugin code goes here.
		 * You can access parameters via $this->params
		 */
		return true;
	}
}
?>


Использование плагинов в вашем коде

Если вы создаете плагин для нового события, а не для встроенного события ядра, то не забудьте активировать плагин после установки. Перед ссылкой на ваш новый плагин должна стоять команда JPluginHelper::importPlugin().

Теперь, когда вы создали свой плагин, то вам наверняка захочется вызвать его в своем коде. Но не всегда: ядро Joomla имеет набор встроенных событий, которые вы можете использовать для регистрации плагина (тогда нижеописанное делать не нужно).

Новый подход в Joomla 4

Новый подход в Joomla 4 - получить диспетчер и отправить именованное событие.

use Joomla\CMS\Event\AbstractEvent;
use Joomla\CMS\Factory;

$dispatcher = Factory::getApplication()->getDispatcher();

// Here we create an event however as long as you implement EventInterface you can create your own 
// custom classes
$event = AbstractEvent::create(
	'<EventName>',
	[
		'name' => $value,
	]
);

$eventResult = $dispatcher->dispatch('<EventName>', $event);

Если вы хотите позволить пользователю изменять значения, вы можете использовать результат события и getResults для его получения. Взгляните на

defined('_JEXEC') or die;

use BadMethodCallException;
use Joomla\CMS\Event\AbstractImmutableEvent;
use Joomla\CMS\Table\TableInterface;

/**
 * Event class for an event
 */
class MyCustomEvent extends AbstractImmutableEvent
{
	/**
	 * Constructor.
	 *
	 * @param   string  $name       The event name.
	 * @param   array   $arguments  The event arguments.
	 *
	 * @throws  BadMethodCallException
	 */
	public function __construct($name, array $arguments = array())
	{
		if (!array_key_exists('myProperty', $arguments))
		{
			throw new BadMethodCallException("Argument 'myProperty' is required for event $name");
		}

		parent::__construct($name, $arguments);
	}

	/**
	 * Setter for the myProperty argument
	 *
	 * @param   mixed  $value  The value to set
	 *
	 * @return  mixed
	 *
	 * @throws  BadMethodCallException  if the argument is not of the expected type
	 */
	protected function setMyProperty($value)
	{
		if (!empty($value) && !is_object($value) && !is_array($value))
		{
			throw new BadMethodCallException("Argument 'src' of event {$this->name} must be empty, object or array");
		}

		return $value;
	}
}

Почему мы ввели класс вместо параметров? Это даёт возможность вводить собственные сеттеры и геттеры для свойств - на данный момент плагин может полностью изменить свойство по своему усмотрению, и для компонентов нет способа наложить какие-либо ограничения. Дополнительно разработчикам проще добавлять и убирать параметры в событии без потери обратной совместимости (так как вы теперь вызываете определённые методы и не привязаны к свойству, являющимся вторым аргументом вашей функкции).

Как достигнуть максимальной совместимости с Joomla 3

Если вы хотите вызвать событие по аналогии с удалённым J3.x JEventDispatcher, то ваш код должен выглядеть вот так:

$results = \Joomla\CMS\Factory::getApplication()->triggerEvent( '<EventName>', <ParameterArray> );

Важно отметить, что параметры должны быть массивом. Функция плагина сама по себе получает параметры как объект Event, если он реализует SubscriberInterface, и как индивидуальные значения, если нет. Но этот метод всегда возвращает массив, который возвращает плагин.

Обратите внимание на то, что если хотя бы один плагин в группе не реализует SubscriberInterface, то тогда свойство результата (так как одновременно является именованным параметром и результатом из плагина) используется как специальное свойство и не может быть использовано.