J4.x

Membuat Plugin untuk Joomla

From Joomla! Documentation

This page is a translated version of the page J4.x:Creating a Plugin for Joomla and the translation is 29% 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 • ‎русский • ‎فارسی • ‎中文(台灣)‎
Tutorial Bagaimana membuat Plugin untuk Joomla 4
Joomla! 
4.x
series

Struktur plugin untuk Joomla! 1.5, 2.5 dan 3.x sangat fleksibel dan tangguh. Bukan sekedar dapat mengendalikan event yang dipicu oleh aplikasi inti dan ekstensi, tapi plugin juga dapat dipakai untuk mengembangkan dan memperkuat ekstensi pihak ketiga. Di Joomla 4, kami telah menulis ulang banyak sistem operator untuk mendukung hal ini guna meningkatkan fleksibilitas yang lebih jauh lagi pada saat anda memodifikasi parameter yang diteruskan sebagai event sambil secara bersamaan meningkatkan performa plugin.

Tutorial ini selayaknya menyediakan informasi dasar bagi anda mengenai hal-hal apa saja yang perlu diketahui sebelum anda mengembangkan plugin sendiri. Kebanyakan plugin hanya terdiri dari satu file tapi untuk dapat memasangnya dengan benar, plugin harus dipaketkan ke dalam suatu paket instalasi sehingga dapat diproses oleh pemasang Joomla.

Membuat File Instalasi

Sama seperti semua ekstensi-ekstensi Joomla yang lain, plugin mudah dipasang sebagai file zip (.tar.gz juga bisa), tapi format XML dari file itu haruslah benar.
Sebagai contoh, berikut adalah file instalasi XML untuk plugin penelusuran kategori:

<?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>

Seperti yang anda lihat, caranya sama dengan file-file instalasi XML Joomla yang lainnya. Anda hanya perlu memerhatikan entri group="xxx" di dalam tag <extension> serta informasi tambahan lain di dalam tag <filename>. Info ini memberitahu Joomla ke folder mana file harus disalin dan ke grup mana plugin harus dimasukkan.

Jika anda membuat plugin yang merespons ke event inti, untuk jenis event yang ingin dimodifikasi, edit atribut group="xxx" sesuai nama folder dari plugin inti yang sudah ada, misalnya group="authentication" atau group="user". Lihat Plugin/Events untuk daftar lengkap kategori event inti yang ada sekarang. Dalam pembuatan plugin baru yang merespons ke event inti penting diketahui bahwa nama plugin anda haruslah unik dan tidak bertentangan dengan plugin-plugin lain yang kemungkinannya juga merespons ke event inti yang sama.

Jika anda membuat plugin yang merespons ke event sistem bukan inti, pilihan nama untuk tag group="xxx" anda haruslah berbeda dari kategori inti yang ada sekarang.

TIPS: Apabila anda menambahkan atribut method="upgrade" ke dalam tag <extension>, plugin ini dapat dipasang tanpa melepas versi yang sebelumnya. Semua file yang ada akan digantikan, tapi file-file yang lama tidak akan dihapus.


Creating the Plugin

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.

The object-oriented way of writing plugins involves writing a subclass of CMSPlugin, a base class that implements the basic properties of plugins. In your methods, the following properties are available:

  • $this->params: the parameters set for this plugin by the administrator
  • $this->_name: the name of the plugin
  • $this->_type: the group (type) of the plugin
  • $this->db: the db object
  • $this->app: the application object

TIP: To use $this->db and $this->app, CMSPlugin tests if the property exists and is not private. If it is desired for the default objects to be used, create un-instantiated properties in the plugin class (i.e. protected $db; protected $app; in the same area as protected $autoloadLanguage = true;). The properties will not exist unless explicitly created.

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.

We also implement the SubscriberInterface here which is the major change from Joomla 1.5-3.x. Instead of the function name automatically being detected and being the same as the event name this allows you to have custom function names. This allows us to tell what plugins are implementing what functions and as parsing public methods in PHP code is slow gives a significant performance boost.

Note throughout the Joomla 4 series there is a deprecated layer that will cover plugins using the old naming strategy of plugin names being the same as the event name when SubscriberInterface is not implemented.

<?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;
	}
}
?>


Using Plugins in Your Code

If you are creating a plugin for a new, non-core event, remember to activate your plugin after you install it. Precede any reference to your new plugin with the PluginHelper::importPlugin() command.

Now that you've created your plugin, you will probably want to call it in your code. You might not: the Joomla core has a number of built-in events that you might want your plugin code to be registered to (and in that case you can ignore this section).

New Joomla 4 Way

The new way of doing this in Joomla 4 is to get the dispatcher and dispatch a named event.

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);

If you want to allow the user to modify values you can then use the event result and getResults back out of it. You can look at

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;
	}
}

Why have we introduced this name class over parameters? Well it makes it easier to introduce custom setters and getters for properties - currently a plugin can either completely change a property as it wants - for a components there's no way of imposing any limitations. Additionally it makes it much easier for developers to add and remove parameters in an event without having major b/c issues (as you are now calling defined methods and are not subject to a property being the 2nd argument of your function).

How to achieve maximum compatibility with Joomla 3

If you want to trigger an event in a similar way to the removed J3.x JEventDispatcher then you use code like this:

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

It is important to note that the parameters have to be in an array. The plugin function itself will get the parameters as an Event object if it implements the SubscriberInterface and as individual values if it does not, but this method will always return an array that the plugin returns.

Note that if ANY plugin in a group doesn't implement the SubscriberInterface then the result property (as both a named parameter and result from a plugin) is used as a special property and cannot be used.