J3.x

Difference between revisions of "Developing an MVC Component/Adding Tags/de"

From Joomla! Documentation

< J3.x:Developing an MVC Component
(Updating to match new version of source page)
 
Line 140: Line 140:
 
CREATE UNIQUE INDEX `aliasindex` ON `#__helloworld` (`alias`, `catid`);
 
CREATE UNIQUE INDEX `aliasindex` ON `#__helloworld` (`alias`, `catid`);
  
INSERT INTO `#__helloworld` (`greeting`,`alias`,`language`, `parent_id`, `level`, `path`, `lft`, `rgt`) VALUES
+
INSERT INTO `#__helloworld` (`greeting`,`alias`,`language`, `parent_id`, `level`, `path`, `lft`, `rgt`, `published`) VALUES
('helloworld root','helloworld-root-alias','en-GB', 0, 0, '', 0, 5),
+
('helloworld root','helloworld-root-alias','en-GB', 0, 0, '', 0, 5, 1),
('Hello World!','hello-world','en-GB', 1, 1, 'hello-world', 1, 2),
+
('Hello World!','hello-world','en-GB', 1, 1, 'hello-world', 1, 2, 0),
('Goodbye World!','goodbye-world','en-GB', 1, 1, 'goodbye-world', 3, 4);
+
('Goodbye World!','goodbye-world','en-GB', 1, 1, 'goodbye-world', 3, 4, 0);
  
 
INSERT INTO `#__content_types` (`type_title`, `type_alias`, `content_history_options`, `table`, `field_mappings`, `router`)  
 
INSERT INTO `#__content_types` (`type_title`, `type_alias`, `content_history_options`, `table`, `field_mappings`, `router`)  

Latest revision as of 18:35, 21 March 2020

Other languages:
Deutsch • ‎English • ‎español • ‎français • ‎italiano • ‎中文(台灣)‎
Joomla! 
3.x
Tutorial
Entwicklung einer MVC Komponente

Hinzufügen einer variablen Anfrage zum Menütyp

Benutzung der Datenbank

Backend-Grundlagen

Hinzufügen von Sprachen

Hinzufügen von Aktionen im Backend

Hinzufügen von Dekorationen zum Backend

Hinzufügen von Validierungen

Hinzufügen von Kategorien

Hinzufügen einer Konfiguration

  1. Hinzufügen von ACL

Hinzufügen eines Installations-/Deinstallations-/Aktualisierungs-Skriptes

Hinzufügen eines Formulars im Frontend

  1. Hinzufügen von Bildern
  2. Hinzufügen von Karten
  3. Hinzufügen von AJAX
  4. Hinzufügen eines Alias

Benutzung des Sprachenfilters

  1. Hinzufügen von Modalen
  2. Hinzufügen von Verknüpfungen
  3. Hinzufügen der Freigabe/Sperrung von Elementen
  4. Hinzufügen von Sortierungen
  5. Hinzufügen von Ebenen
  6. Hinzufügen von Versionen
  7. Hinzufügen von Schlagwörtern (Tags)
  8. Hinzufügen von Zugriffsebenen
  9. Hinzufügen von Stapelverarbeitungen
  10. Hinzufügen eines Cache-Speichers
  11. Hinzufügen eines Feeds

Hinzufügen eines Aktualisierungs-Servers

  1. Adding Custom Fields
  2. Upgrading to Joomla4



Dies ist eine Artikel-Serie mit Tutorials über die Entwicklung einer Modal-View-Controller Komponente für Joomla! VersionJoomla 3.x.

Beginne mit der Einführung und navigiere durch die Artikel dieser Serie mit Hilfe des Buttons am Ende der Seite oder der Box auf der rechten Seite ("Artikel in dieser Serie").



Dieses Tutorial ist ein Teil der Artikel-Serie Entwicklung einer MVC-Komponente für Joomla! 3.2. Es wird empfohlen, die vorherigen Teile des Tutorials zu lesen, bevor du hier fortfährst.

In diesem Schritt fügen wir Schlagwörter zu unserer Helloworld-Komponente hinzu. Ein Video, welches diesen Schritt begleitet, ist hier Adding Tags verfügbar.

Einführung

Schlagwörter bieten Besuchern einer Webseite einen alternativen Weg, Inhalte nach Sachgebieten zu entdecken, statt dafür Webseitenmenüs zu benutzen. Dafür werden Elemente den Schlagwörtern zugeordnet. Joomla! ermöglicht es, Schlagwörter zu Einträgen, Newsfeeds, Kategorien und Kontakten hinzuzufügen. Während ein Element höchstens zu einer Kategorie gehören kann, können demselben Element eventuell viele Schlagwörter zugeordnet werden. In diesem Schritt erweitern wir die Funktionalität der Datensätze und Kategorien unserer HelloWorld-Seite.

Joomla! hat die Funktion für Schlagwörter aufbauend auf den UCM-Tabellen (Unified Content Model = vereinheitlichtes Inhaltsmodell) in der Datenbank entworfen. Das UCM war ein Konzept, welches in Joomla! nur teilweise entwickelt wurde, und es hat eine ungewisse Zukunft. Für die verschiedenen Inhaltstypen in Joomla! - Einträge, Kontakte, Newsfeeds, etc. - zielte es darauf ab, oft verwendete Informationen (Felder) in einer gemeinsamen Datenbanktabelle zu speichern (die Tabelle ucm_content, zusammen mit der verbundenen Tabelle ucm_base). Nur die Felder, die für einen Inhaltstyp spezifisch sind, werden in einer davon getrennten Tabelle gespeichert.

Joomla Tags E-R diagram

Die Joomla!-Funktion für Schlagwörter wurde aufbauend auf UCM installiert. Dies ist im Entitäten-Beziehungsdiagramm zu sehen, wobei die Tabelle contentitem_tag_map Beziehungen des Typs n:n zwischen Schlagwörtern und Inhaltselementen umsetzt. Das bedeutet für uns, dass wir Schlüsselfelder der Datensätze und Kategorien von Helloworld benötigen, die in die ucm_content Tabelle kopiert werden. Wir ermöglichen das durch die Festlegung der Konfigurationsdaten in der Tabelle content_types, ähnlich, wie wir dies im vorherigen Schritt Hinzufügen von Versionen getan haben.

Die Joomla! Schlagwörter benutzen dasselbe Observer-Muster wie die Joomla! Versionierung: wann immer ein Datensatz in der JTable-Klasse gespeichert wird, werden die Events onBeforeStore, onAfterStore und onBeforeDelete vom Schlagwort-Code aufgegriffen. Wenn ein Schlagwort zum ersten Mal einem Datensatz zugewiesen wird, werden die Felder vom Originaldatensatz in die Tabelle ucm_content kopiert, unter Verwendung der Feldzuordnungen, die in den Konfigurationsdaten in der Tabelle content_types definiert sind. Außerdem wird ein gekürzter Datensatz in die Tabelle ucm_base geschrieben. Anschließend wird der Datensatz in die Tabelle contentitem_tag_map geschrieben, der wiederum auf die Tabelle ucm_content und die entsprechenden Schlagwort-Datensätze (in der Tabelle tags) verlinkt. So wird ein Schlagwort x dem Inhaltselement y zugeordnet.

Vorgehensweise

Einige Details zur Integration von Schlagwörtern in eine Erweiterung sind in Using Tags in an Extension zu finden. In unseren Datenbankdateien fügen wir die Konfigurationsdaten zu den Datensätzen in helloworld content_types hinzu. Diese ermöglichen es der Schlagwort-Funktionalität von Joomla, die wichtigen Felder aus unseren Helloworld-Datensätzen und Helloworld-Kategoriedatensätzen in die Tabelle ucm_content zu kopieren.

Wir fügen den Code zu unserer Helloworld-Tabellenklasse hinzu, um den Observer für Schlagwörter dazu zu bringen, auf Ereignisse zu reagieren, die mit unserer Helloworld-Tabellenklasse verbunden sind. Das Hinzufügen zum Konstruktor unserer Helloworld-Tabellenklasse bedeutet, dass dies sowohl für das Backend als auch für das Frontend gilt, da beide den gleichen Helloworld-Tabellencode verwenden.

Wir fügen ein Standard-Formularfeld für Schlagwörter zu unserem Admin-Formular zum Bearbeiten eines Helloworld-Datensatzes und zu unserem Frontend-Formular zum Erstellen eines Helloworld-Datensatzes hinzu. Das Schlagwort-Feld hat 2 Funktionsarten, siehe auch Tag form field type:

  1. Verschachtelt: Die Schlagwörter werden als feste Liste präsentiert, aus der der Benutzer eines oder mehrere auswählen kann (vorausgesetzt, die Feldoption multiple="true" ist gesetzt). Schlagwörter sind als Baumansicht und mit in einer verschachtelten Tabellenstruktur implementiert, so dass in der Darstellung innerhalb des Schlagwort-Feldes untergeordnete Scjhlagwörter eingerückt sind, genauso wie Untermenüs und Unterkategorien in Admin-Formularen eingerückt sind.
  2. Ajax (Standard): Die Schlagwörter werden als Liste in Form verschachtelter Tabellenpfade dargestellt, anstatt die untergeordneten Schlagwörter einzurücken. Zusätzlich kann der Benutzer freien Text eingeben: Wenn eine Anzahl von Zeichen eingegeben wird, führt Joomla eine Ajax-Anfrage an den Server aus, um Schlagworte zu finden, die mit dem eingegebenen Text übereinstimmen. Wenn keine passenden Schlagwörter gefunden werden, kann der Benutzer sofort ein neues Schlagwort erstellen, außer wenn die Option custom="deny" zum Schlagwort-Feld hinzugefügt wird.

Wir verwenden den Ajax-Modus im Admin-Formular und den verschachtelten Modus im Frontend-Formular.

Im Hinblick auf die Darstellung der Schlagwörter eines Datensatzes wollen wir mit Hilfe des Admin-Bearbeitungsformulars zeigen, welche Schlagwörter bereits vorhanden sind. Dazu wird das Feld mit Schlagwörtern vorausgefüllt, wobei wir diese Daten durch unseren Model-Code aus der Datenbank abfragen. Wir werden die Tags in der Admin-Helloworlds-Liste nicht anzeigen.

Im Frontend werden wir die Schlagwörter, die mit einem Helloworld-Datensatz verbunden sind, im Helloworld-Menüpunkt anzeigen. Dafür werden wir ein Standard-Joomla-Layout verwenden.

Datenbank

In unserer Datenbank-Update-Datei fügen wir in den Datensätzen aus der Tabelle content_types die Tabellen- und Feldzuordnungsinformationen hinzu, um diese Feldwerte in die Tabelle ucm_content zu kopieren. Für die Kategorie-Einträge folgen wir einfach dem Beispiel der Joomla-Kernkomponenten. Für die Helloworld-Datensätze müssen wir uns überlegen, welche Schlagwort-bezogenen Daten auf anderen Webseiten angezeigt werden sollen, z. B. wenn der Administrator einen Menüeintrag erstellt hat, der einen der Menüeintragstypen für Schlagworte verwendet. Solche Seiten können Links zu den Helloworld-Datensätzen enthalten, so dass Felder wie Alias und Sprache, die bei der Konstruktion der URLs verwendet werden, einbezogen werden sollten. Die "speziellen" Feldzuordnungen werden derzeit nicht verwendet, so dass diese ausgelassen werden können.

Administratoren können Menüeintragstypen (vom Typ Schlagwörter) für das Frontend definieren, die Links zu Elementen mit bestimmten Schlagwörtern anzeigen.Falls ein Eintrag ein Helloworld-Datensatz oder ein Helloworld-Kategoriedatensatz ist, sucht Joomla nach einer Route-Helper-Methode, die im Feld ucm_content router angegeben werden muss. Wir müssen die Datensätze also aktualisieren, so dass sie diese Werte enthalten. Wir definieren diese Methoden unten im Abschnitt Route-Helper für das Frontend.

admin/sql/updates/mysql/0.0.28.sql

UPDATE `#__content_types` SET
`table` = '{"special":{"dbtable":"#__helloworld","key":"id","type":"Helloworld","prefix":"HelloworldTable","config":"array()"},
"common":{"dbtable":"#__ucm_content","key":"ucm_id","type":"Corecontent","prefix":"JTable","config":"array()"}}',
`field_mappings` = 
'{"common": {
    "core_content_item_id": "id",
    "core_title": "greeting",
    "core_state": "published",
    "core_alias": "alias",
    "core_language":"language", 
    "core_created_time": "created",
    "core_body": "description",
    "core_catid": "catid"
  }}',
`router` = 'HelloworldHelperRoute::getHelloworldRoute'
WHERE `type_alias` = 'com_helloworld.helloworld';

UPDATE `#__content_types` SET
`table` = '{"special":{"dbtable":"#__categories","key":"id","type":"Category","prefix":"JTable","config":"array()"},
"common":{"dbtable":"#__ucm_content","key":"ucm_id","type":"Corecontent","prefix":"JTable","config":"array()"}}',
`field_mappings` = 
'{"common": {
	"core_content_item_id":"id",
	"core_title":"title",
	"core_state":"published",
	"core_alias":"alias",
	"core_created_time":"created_time",
	"core_modified_time":"modified_time",
	"core_body":"description", 
	"core_hits":"hits",
	"core_publish_up":"null",
	"core_publish_down":"null",
	"core_access":"access", 
	"core_params":"params", 
	"core_featured":"null", 
	"core_metadata":"metadata", 
	"core_language":"language", 
	"core_images":"null", 
	"core_urls":"null", 
	"core_version":"version",
	"core_ordering":"null", 
	"core_metakey":"metakey", 
	"core_metadesc":"metadesc", 
	"core_catid":"parent_id", 
	"core_xreference":"null", 
	"asset_id":"asset_id"}, 
  "special":{
    "parent_id":"parent_id",
	"lft":"lft",
	"rgt":"rgt",
	"level":"level",
	"path":"path",
	"extension":"extension",
	"note":"note"}}',
`router` = 'HelloworldHelperRoute::getCategoryRoute'
WHERE `type_alias` = 'com_helloworld.category';

admin/sql/install.mysql.utf8.sql

DROP TABLE IF EXISTS `#__helloworld`;

CREATE TABLE `#__helloworld` (
	`id`       INT(11)     NOT NULL AUTO_INCREMENT,
	`asset_id` INT(10)     NOT NULL DEFAULT '0',
	`created`  DATETIME    NOT NULL DEFAULT '0000-00-00 00:00:00',
	`created_by`  INT(10) UNSIGNED NOT NULL DEFAULT '0',
	`checked_out` INT(10) NOT NULL DEFAULT '0',
	`checked_out_time` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
	`greeting` VARCHAR(25) NOT NULL,
	`description` VARCHAR(4000) NOT NULL DEFAULT '',
	`alias`  VARCHAR(40)  NOT NULL DEFAULT '',
    `language`  CHAR(7)  NOT NULL DEFAULT '*',
	`parent_id`	int(10)    NOT NULL DEFAULT '1',
	`level`	int(10)    NOT NULL DEFAULT '0',
	`path`	VARCHAR(400)    NOT NULL DEFAULT '',
	`lft`	int(11)    NOT NULL DEFAULT '0',
	`rgt`	int(11)    NOT NULL DEFAULT '0',
	`published` tinyint(4) NOT NULL DEFAULT '1',
	`catid`	    int(11)    NOT NULL DEFAULT '0',
	`params`   VARCHAR(1024) NOT NULL DEFAULT '',
	`image`   VARCHAR(1024) NOT NULL DEFAULT '',
	`latitude` DECIMAL(9,7) NOT NULL DEFAULT 0.0,
	`longitude` DECIMAL(10,7) NOT NULL DEFAULT 0.0,
	PRIMARY KEY (`id`)
)
	ENGINE =MyISAM
	AUTO_INCREMENT =0
	DEFAULT CHARSET =utf8;

CREATE UNIQUE INDEX `aliasindex` ON `#__helloworld` (`alias`, `catid`);

INSERT INTO `#__helloworld` (`greeting`,`alias`,`language`, `parent_id`, `level`, `path`, `lft`, `rgt`, `published`) VALUES
('helloworld root','helloworld-root-alias','en-GB', 0, 0, '', 0, 5, 1),
('Hello World!','hello-world','en-GB', 1, 1, 'hello-world', 1, 2, 0),
('Goodbye World!','goodbye-world','en-GB', 1, 1, 'goodbye-world', 3, 4, 0);

INSERT INTO `#__content_types` (`type_title`, `type_alias`, `content_history_options`, `table`, `field_mappings`, `router`) 
VALUES
('Helloworld', 'com_helloworld.helloworld', 
'{"formFile":"administrator\\/components\\/com_helloworld\\/models\\/forms\\/helloworld.xml", 
"hideFields":["asset_id","checked_out","checked_out_time","version","lft","rgt","level","path"], 
"ignoreChanges":["checked_out", "checked_out_time", "path"],
"convertToInt":[], 
"displayLookup":[
{"sourceColumn":"created_by","targetTable":"#__users","targetColumn":"id","displayColumn":"name"},
{"sourceColumn":"parent_id","targetTable":"#__helloworld","targetColumn":"id","displayColumn":"greeting"},
{"sourceColumn":"catid","targetTable":"#__categories","targetColumn":"id","displayColumn":"title"}]}',
'{"special":{"dbtable":"#__helloworld","key":"id","type":"Helloworld","prefix":"HelloworldTable","config":"array()"},
"common":{"dbtable":"#__ucm_content","key":"ucm_id","type":"Corecontent","prefix":"JTable","config":"array()"}}',
'{"common": {
    "core_content_item_id": "id",
    "core_title": "greeting",
    "core_state": "published",
    "core_alias": "alias",
    "core_language":"language", 
    "core_created_time": "created",
    "core_body": "description",
    "core_catid": "catid"
  }}',
'HelloworldHelperRoute::getHelloworldRoute'),
('Helloworld Category', 'com_helloworld.category',
'{"formFile":"administrator\\/components\\/com_categories\\/models\\/forms\\/category.xml", 
"hideFields":["asset_id","checked_out","checked_out_time","version","lft","rgt","level","path","extension"], 
"ignoreChanges":["modified_user_id", "modified_time", "checked_out", "checked_out_time", "version", "hits", "path"],
"convertToInt":["publish_up", "publish_down"], 
"displayLookup":[
{"sourceColumn":"created_user_id","targetTable":"#__users","targetColumn":"id","displayColumn":"name"},
{"sourceColumn":"access","targetTable":"#__viewlevels","targetColumn":"id","displayColumn":"title"},
{"sourceColumn":"modified_user_id","targetTable":"#__users","targetColumn":"id","displayColumn":"name"},
{"sourceColumn":"parent_id","targetTable":"#__categories","targetColumn":"id","displayColumn":"title"}]}',
'{"special":{"dbtable":"#__categories","key":"id","type":"Category","prefix":"JTable","config":"array()"},
"common":{"dbtable":"#__ucm_content","key":"ucm_id","type":"Corecontent","prefix":"JTable","config":"array()"}}',
'{"common": {
	"core_content_item_id":"id",
	"core_title":"title",
	"core_state":"published",
	"core_alias":"alias",
	"core_created_time":"created_time",
	"core_modified_time":"modified_time",
	"core_body":"description", 
	"core_hits":"hits",
	"core_publish_up":"null",
	"core_publish_down":"null",
	"core_access":"access", 
	"core_params":"params", 
	"core_featured":"null", 
	"core_metadata":"metadata", 
	"core_language":"language", 
	"core_images":"null", 
	"core_urls":"null", 
	"core_version":"version",
	"core_ordering":"null", 
	"core_metakey":"metakey", 
	"core_metadesc":"metadesc", 
	"core_catid":"parent_id", 
	"core_xreference":"null", 
	"asset_id":"asset_id"}, 
  "special":{
    "parent_id":"parent_id",
	"lft":"lft",
	"rgt":"rgt",
	"level":"level",
	"path":"path",
	"extension":"extension",
	"note":"note"}}',
'HelloworldHelperRoute::getCategoryRoute');

Mit unserem SQL-Deinstallationsskript müssen wir alle Helloworld-Datensätze entfernen, die in die Tabellen ucm_base und ucm_content kopiert wurden. Außerdem müssen alle Links zu Helloworld-Datensätzen in der Tabelle contentitem_tag_map entfernt werden.

admin/sql/uninstall.mysql.utf8.sql

DROP TABLE IF EXISTS `#__helloworld`;
DELETE FROM `#__ucm_history` WHERE ucm_type_id in 
	(select type_id from `#__content_types` where type_alias in ('com_helloworld.helloworld','com_helloworld.category'));
DELETE FROM `#__ucm_base` WHERE ucm_type_id in 
	(select type_id from `#__content_types` WHERE type_alias in ('com_helloworld.helloworld','com_helloworld.category'));
DELETE FROM `#__ucm_content` WHERE core_type_alias in ('com_helloworld.helloworld','com_helloworld.category');
DELETE FROM `#__contentitem_tag_map`WHERE type_alias in ('com_helloworld.helloworld','com_helloworld.category');
DELETE FROM `#__content_types` WHERE type_alias in ('com_helloworld.helloworld','com_helloworld.category');

Einrichtung des Observers für Schlagwörter

Unter Hinzufügen von Versionen haben wir die Funktion JObserverMapper::addObserverClassToClass() verwendet, um den Observer für die Versionierung einzurichten. Für den Observer für Schlagwörter verwenden wir jedoch den alternativen Mechanismus JTableObserverTags::createObserver(). Wir rufen diesen in unserem Helloworld-Tabellenkonstruktor auf und stellen sicher, dass er nach dem übergeordneten Konstruktor aufgerufen wird.

admin/tables/helloworld.php

<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_helloworld
 *
 * @copyright   Copyright (C) 2005 - 2015 Open Source Matters, Inc. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */
// No direct access
defined('_JEXEC') or die('Restricted access');

/**
 * Hello Table class
 *
 * @since  0.0.1
 */
class HelloWorldTableHelloWorld extends JTableNested
{
	/**
	 * Constructor
	 *
	 * @param   JDatabaseDriver  &$db  A database connector object
	 */
	function __construct(&$db)
	{
		JObserverMapper::addObserverClassToClass('JTableObserverContenthistory', 'HelloWorldTableHelloWorld', array('typeAlias' => 'com_helloworld.helloworld'));
		parent::__construct('#__helloworld', 'id', $db);
		JTableObserverTags::createObserver($this, array('typeAlias' => 'com_helloworld.helloworld'));
	}
	/**
	 * Overloaded bind function
	 *
	 * @param       array           named array
	 * @return      null|string     null is operation was satisfactory, otherwise returns an error
	 * @see JTable:bind
	 * @since 1.5
	 */
	public function bind($array, $ignore = '')
	{
		if (isset($array['params']) && is_array($array['params']))
		{
			// Convert the params field to a string.
			$parameter = new JRegistry;
			$parameter->loadArray($array['params']);
			$array['params'] = (string)$parameter;
		}
        
        if (isset($array['imageinfo']) && is_array($array['imageinfo']))
		{
			// Convert the imageinfo array to a string.
			$parameter = new JRegistry;
			$parameter->loadArray($array['imageinfo']);
			$array['image'] = (string)$parameter;
		}
        
        // Bind the rules.
		if (isset($array['rules']) && is_array($array['rules']))
		{
			$rules = new JAccessRules($array['rules']);
			$this->setRules($rules);
		}
        
		if (isset($array['parent_id']))
		{
			if (!isset($array['id']) || $array['id'] == 0)
			{   // new record
				$this->setLocation($array['parent_id'], 'last-child');
			}
			elseif (isset($array['helloworldordering']))
			{
				// when saving a record load() is called before bind() so the table instance will have properties which are the existing field values
				if ($this->parent_id == $array['parent_id'])
				{
					// If first is chosen make the item the first child of the selected parent.
					if ($array['helloworldordering'] == -1)
					{
						$this->setLocation($array['parent_id'], 'first-child');
					}
					// If last is chosen make it the last child of the selected parent.
					elseif ($array['helloworldordering'] == -2)
					{
						$this->setLocation($array['parent_id'], 'last-child');
					}
					// Don't try to put an item after itself. All other ones put after the selected item.
					elseif ($array['helloworldordering'] && $this->id != $array['helloworldordering'])
					{
						$this->setLocation($array['helloworldordering'], 'after');
					}
					// Just leave it where it is if no change is made.
					elseif ($array['helloworldordering'] && $this->id == $array['helloworldordering'])
					{
						unset($array['helloworldordering']);
					}
				}
				// Set the new parent id if parent id not matched and put in last position
				else
				{
					$this->setLocation($array['parent_id'], 'last-child');
				}
			}
		}

		return parent::bind($array, $ignore);
	}
    
    /**
	 * Method to compute the default name of the asset.
	 * The default name is in the form `table_name.id`
	 * where id is the value of the primary key of the table.
	 *
	 * @return	string
	 * @since	2.5
	 */
	protected function _getAssetName()
	{
		$k = $this->_tbl_key;
		return 'com_helloworld.helloworld.'.(int) $this->$k;
	}
	/**
	 * Method to return the title to use for the asset table.
	 *
	 * @return	string
	 * @since	2.5
	 */
	protected function _getAssetTitle()
	{
		return $this->greeting;
	}
	/**
	 * Method to get the asset-parent-id of the item
	 *
	 * @return	int
	 */
	protected function _getAssetParentId(JTable $table = NULL, $id = NULL)
	{
		// We will retrieve the parent-asset from the Asset-table
		$assetParent = JTable::getInstance('Asset');
		// Default: if no asset-parent can be found we take the global asset
		$assetParentId = $assetParent->getRootId();

		// Find the parent-asset
		if (($this->catid)&& !empty($this->catid))
		{
			// The item has a category as asset-parent
			$assetParent->loadByName('com_helloworld.category.' . (int) $this->catid);
		}
		else
		{
			// The item has the component as asset-parent
			$assetParent->loadByName('com_helloworld');
		}

		// Return the found asset-parent-id
		if ($assetParent->id)
		{
			$assetParentId=$assetParent->id;
		}
		return $assetParentId;
	}

	public function check()
	{
		$this->alias = trim($this->alias);
		if (empty($this->alias))
		{
			$this->alias = $this->greeting;
		}
		$this->alias = JFilterOutput::stringURLSafe($this->alias);
		return true;
	}

	public function delete($pk = null, $children = false)
	{
		return parent::delete($pk, $children);
	}
}

Bearbeitungsformular im Admin-Bereich

Wir fügen das Joomla-Standardfeld für Schlagwörter in das Admin-Bearbeitungsformular ein. Damit kann der Administrator mehrere Schlagwörter auswählen oder ein neues Schlagwort direkt im Feld erstellen.

Wir müssen außerdem ein kleines Problem lösen: Joomla bietet eine Funktion zur Anzeige der Elemente, die mit einem Schlagwort verbunden sind. Wenn wir diese jedoch (mit der derzeitigen Version des Formulars) verwenden würden, dann würde unser Helloworld-Eintrag nicht erscheinen, da Joomla nur veröffentlichte Elemente anzeigt. Der Standardwert des Felds published in unserer Helloworld-Tabelle ist zwar 1, so dass neue Datensätze standardmäßig den Status "veröffentlicht" erhalten. Das entsprechende Feld state in der Tabelle ucm_content (welches in diesem Fall maßgeblich ist) ist jedoch standardmäßig auf Null (nicht unveröffentlicht) gesetzt, was sich mit unserem jetzigen Formular auch nicht ändern würde..

Daher fügen wir ein Statusfeld namens "published" zum Formular hinzu, das wir standardmäßig auf 1 (veröffentlicht) setzen. Der Wert des Statusfelds wird somit in den Datensatz der Tabelle ucm_content kopiert (basierend auf unserer Feldzuordnung oben in der SQL-Datei) und stimmt so mit dem Wert im Helloworld-Datensatz überein.

admin/models/forms/helloworld.xml

<?xml version="1.0" encoding="utf-8"?>
<form
				addrulepath="/administrator/components/com_helloworld/models/rules"
>
	<fieldset
				name="details"
				label="COM_HELLOWORLD_HELLOWORLD_DETAILS"
	>
		<field
				name="id"
				type="hidden"
				/>
                <field
				name="greeting"
				type="text"
				label="COM_HELLOWORLD_HELLOWORLD_GREETING_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_GREETING_DESC"
				size="40"
				class="inputbox validate-greeting"
				validate="greeting"
				required="true"
				default=""
				/>
		<field 
				name="alias" 
				type="text" 
				label="JFIELD_ALIAS_LABEL"
				description="JFIELD_ALIAS_DESC"
				hint="JFIELD_ALIAS_PLACEHOLDER"
				size="40" 
				/>
        <field
				name="catid"
				type="category"
				extension="com_helloworld"
				class="inputbox"
				default=""
				label="COM_HELLOWORLD_HELLOWORLD_FIELD_CATID_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_FIELD_CATID_DESC"
				required="true"
		>
			<option value="0">JOPTION_SELECT_CATEGORY</option>
		</field>
		<field
				name="latitude"
				type="number"
				label="COM_HELLOWORLD_HELLOWORLD_FIELD_LATITUDE_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_FIELD_LATITUDE_DESC"
				min="-90.0"
				max="90.0"
				class="inputbox"
				required="true"
				default="0.0"
				/>
		<field
				name="longitude"
				type="number"
				label="COM_HELLOWORLD_HELLOWORLD_FIELD_LONGITUDE_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_FIELD_LONGITUDE_DESC"
				min="-180.0"
				max="180.0"
				class="inputbox"
				required="true"
				default="0.0"
				/>
		<field  
                name="language" 
        		type="contentlanguage" 
                label="JFIELD_LANGUAGE_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_FIELD_LANGUAGE_DESC"
		>
			<option value="*">JALL</option>
		</field>
		<field 	name="published" 
				type="list" 
				label="JSTATUS"
				description="JFIELD_PUBLISHED_DESC" 
				class="chzn-color-state"
				filter="intval" 	
				size="1" 
				default="1"
		>
			<option value="1">
				JPUBLISHED</option>
			<option value="0">
				JUNPUBLISHED</option>
		</field>
		<field 
				name="tags" 
				type="tag"
				label="JTAG" 
				description="JTAG_DESC"
				multiple="true"
		>
		</field>
		<field
				name="parent_id"
				type="helloworldparent"
				label="COM_HELLOWORLD_HELLOWORLD_FIELD_PARENT_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_FIELD_PARENT_DESC"
				default="1"
				filter="int">
		</field>
		<field
				name="helloworldordering"
				type="helloworldordering"
				label="JFIELD_ORDERING_LABEL"
				description="JFIELD_ORDERING_DESC"
				filter="int"
				size="1">
		</field>
		<field 	name="version_note"
				type="text"
				label="JGLOBAL_FIELD_VERSION_NOTE_LABEL"
				description="JGLOBAL_FIELD_VERSION_NOTE_DESC"
				class="inputbox" 
				size="45"
				labelclass="control-label">
		</field>
    </fieldset>
		<field 	name="description" 
				type="editor"
				label="COM_HELLOWORLD_HELLOWORLD_FIELD_DESCRIPTION_LABEL" 
				description="COM_HELLOWORLD_HELLOWORLD_FIELD_DESCRIPTION_DESC"
				filter="JComponentHelper::filterText" 
				buttons="true" 
		/>
	<fields name="imageinfo">
		<fieldset
				name="image-info"
				label="COM_HELLOWORLD_IMAGE_FIELDS"
		>
			<field
                name="image"
                type="media"
                preview="tooltip"
                label="COM_HELLOWORLD_HELLOWORLD_FIELD_IMAGE_LABEL"
                description="COM_HELLOWORLD_HELLOWORLD_FIELD_IMAGE_DESC" />
            <field name="alt"
                type="text"
                label="COM_HELLOWORLD_HELLOWORLD_FIELD_ALT_LABEL"
                description="COM_HELLOWORLD_HELLOWORLD_FIELD_ALT_DESC"
                size="30"/>
            <field name="caption"
                type="text"
                label="COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTION_LABEL"
                description="COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTION_DESC"
                size="30"/>
		</fieldset>
	</fields>
	<fields name="params">
		<fieldset
				name="params"
				label="JGLOBAL_FIELDSET_DISPLAY_OPTIONS"
		>
			<field
					name="show_category"
					type="list"
					label="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_LABEL"
					description="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_DESC"
					default=""
			>
				<option value="">JGLOBAL_USE_GLOBAL</option>
				<option value="0">JHIDE</option>
				<option value="1">JSHOW</option>
			</field>
		</fieldset>
	</fields>
	<fieldset
			name="accesscontrol"
			label="COM_HELLOWORLD_FIELDSET_RULES"
	>
    	<field
				name="asset_id"
				type="hidden"
				filter="unset"
				/>
    	<field
				name="rules"
				type="rules"
				label="COM_HELLOWORLD_FIELD_RULES_LABEL"
				filter="rules"
				validate="rules"
				class="inputbox"
				component="com_helloworld"
				section="message"
				/>
    </fieldset>
</form>

Im Formular müssen wir das Feld mit allen Schlagwörtern vorbefüllen, die bereits mit dem Datensatz verbunden sind. Das tun wir im Model mit Hilfe der Funktion getItem(). Diese benötigt nur die IDs der bereits gesetzten Schlagwörter. Die Namen aller verfügbaren Schlagwörter und ihre entsprechenden IDs sind dann als Optionen in einem HTML-<select>-Element (Auswahlliste) enthalten. Die bereits vorhandenen Schlagwörter erhalten das Attribut selected, das auf der Übereinstimmung ihrer IDs mit den Optionen basiert. (Es ist zu beachten, dass das HMTL-<select>-Element von der Javascript-Bibliothek Chosen konvertiert wird, so dass es nicht im Quelltext der Webseite zu sehen ist).

admin/models/helloworld.php

<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_helloworld
 *
 * @copyright   Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

// No direct access to this file
defined('_JEXEC') or die('Restricted access');

use Joomla\Registry\Registry;

/**
 * HelloWorld Model
 *
 * @since  0.0.1
 */
class HelloWorldModelHelloWorld extends JModelAdmin
{
    // JModelAdmin needs to know this for storing the associations 
	protected $associationsContext = 'com_helloworld.item';
    
	// Contenthistory needs to know this for restoring previous versions
	public $typeAlias = 'com_helloworld.helloworld';
	
	/**
	 * Method to override getItem to allow us to convert the JSON-encoded image information
	 * in the database record into an array for subsequent prefilling of the edit form
	 * We also use this method to prefill the tags and associations
	 */
	public function getItem($pk = null)
	{
		$item = parent::getItem($pk);
		if ($item AND property_exists($item, 'image'))
		{
			$registry = new Registry($item->image);
			$item->imageinfo = $registry->toArray();
		}

		if (!empty($item->id))
		{
			$tagsHelper = new JHelperTags;
			$item->tags = $tagsHelper->getTagIds($item->id, 'com_helloworld.helloworld');
		}
        
		// Load associated items
		if (JLanguageAssociations::isEnabled())
		{
			$item->associations = array();

			if ($item->id != null)
			{
				$associations = JLanguageAssociations::getAssociations('com_helloworld', '#__helloworld', 'com_helloworld.item', (int)$item->id);

				foreach ($associations as $tag => $association)
				{
					$item->associations[$tag] = $association->id;
				}
			}
		}
		return $item; 
	}
	
	/**
	 * Method to get a table object, load it if necessary.
	 *
	 * @param   string  $type    The table name. Optional.
	 * @param   string  $prefix  The class prefix. Optional.
	 * @param   array   $config  Configuration array for model. Optional.
	 *
	 * @return  JTable  A JTable object
	 *
	 * @since   1.6
	 */
	public function getTable($type = 'HelloWorld', $prefix = 'HelloWorldTable', $config = array())
	{
		return JTable::getInstance($type, $prefix, $config);
	}

	/**
	 * Method to get the record form.
	 *
	 * @param   array    $data      Data for the form.
	 * @param   boolean  $loadData  True if the form is to load its own data (default case), false if not.
	 *
	 * @return  mixed    A JForm object on success, false on failure
	 *
	 * @since   1.6
	 */
	public function getForm($data = array(), $loadData = true)
	{
		// Get the form.
		$form = $this->loadForm(
			'com_helloworld.helloworld',
			'helloworld',
			array(
				'control' => 'jform',
				'load_data' => $loadData
			)
		);

		if (empty($form))
		{
			return false;
		}

		return $form;
	}
    
    /**
	 * Method to preprocess the form to add the association fields dynamically
	 *
	 * @return     none
	 */
	protected function preprocessForm(JForm $form, $data, $group = 'helloworld')
	{
		// Association content items
		if (JLanguageAssociations::isEnabled())
		{
			$languages = JLanguageHelper::getContentLanguages(false, true, null, 'ordering', 'asc');

			if (count($languages) > 1)
			{
				$addform = new SimpleXMLElement('<form />');
				$fields = $addform->addChild('fields');
				$fields->addAttribute('name', 'associations');
				$fieldset = $fields->addChild('fieldset');
				$fieldset->addAttribute('name', 'item_associations');

				foreach ($languages as $language)
				{
					$field = $fieldset->addChild('field');
					$field->addAttribute('name', $language->lang_code);
					$field->addAttribute('type', 'modal_helloworld');
					$field->addAttribute('language', $language->lang_code);
					$field->addAttribute('label', $language->title);
					$field->addAttribute('translate_label', 'false');
				}

				$form->load($addform, false);
			}
		}
		parent::preprocessForm($form, $data, $group);
	}

	/**
	 * Method to get the script to be included on the form
	 *
	 * @return string	Script files
	 */
	public function getScript() 
	{
		return 'administrator/components/com_helloworld/models/forms/helloworld.js';
	}
	
	/**
	 * Method to get the data that should be injected in the form.
	 *
	 * @return  mixed  The data for the form.
	 *
	 * @since   1.6
	 */
	protected function loadFormData()
	{
		// Check the session for previously entered form data.
		$data = JFactory::getApplication()->getUserState(
			'com_helloworld.edit.helloworld.data',
			array()
		);

		if (empty($data))
		{
			$data = $this->getItem();
		}

		return $data;
	}
	/**
	 * Method to override the JModelAdmin save() function to handle Save as Copy correctly
	 *
	 * @param   The helloworld record data submitted from the form.
	 *
	 * @return  parent::save() return value
	 */
	public function save($data)
	{
		$input = JFactory::getApplication()->input;

		JLoader::register('CategoriesHelper', JPATH_ADMINISTRATOR . '/components/com_categories/helpers/categories.php');

		// Validate the category id
		// validateCategoryId() returns 0 if the catid can't be found
		if ((int) $data['catid'] > 0)
		{
			$data['catid'] = CategoriesHelper::validateCategoryId($data['catid'], 'com_helloworld');
		}

		// Alter the greeting and alias for save as copy
		if ($input->get('task') == 'save2copy')
		{
			$origTable = clone $this->getTable();
			$origTable->load($input->getInt('id'));

			if ($data['greeting'] == $origTable->greeting)
			{
				list($greeting, $alias) = $this->generateNewTitle($data['catid'], $data['alias'], $data['greeting']);
				$data['greeting'] = $greeting;
				$data['alias'] = $alias;
			}
			else
			{
				if ($data['alias'] == $origTable->alias)
				{
					$data['alias'] = '';
				}
			}
			// standard Joomla practice is to set the new record as unpublished
			$data['published'] = 0;
		}

		$result = parent::save($data);
		if ($result)
		{
			$this->getTable()->rebuild(1);
		}
		return $result;
	}
	/**
	 * Method to check if it's OK to delete a message. Overrides JModelAdmin::canDelete
	 */
	protected function canDelete($record)
	{
		if( !empty( $record->id ) )
		{
			return JFactory::getUser()->authorise( "core.delete", "com_helloworld.helloworld." . $record->id );
		}
	}
	/**
	 * Prepare a helloworld record for saving in the database
	 */
	protected function prepareTable($table)
	{
	}
	
	/**
	 * Save the record reordering after a record is dragged to a new position in the helloworlds view
	 */
	public function saveorder($idArray = null, $lft_array = null)
	{
		// Get an instance of the table object.
		$table = $this->getTable();

		if (!$table->saveorder($idArray, $lft_array))
		{
			$this->setError($table->getError());

			return false;
		}

		return true;
	}
}

Formular für das Frontend

Ähnlich wie beim Admin-Formular fügen wir dem Frontend-Formular ein Feld für Schlagwörter hinzu. Wir verwenden hier jedoch die verschachtelte Option und erlauben dem Benutzer nicht, neue Schlagwörter zu definieren. Wir fügen auch das Feld published mit dem Standardwert 1 ein, setzen dieses aber auf hidden.

site/models/forms/add-form.xml

<?xml version="1.0" encoding="utf-8"?>
<form
    addrulepath="/administrator/components/com_helloworld/models/rules"
    addfieldpath="/administrator/components/com_helloworld/models/fields"
    >
    <fieldset
				name="details"
				label="COM_HELLOWORLD_HELLOWORLD_DETAILS"
	>
		<field
				name="id"
				type="hidden"
				/>
		<field
				name="greeting"
				type="text"
				label="COM_HELLOWORLD_HELLOWORLD_GREETING_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_GREETING_DESC"
				size="40"
				class="inputbox"
				validate="greeting"
				required="true"
				hint="COM_HELLOWORLD_HELLOWORLD_GREETING_HINT"
				default=""
				/>
		<field 
				name="alias" 
				type="text" 
				label="JFIELD_ALIAS_LABEL"
				description="JFIELD_ALIAS_DESC"
				hint="JFIELD_ALIAS_PLACEHOLDER"
				size="40" 
				/>
		<field
				name="catid"
				type="category"
				extension="com_helloworld"
				class="inputbox"
				default=""
				label="COM_HELLOWORLD_HELLOWORLD_FIELD_CATID_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_FIELD_CATID_DESC"
				required="true"
				>
				<option value="0">JOPTION_SELECT_CATEGORY</option>
		</field>
		<fields name="imageinfo" label="COM_HELLOWORLD_HELLOWORLD_IMAGE_LABEL">
			<field
				name="image"
				type="file"
				label="COM_HELLOWORLD_HELLOWORLD_PICTURE_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_PICTURE_DESC" 
				accept="image/*"
				>
			</field>
			<field
 				name="caption"
				type="text"
				label="COM_HELLOWORLD_HELLOWORLD_CAPTION_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_CAPTION_DESC"
				size="40"
				class="inputbox"
				>
			</field>
			<field
				name="alt"
				type="text"
				label="COM_HELLOWORLD_HELLOWORLD_ALTTEXT_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_ALTTEXT_DESC"
				size="40"
				class="inputbox"
				>
			</field>
		</fields>
		<field
				name="language"
				type="contentlanguage"
				label="JFIELD_LANGUAGE_LABEL"
				description="JFIELD_LANGUAGE_DESC"
				class="inputbox">
		</field>
		<field 
				name="tags" 
				type="tag"
				label="JTAG" 
				description="JTAG_DESC"
				mode="nested"
				multiple="true">
		</field>
		<field
				name="message"
				type="textarea"
				rows="5"
				cols="80"
				label="COM_HELLOWORLD_HELLOWORLD_MESSAGE_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_MESSAGE_DESC"
				hint="COM_HELLOWORLD_HELLOWORLD_MESSAGE_HINT"
				required="true"
				>
        </field>
        <field
				name="captcha"
				type="captcha"
				label="COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTCHA_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTCHA_DESC"
				validate="captcha"
                >
		</field>
        <fields name="params">
            <field
                    name="show_category"
                    type="list"
                    label="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_LABEL"
                    description="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_DESC"
                    default=""
                    useglobal="true"
            >
                <option value="0">JHIDE</option>
                <option value="1">JSHOW</option>
            </field>
        </fields>
		<field
				name="parent_id"
				type="helloworldparent"
				label="COM_HELLOWORLD_HELLOWORLD_FIELD_PARENT_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_FIELD_PARENT_DESC"
				default="1"
				filter="int">
		</field>
		<field name="published" type="hidden" default="1" />
    </fieldset>
</form>

Anzeige des Helloworld-Datensatzes im Frontend

Im View-Code erhalten wir die Daten der Schlagwörter mit der Funktion TagsHelper::getItemTags(). Diesmal benötigen wir nicht nur die IDs, sondern auch andere Informationen wie den Alias und den Titel, damit der Link korrekt erstellt werden kann.

Im Layout-Code verwenden wir das Standard-Joomla-Layout für Schlagwörter, das in der Datei layouts/joomla/content/tags.php zu finden ist. Mit render() übergeben wir die Schlagwort-Daten, die in der View-Datei eingerichtet wurden. Es ist zu beachten, dass dieses Layout nur für die Verwendung im Frontend vorgesehen ist.

site/views/helloworld/view.html.php

<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_helloworld
 *
 * @copyright   Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

// No direct access to this file
defined('_JEXEC') or die('Restricted access');

/**
 * HTML View class for the HelloWorld Component
 *
 * @since  0.0.1
 */
class HelloWorldViewHelloWorld extends JViewLegacy
{
	/**
	 * Display the Hello World view
	 *
	 * @param   string  $tpl  The name of the template file to parse; automatically searches through the template paths.
	 *
	 * @return  void
	 */
	function display($tpl = null)
	{
		// Assign data to the view
		$this->item = $this->get('Item');

		// Check for errors.
		if (count($errors = $this->get('Errors')))
		{
			JLog::add(implode('<br />', $errors), JLog::WARNING, 'jerror');

			return false;
		}

		$this->addMap();

		$tagsHelper = new JHelperTags;
		$this->item->tags = $tagsHelper->getItemTags('com_helloworld.helloworld' , $this->item->id);

		$model = $this->getModel();
		$this->parentItem = $model->getItem($this->item->parent_id);
		$this->children = $model->getChildren($this->item->id);
		// getChildren includes the record itself (as well as the children) so remove this record
		unset($this->children[0]);

		// Display the view
		parent::display($tpl);
	}

	function addMap() 
	{
		$document = JFactory::getDocument();

		// everything's dependent upon JQuery
		JHtml::_('jquery.framework');

		// we need the Openlayers JS and CSS libraries
		$document->addScript("https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.6.4/ol.js");
		$document->addStyleSheet("https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.6.4/ol.css");

		// ... and our own JS and CSS
		$document->addScript(JURI::root() . "media/com_helloworld/js/openstreetmap.js");
		$document->addStyleSheet(JURI::root() . "media/com_helloworld/css/openstreetmap.css");

		// get the data to pass to our JS code
		$params = $this->get("mapParams");
		$document->addScriptOptions('params', $params);
	}
}

site/views/helloworld/tmpl/default.php

<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_helloworld
 *
 * @copyright   Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

// No direct access to this file
defined('_JEXEC') or die('Restricted access');
$lang = JFactory::getLanguage()->getTag();
if (JLanguageMultilang::isEnabled() && $lang)
{
    $query_lang = "&lang={$lang}";
}
else
{
    $query_lang = "";
}
?>
<h1><?php echo $this->item->greeting.(($this->item->category and $this->item->params->get('show_category'))
                                      ? (' ('.$this->item->category.')') : ''); ?>
</h1>
<?php
    echo $this->item->description;
    $tagLayout = new JLayoutFile('joomla.content.tags');
    echo $tagLayout->render($this->item->tags);
    $src = $this->item->imageDetails['image'];
    if ($src)
    {
        $html = '<figure>
                    <img src="%s" alt="%s" >
                    <figcaption>%s</figcaption>
                </figure>';
        $alt = $this->item->imageDetails['alt'];
        $caption = $this->item->imageDetails['caption'];
        echo sprintf($html, $src, $alt, $caption);
    } ?>

<?php if ($this->parentItem->id > 1) : ?>
	<h1><?php echo JText::_('COM_HELLOWORLD_PARENT') ?>
	</h1>
	<h3>
		<?php $url = JRoute::_('index.php?option=com_helloworld&view=helloworld&id=' . $this->parentItem->id . ':' . $this->parentItem->alias . '&catid=' . $this->parentItem->catid . $query_lang); ?>
		<a href="<?php echo $url; ?>"><?php echo $this->parentItem->greeting; ?></a>
	</h3>
<?php endif; ?>

<?php if ($this->children) : 
		$baseLevel = $this->item->level; ?>
		<h1><?php echo JText::_('COM_HELLOWORLD_CHILDREN') ?>
		</h1>
		<?php foreach ($this->children as $i => $child) : ?>
			<h3>
				<?php $prefix = JLayoutHelper::render('joomla.html.treeprefix', array('level' => $child->level - $baseLevel)); ?>
				<?php echo $prefix; ?>
				<?php $url = JRoute::_('index.php?option=com_helloworld&view=helloworld&id=' . $child->id . ':' . $child->alias . '&catid=' . $child->catid . $query_lang); ?>
				<a href="<?php echo $url; ?>"><?php echo $child->greeting; ?></a>
			</h3>
	<?php endforeach; ?>
<?php endif; ?>

<div id="map" class="map"></div>
<div class="map-callout map-callout-bottom" id="greeting-container"></div>
<div id="searchmap">
    <?php echo '<input id="token" type="hidden" name="' . JSession::getFormToken() . '" value="1" />'; ?>
    <button type="button" class="btn btn-primary" onclick="searchHere();">
        <?php echo JText::_('COM_HELLOWORLD_SEARCH_HERE_BUTTON') ?>
    </button>
    <div id="searchresults">
    </div>
</div>

Route Helper für das Frontend

Wie schon im Abschnitt Datenbank erwähnt, fügen wir die Methoden getHelloworldRoute() und getCategoryRoute() zur Datei route.php hinzu. Das ist nicht unbedingt notwendig, denn wenn wir diese Funktionen nicht einfügen, wird Joomla einen standardmäßigen Route Helper verwenden. Das Hinzufügen des Codes führt jedoch zu höherer Performance.

site/helpers/route.php

<?php

defined('_JEXEC') or die;

/**
 * Helloworld Component Helper file for generating the URL Routes
 *
 */
class HelloworldHelperRoute
{
	/**
	 * When the Helloworld message is displayed then there is also shown a map with a Search Here button.
	 * This function generates the URL which the Ajax call will use to perform the search. 
	 * 
	 */
	public static function getAjaxURL()
	{
		if (!JLanguageMultilang::isEnabled())
		{
			return null;
		}
        
		$lang = JFactory::getLanguage()->getTag();
		$app  = JFactory::getApplication();
		$sitemenu= $app->getMenu();
		$thismenuitem = $sitemenu->getActive();

		// if we haven't got an active menuitem, or we're currently on a menuitem 
		// with view=category or note = "Ajax", then just stay on it
		if (!$thismenuitem || strpos($thismenuitem->link, "view=category") !== false || $thismenuitem->note == "Ajax")
		{
			return null;
		}

		// look for a menuitem with the right language, and a note field of "Ajax"
		$menuitem = $sitemenu->getItems(array('language','note'), array($lang, "Ajax"));
		if ($menuitem)
		{
			$itemid = $menuitem[0]->id; 
			$url = JRoute::_("index.php?Itemid=$itemid&view=helloworld&format=json");
			return $url;
		}
		else
		{
			return null;
		}
	}

	/**
	 * Helper function for generating the URL to a Helloworld page
	 * This is needed for the Tags functionality
	 */
	public static function getHelloworldRoute($id, $catid = 0, $language = 0)
	{
		// Create the link
		$link = 'index.php?option=com_helloworld&view=helloworld&id=' . $id;

		if ((int) $catid > 1)
		{
			$link .= '&catid=' . $catid;
		}

		if ($language && $language !== '*' && JLanguageMultilang::isEnabled())
		{
			$link .= '&lang=' . $language;
		}

		return $link;
	}

	/**
	 * Helper function for generating the URL to a Helloworld Category page
	 * This is needed for the Tags functionality
	 */
	public static function getCategoryRoute($catid, $language = 0)
	{
		if ($catid instanceof JCategoryNode)
		{
			$id = $catid->id;
		}
		else
		{
			$id = (int) $catid;
		}

		if ($id < 1)
		{
			$link = '';
		}
		else
		{
			$link = 'index.php?option=com_helloworld&view=category&id=' . $id;

			if ($language && $language !== '*' && JLanguageMultilang::isEnabled())
			{
				$link .= '&lang=' . $language;
			}
		}

		return $link;
	}
}

Zusammenstellung der Komponente

Der Inhalt deines Codeverzeichnisses. Jeder folgende Dateilink führt dich zu dem Schritt im Tutorial, der die aktuellste Version dieser Quellcodedatei aufweist.

helloworld.xml

<?xml version="1.0" encoding="utf-8"?>
<extension type="component" version="3.0" method="upgrade">

	<name>COM_HELLOWORLD</name>
	<!-- The following elements are optional and free of formatting constraints -->
	<creationDate>January 2018</creationDate>
	<author>John Doe</author>
	<authorEmail>john.doe@example.org</authorEmail>
	<authorUrl>http://www.example.org</authorUrl>
	<copyright>Copyright Info</copyright>
	<license>License Info</license>
	<!--  The version string is recorded in the components table -->
	<version>0.0.28</version>
	<!-- The description is optional and defaults to the name -->
	<description>COM_HELLOWORLD_DESCRIPTION</description>

	<!-- Runs on install/uninstall/update; New in 2.5 -->
	<scriptfile>script.php</scriptfile>

	<install> <!-- Runs on install -->
		<sql>
			<file driver="mysql" charset="utf8">sql/install.mysql.utf8.sql</file>
		</sql>
	</install>
	<uninstall> <!-- Runs on uninstall -->
		<sql>
			<file driver="mysql" charset="utf8">sql/uninstall.mysql.utf8.sql</file>
		</sql>
	</uninstall>
	<update> <!-- Runs on update; New since J2.5 -->
		<schemas>
			<schemapath type="mysql">sql/updates/mysql</schemapath>
		</schemas>
	</update>

	<!-- Site Main File Copy Section -->
	<!-- Note the folder attribute: This attribute describes the folder
		to copy FROM in the package to install therefore files copied
		in this section are copied from /site/ in the package -->
	<files folder="site">
		<filename>index.html</filename>
		<filename>helloworld.php</filename>
		<filename>controller.php</filename>
		<filename>router.php</filename>
		<folder>controllers</folder>
		<folder>views</folder>
		<folder>models</folder>
		<folder>helpers</folder>
	</files>

		<languages folder="site/language">
			<language tag="en-GB">en-GB/en-GB.com_helloworld.ini</language>
			<language tag="fr-FR">fr-FR/fr-FR.com_helloworld.ini</language>
		</languages>

	<media destination="com_helloworld" folder="media">
		<filename>index.html</filename>
		<folder>images</folder>
		<folder>js</folder>
		<folder>css</folder>
	</media>

	<administration>
		<!-- Administration Menu Section -->
		<menu link='index.php?option=com_helloworld' img="../media/com_helloworld/images/tux-16x16.png">COM_HELLOWORLD_MENU</menu>
		<!-- Administration Main File Copy Section -->
		<!-- Note the folder attribute: This attribute describes the folder
			to copy FROM in the package to install therefore files copied
			in this section are copied from /admin/ in the package -->
		<files folder="admin">
			<!-- Admin Main File Copy Section -->
			<filename>index.html</filename>
			<filename>config.xml</filename>
			<filename>helloworld.php</filename>
			<filename>controller.php</filename>
			<filename>access.xml</filename>
			<!-- SQL files section -->
			<folder>sql</folder>
			<!-- tables files section -->
			<folder>tables</folder>
			<!-- models files section -->
			<folder>models</folder>
			<!-- views files section -->
			<folder>views</folder>
			<!-- controllers files section -->
			<folder>controllers</folder>
			<!-- helpers files section -->
			<folder>helpers</folder>
		</files>
		<languages folder="admin/language">
			<language tag="en-GB">en-GB/en-GB.com_helloworld.ini</language>
			<language tag="en-GB">en-GB/en-GB.com_helloworld.sys.ini</language>
			<language tag="fr-FR">fr-FR/fr-FR.com_helloworld.ini</language>
			<language tag="fr-FR">fr-FR/fr-FR.com_helloworld.sys.ini</language>
		</languages>
	</administration>

</extension>

Mitwirkende