Erweiterungen von Joomla! 3 zu Joomla! 4 migrieren

25. April 2024 · Programmierung · andreas · Kein Kommentar

Im Laufe der Jahre hat sich die Anzahl der von mir betreuten Joomla!-Installationen deutlich reduziert. Von mehreren Websites ist noch eine Intranet-Seite übriggeblieben, welche bis vor Kurzem noch mit Joomla! 3 lief.

Hauptgrund hierfür war die aus meiner Sicht nur spärlich vorhandene Dokumentation für Entwickler, welche Anpassungen an Erweiterungen bei Versionsänderungen zu machen sind. Während zum Beispiel der Wechsel zu Hugo auf Grund der guten Dokumentation in Kombination mit einem ebenso guten Forum nur wenig Frustmomente bot, war schon die Erfahrung beim Umstieg von Joomla! 1.5 auf 2.5 derart nervig, daß im Laufe der Zeit alle Websites von Joomla! zu WordPress migriert wurden, statt den Versuch zu starten, die bestehende Funktionalität unter der neuen Joomla!-Version lauffähig zu bekommen.

Nachdem inzwischen aber Joomla! 5 veröffentlicht ist, sollte auch die Intranet-Site zumindest auf Joomla! 4 migriert und somit auch die selbstentwickelten Erweiterungen angepasst werden, was sich wieder als frustrierendes Erlebnis entpuppte. Wenn es schon Zusammenstellungen wie “Potential backward compatibility issues in Joomla 4” gibt, wäre es hilfreich, wenn dort nicht als erstes ein “Content is Incomplete” ins Auge springen würde und viele der hier aufgelisteten Dinge auch dort zu finden wären.

In diesem Beitrag habe ich die verschiedenen Quellen gesammelt, mit denen ich die Erweiterungen mit Joomla! 4 lauffähig gemacht habe - am Ende des jeweiligen Abschnitts ist als Quelle die Seite angegeben, auf welcher ich als erstes auf meiner Suche nach Lösungen fündig wurde. Vieles davon ist sog. Legacy-Funktionalität, d.h. ich habe nicht nach aktueller Philosophie neu entwickelt, sondern versucht, mit minimalem Aufwand die jeweilige Erweiterung zu reparieren und wieder funktionsfähig zu bekommen.

JArrayHelper

“JArrayHelper” wurde entfernt, stattdessen übernimmt “Joomla\Utilities\ArrayHelper” die Funktionalität:

JArrayHelper::toInteger($cid)

wird zu

Joomla\Utilities\ArrayHelper::toInteger($cid)

Quelle: Potential backward compatibility issues in Joomla 4

JRequest

“JRequest” wurde entfernt und wurde durch die “Input”-Klasse ersetzt:

$option = JRequest::getCmd('option');

wird zu

$jinput = JFactory::getApplication()->input;
$option = $jinput->get('option');

Quelle: error Class ‘JRequest’ not found in Joomla 4

An den Inhalt des “POST”-Arrays kommt man nun auf folgenden Weg:

$post = $jinput->post->getArray();

Quelle: JRequest::get(‘post’, JREQUEST_ALLOWRAW) has been deprecated, what is the working code now?

Für die anderen Variablen gestaltet sich das ähnlich:

$val = $input->get->get(param_name, default_value, filter);
$val = $input->post->get(param_name, default_value, filter);
$val = $input->server->get(param_name, default_value, filter);

Quelle: Retrieving request data using JInput

Will man auf Datei-Uploads zugreifen, so ist auch hier neuerdings die “Input”-Klasse zuständig:

$upload = JRequest::get('files')['document_upload']

wird zu

$upload = $jinput->files->get('document_upload')

Quelle: How to use the filesystem package

MysqliDriver

Beim MysqliDriver wurde die “query”-Methode entfernt und durch “execute” ersetzt:

if (!$db->query()) { JError::raiseWarning( 500, $db->getError() ); }

wird zu

if (!$db->execute()) { JError::raiseWarning( 500, $db->getError() ); }

Quelle: Call to undefined method Joomla\Database\Mysqli\MysqliDriver::stderr()

JSubmenuHelper

Im Administrationsbereich muß die Verwendung von “JSubmenuHelper” durch “JHtmlSidebar” ersetzt und ggf. muß auch noch das Template entsprechend angepasst werden:

JSubmenuHelper::addEntry(
	...
);

wird zu

JHtmlSidebar::addEntry(
	...
);

Quelle: Replacing JSubmenuHelper with JHtmlSidebar / Administrative menu in Joomla 4

parseXMLInstallFile

“parseXMLInstallFile” wird nun nicht mehr von “ApplicationHelper” sondern von “JInstaller” bereitgestellt:

$xmldata = JApplicationHelper::parseXMLInstallFile(JPATH_COMPONENT . DIRECTORY_SEPARATOR . 'tinydoc.xml'))

wird zu

$xmldata = JInstaller::parseXMLInstallFile(JPATH_COMPONENT . DIRECTORY_SEPARATOR . 'tinydoc.xml'))

Quelle: Joomla! API Deprecated Elements

JHtmlBehavior::calendar

“behavior::calendar” wurde entfernt, weshalb eine Zeile wie

JHTML::_('behavior.calendar');

aus dem Quellcode entfernt werden muß - sonst kommt es zu einem “behavior::calendar not found”-Fehler.

Quelle: Remove the deprecated code in the JHtmlBehavior class

grid.published

“grid.published” wurde um ein vorangestelltes “j” ergänzt und statt dem kompletten Element gibt es nur noch den “published”-Status als Parameter:

JHTML::_('grid.published', $row, $i)

wird zu

JHtml::_('jgrid.published', $row->published, $i)

Quelle: Html::_(‘grid.published’) does not pick up tick.png icon

JFile::read($file);

“JFile::read($file);” wurde ebenfalls entfernt, stattdessen soll nun die PHP-Funktion “file_get_contents()” verwendet werden.

$documentData = JFile::read($file);

wird zu

$documentData = file_get_contents($file);

Quelle Joomla! API Deprecated Elements

assignRef

Eine Änderung, die mit PHP5 Einzug gehalten hat, macht die Verwendung von “assignRef” überflüssig:

$this->assignRef('document', $document)

wird zu

$this->document = $document

Quelle: Joomla 3 - What to use instead of assignRef?

JResponse::setHeader

Mit “JResponse::setHeader” können keine zusätzlichen Header-Daten mehr Richtung Browser geschickt werden, hierfür ist nun “JFactory::getApplication()->setHeader” zuständig.

JResponse::setHeader('Content-Transfer-Encoding',  'binary')

wird zu

$app = JFactory::getApplication();
$app->setHeader('Content-Transfer-Encoding', 'binary');

Quelle: How to add custom Header Data in Joomla

Routing

Auch beim Routing einer Komponente hat sich einiges geändert und es gibt zwei Möglichkeiten, einen Router für Joomla! 4 zu implementieren:

Der vielleicht schönere Weg ist über einen eigenen RouterView. Eine gute Erklärung, was in welcher Reihenfolge zu tun ist, findet sich im Google Groups-Beitrag “How to use the new View Based Router Classes”.

Eher quick & dirty ist der Ansatz des “Traditional Routers”, bei dem große Teile des bestehenden Router-Codes übernommen werden können:

In der “router.php” eine Klasse mit dem Namen der Komponente (ohne “com_"-Präfix) anlegen, welche von “RouterInterface” abgeleitet wird:

use Joomla\CMS\Component\Router\RouterInterface;

class TinyDocRouter implements RouterInterface {
...
}

In dieser Klasse dann einen Konstruktor sowie die Funktionen “build”, “parse” und “preprocess” definieren. Sofern eine dieser Funktionen nicht definiert ist, beschwert sich Joomla! mit der Fehlermeldung “Error: Class TinyDocRouter contains … abstract method and must therefore be declared abstract or implement the remaining methods”.

public function __construct($application, $menu) {

	...
}

public function build(&$query) {

	$segments = [];
	...
	return $segments;
}

public function parse(&$segments) {

	$vars = [];
	...
	// clear segments
	$segments = [];

	return $vars;
}

public function preprocess($query) {

	...
	return $query;
}

In die Funktionen “build” und “parse” kann der alte Router-Code übernommen werden, es ist aber darauf zu achten, in der Funktion “parse” das “segments”-Array zu leeren, da es sonst zu einem “HTTP 404 (page not found)"-Fehler kommt.

Quellen: Developing an MVC Component/Upgrading to Joomla4 sowie TraditionalRouter.php

JError::raiseWarning, JError::raiseError und $this->setError

Auch “$this->setError” ist - genau wie “JError::raiseWarning” oder “JError::raiseError” - mit Joomla! 4 Geschichte. Die Aufgabe kann stattdessen von “$this->setMessage” übernommen werden:

$this->setError(JText::_('Error uploading file'));

wird zu

$this->setMessage(JText::_('Error uploading file'), 'error');

Im zweiten Parameter kann festgelegt werden, ob es sich um eine Information (= Default ‘message’), einen Fehler (’error’) oder eine Warnung (‘warning’) handelt.

Quelle: What is the best way for raising an error message in Joomla 4