Controller

15 Eigene Komponenten, Module, Plugins

Sie wollen mit Joomla! ein Problem lösen, für das es noch keine fertige Komponente gibt? Sie sind beispielsweise Autohändler und benötigen eine über die Joomla!-Administration pflegbare Auflistung Ihrer Gebrauchtwagen auf Ihrer Website? Oder eine Liste Ihrer Niederlassungen? Erweitern Sie einfach den Funktionsumfang um neue Komponenten, Module und Plugins. Was auf den ersten Blick sehr kompliziert aussieht, ist auch mit PHP-Anfängerwissen durchaus machbar.

Nach den Erfahrungen im Template-Bau in Kapitel 13 können Sie sich bestimmt schon vorstellen, was auf Sie zukommt. Diesmal liegt der Schwerpunkt allerdings nicht auf der Gestaltung, sondern auf der Programmierung.

Exkurs

Seit 01.01.2008 wird PHP 4 offiziell nicht mehr weiterentwickelt. Daher sollten Sie alle neuen Erweiterungen nach den Empfehlungen für die Version PHP 5 umsetzen. Joomla selbst ist in der Version 1.5 zu beiden Standards kompatibel. Wenn Sie mehr erfahren möchten: http://gophp5.org/.

15.0.1 Model – View – Controller

Mit Joomla! 1.5 wurde ein Konzept (Muster) für die Erstellung von Erweiterungen eingeführt: das Model-View-Controller-Muster, abgekürzt mit MVC. In der modernen Software-Entwicklung wird dieser Ansatz benutzt, um eine gewisse Ordnung in die Dinge zu bekommen.

Fast immer, wenn es um Aufgaben einer Software geht, benötigt man drei Bereiche:

  • Ein Datenmodell (engl. Model)
  • Eine Präsentation (engl. View)
  • Eine Programmsteuerung (engl. Controller)

Also liegt es nahe, diese drei Bereiche zu standardisieren. Da Programmierer auf solche Vorgaben eher allergisch reagieren, muss es echte Vorteile geben. Schauen wir uns die einzelnen Begriffe näher an.

Modell (Model)

Das Modell enthält die darzustellenden Daten. Die Herkunft der Daten ist unerheblich. Das Modell weiß nichts über die Ausgabe der Daten und hat auch keine Ahnung davon, wie diese Daten verändert werden.

Präsentation (View)

Die Präsentation stellt die Daten des Modells dar. Es muss eine Verbindung zwischen Modell und Präsentation geben, damit etwas dargestellt werden kann.

Steuerung (Controller)

Ja, was wird die Steuerung machen? Sie steuert das Ganze, reagiert auf Benutzereingaben oder andere Ereignisse, verwaltet die Modelle und die Präsentationen und gibt ihre Informationen an Präsentationen weiter.

15.0.2 Wo ist der Vorteil von MVC?

Wie bereits oben angedeutet, kommt gerade im Falle von Joomla! eine gewisse Ordnung in die Dinge. War es mit Joomla! 1.0 üblich, so zu programmieren, wie man Lust hatte, ist das mit Joomla! 1.5 nicht mehr erwünscht. Das hat nichts mit Bevormundung zu tun, sondern mit den Anforderungen der Benutzer, also mit Ihnen!

Sie wollen:

  • Barrierefreie Websites (overrides)
  • Suchmaschinenfreundliche URLs
  • Verteilte Authentifizierung
  • Internationalisierung
  • Sicherheit und Wartbarkeit
  • Wiederverwendbarkeit
  • mehr, mehr, mehr ...

Genau dieses mehr, mehr, mehr ist der Grund dafür, Ordnung im System zu schaffen. Solange Sie eine Firma mit einem Mitarbeiter haben, können Sie die Verwaltung noch im Kopf, mit einem Zettel und einem Stift erledigen. Haben Sie 10 Mitarbeiter, wird das schon schwierig. Wenn Ihre Firma jetzt vielleicht auch noch »brummt« und anfängt zu wachsen, brauchen Sie irgendein System zur Verwaltung!

MVC ist so ein System, in unserem Falle aber für Software. Es soll ein Programmdesign ermöglichen, das spätere Änderungen erleichtert und eine Wiederverwendbarkeit von Code ermöglicht. Es ist nicht unumstritten, aber auch nicht schlecht. Wenn sich alle dran halten, ist es gut!

Quell religiöser Diskussion sind natürlich Fragen wie:

  • Wohin soll meine Geschäftslogik? In den Controller oder in das Model?
  • Wie ist das mit wiederverwendbaren Dialogen? In den View?
  • Wie regeln wir mehrere Views (Tabelle, Einzelansicht, Frontend, Backend)?

Da eine vergleichbare Struktur in Joomla! 1.0 nicht existierte, ist dieses »Mantra« und vor allem seine Durchsetzung in meinen Augen die größte Leistung, die Joomla! 1.5 bzw. das Projektteam dahinter vollbracht haben. Die Zukunft wird zeigen, ob die Benutzer und Entwickler das auch so sehen. :-)

Wo ist er also, der Vorteil für alle?

Ganz einfach, Sie kennen das von sich selbst. Wenn die Küche, der Keller, das Auto, der Dachboden erst einmal aufgeräumt sind, ist es relativ einfach, diesen Zustand beizubehalten. Im Falle von Software ist das die Grundvoraussetzung, überhaupt wachsen zu können und pflegbar zu sein. Und Joomla! steht immer noch am Anfang seiner Möglichkeiten. Das heißt, wenn sich heute jemand (das Joomla!-Projektteam) die Arbeit gemacht hat und Joomla! 1.0 aufgeräumt hat, wird es für Sie in Zukunft deutlich einfacher, Joomla! an Ihre Wünsche anzupassen.

1Download siehe Anhang (hier in der Online Version direkt an der Kapitelseite).

 

15.2 Beispiel-Komponente auto

Jetzt einen großen Sprung nach vorn in Richtung Komplexität. Wir wollen eine sehr einfache Gebrauchtwagen-Liste erstellen, die über den Administrationsbereich gepflegt werden kann.

Sie benötigen dazu:

  • eine neue Frontend-Komponente,
  • eine Backend-Komponente,
  • eine spezielle Tabelle in der Datenbank,
  • (optional) ein zusätzliches Modul zur Anzeige der Einträge auf der Website und
  • (optional) ein Such-Plugin, um Ihre neuen Inhalte durchsuchen zu können.

Damit Sie eine Vorstellung bekommen, worauf ich hinaus möchte und wie die Liste auf der Website aussehen soll, hier das fertige Beispiel. Installieren Sie sich einfach die Komponente com_auto.zip, um es selbst nachvollziehen zu können.

Auf der Website soll eine Liste von Autos mit einem kleinen Foto erscheinen. Klickt man auf das Foto, öffnet sich ein Fenster mit einem größeren Foto. Außerdem gibt es noch ein Modul mit einer Auflistung der Autos (Abbildung 15.5).

Eigene Komponente auf der Website
Abbildung 15.5: Eigene Komponente auf der Website

Diese Liste enthält in unserem Falle Autotypen. Sie können das Beispiel auch mit anderen Daten füllen (Niederlassungen, Angebote etc.). Für den Besucher der Website reicht es meistens aus, eine solche Liste zu sehen. Er muss die Einträge nicht bearbeiten können.

Der Administrator dagegen muss die Liste verwalten.

Verwalten bedeutet in diesem Zusammenhang:

  • Neue Einträge aufnehmen
  • Vorhandene Einträge ändern
  • Vorhandene Einträge löschen

Um das Beispiel nicht zu verkomplizieren, zeigen und bearbeiten wir vier Felder. Zwei Felder für den Text (Typ, Hersteller) und zwei Felder für die Fotos (URL zum Foto, klein und groß). Das Prinzip der Programmierung einer Komponente in Joomla! wird so deutlich, und Sie können das Beispiel leicht um mehrere Felder erweitern. Die Komponente soll sich in die vorhandene Administrationsstruktur einfügen. Die Verwaltungsoberfläche im Administrationsbereich soll aussehen wie in Abbildung 15.6. Es soll möglich sein, die oben genannten Funktionen auszuführen. Außerdem benötigen Sie natürlich eine spezielle Toolbar für die Listenanzeige und eine für den Editier-Modus. Sie benötigen eine Möglichkeit, die Einträge zu veröffentlichen und zu sperren, und Sie wollen die Funktionen Veröffentlichen und Löschen natürlich auch in einer Mehrfachverarbeitung haben, indem Sie die Checkboxen vor den Einträgen ankreuzen und dann beispielsweise mehrere Einträge auf einmal veröffentlichen.

Eigene Komponente in der Joomla!-Administration als Liste
Abbildung 15.6: Eigene Komponente in der Joomla!-Administration als Liste

Außer der Liste benötigen Sie natürlich noch ein Formular für das Ändern und Neuanlegen eines Eintrags (Abbildung 15.7).

Eigene Komponente in der Joomla!-Administration als Formular
Abbildung 15.7: Eigene Komponente in der Joomla!-Administration als Formular

Beginnen wir mit der Komponente. Ich nenne sie com_auto. Um die Ausführungen hier nachvollziehen zu können, sollten Sie sich das fertige Beispiel herunterladen1 und wie jede Komponente installieren. Sie können diese Komponente dann per Hand ändern. Das Ändern und Umbauen hat den Vorteil, dass Sie nach und nach die Strukturen durchschauen und vielleicht Lust auf mehr bekommen. Ich gebe zu, dass komplexere Code-Beispiele schnell unübersichtlich werden. Ich habe aber in diesem Fall versucht, die Dateien so kurz wie möglich zu gestalten, ohne wichtige Dinge wegzulassen.

15.2.1 Die MySQL-Tabelle

Die Komponente nutzt eine Tabelle in MySQL, um die Autos zu speichern. Die Tabelle wird automatisch bei der Installation angelegt. Die SQL-Befehle finden Sie nach der Installation in der Datei /administrator/com_auto/install.sql. Analog dazu gibt es eine Datei uninstall.sql, die bei der Deinstallation der Komponente die Tabelle wieder löscht.

Wenn Sie http://localhost/phpmyadmin aufrufen, können Sie sich die Tabelle jos_auto ansehen (Abbildung 15.8).

Auto-Tabelle in MySQL
Abbildung 15.8: Auto-Tabelle in MySQL

Das Präfix jos_ wird vom Installer vergeben, abhängig von den Angaben in Ihrer Joomla!-Konfiguration. Das Feld published enthält entweder den Wert 1 (veröffentlicht) oder 0 (nicht veröffentlicht). Die beiden Fotos enthalten einen Pfad, den Sie vermutlich anpassen müssen. Das können Sie in der Administration tun. Die Bilder sind ebenfalls auf der dem Buch beiliegenden CD enthalten.

15.2.2 Das Frontend

Wir beginnen mit der Liste der Autos im Frontend. Wie bereits bei der hallo-Komponente benötigen Sie die folgenden Dateien:

  • /components/com_auto/auto.php
  • /components/com_auto/controller.php
  • /components/com_auto/views/auto/view.html.php
  • /components/com_auto/views/auto/tmpl/default.php
  • /components/com_auto/models/auto.php

Wie Sie sehen, existiert dieses Mal ein model, wir benötigen ja Daten aus einer Datenbank.

Der Einstiegspunkt (/components/com_auto/auto.php)

Auch gibt es einen Einstiegspunkt. Die Datei /components/com_auto/auto.php sammelt alles zusammen und delegiert die Aufgaben (Listing 15.6).

<?php
// kein direkter Zugriff
defined('_JEXEC') or die('Restricted access');
// laden des Joomla! Basis Controllers
require_once (JPATH_COMPONENT.DS.'controller.php');
// Erzeugen eines Controllers
$controller = new AutoController();
// den request task auslesen
$controller->execute(JRequest::getCmd('task'));
// Redirect aus dem controller
$controller->redirect();
?>

Listing 15.6: /components/com_auto/auto.php

Der Code ist fast der gleiche wie in der com_hallo-Komponente (Stichwort Wiederverwendbarkeit) (Listing 15.7).

Der Controller (/components/com_auto/controller.php)

Auch hier alles bekannte Dinge aus der com_hallo-Komponente:

<?php
defined('_JEXEC') or die();
jimport('joomla.application.component.controller');
class AutoController extends JController{
function display(){
parent::display();
}
}

Listing 15.7: /components/com_auto/controller.php

Der View (/components/com_auto/views/auto/view.html.php)

Hier finden sich erste Unterschiede. Wir benötigen natürlich Daten für die Liste (Listing 15.8).

<?php
jimport('joomla.application.component.view');
class AutoViewAuto extends JView {
  function display($tpl = null){
    $model = &$this->getModel();
    $rows = $model->getAutoList();
    $this->assignRef('rows' , $rows);
    parent::display($tpl);
  }    
}
?>

Listing 15.8: /components/com_auto/views/auto/view.html.php

$model = &$this->getModel();

Das Modell wird instanziert und steht als Objekt in der Variable $model zur Verfügung

$rows = $model->getAutoList();

Die Methode getAutoList wird im Objekt aufgerufen und liefert ein Array $rows (Zeilen) zurück.

$this->assignRef('rows', $rows);
parent::display($tpl);

Die Ergebniszeilen werden zugewiesen und an das Template weitergegeben.

Das Template (/components/com_auto/views/auto/tmpl/default.php)

Im Standard-Template werden die Zeilen in der Variable $rows durch eine for-Schleife geschickt, in eine einzelne $row verwandelt und dann einzeln ausgegeben (Listing 15.9)

<?php 
defined('_JEXEC') or die('Restricted access');
?>
<script type="text/javascript">
function FensterOeffnen (Adresse) {
MeinFenster = window.open(Adresse, "Auto",
     "width=400,height=300,left=100,top=200");
MeinFenster.focus();
}
</script>
<h1><?php echo "&Uuml;berschrift"; ?></h1>
<ul>
<?php
// Auslesen der Datensätze im Array
foreach ($this->rows as $row) {
  ?>
<li>
<?php echo $row->text; ?>
<small><em>(<?php echo $row->hersteller; ?>)
</em></small><br>
<a href="<?php echo $row->photo_gross; ?>"
onclick="FensterOeffnen(this.href); return false">
<img src=<?php echo $row->photo_klein; ?>></a>
</li>
<?php
}
?>
</ul>

Listing 15.9: /components/com_auto/views/auto/tmpl/default.php

foreach($this->rows as $row){
 ?>
<li>
<?php echo $row->text; ?>
<?php echo $row->hersteller; ?>
<?php echo $row->photo_klein; ?>
</li>
<?php
}
?>

Diese Schleife ist das eigentlich Wichtige an diesem Template. Die restliche Formatierung mit HTML oder wie im Besipielcode mit JavaScript zum Öffnen des Popup-Fensters können Sie beliebig verkomplizieren, mit Lightbox-Effekten versehen und alles, was beispielsweise Dreamweaver, MooTools und Ihre Kreativität hergeben, einbauen. Dieser Teil der Formatierung ist in der Version Joomla! 1.5 jetzt eigenständig, bis auf die Variable $rows.

Model (/components/com_auto/models/auto.php)

Da war doch noch etwas – das Modell! Wie kommen wir an die Daten? (Listing 15.10)

<?php
defined('_JEXEC') or die();
jimport('joomla.application.component.model');
class AutoModelAuto extends JModel{
function _getAutoQuery( &$options ){
$db = JFactory::getDBO();
$id = @$options['id'];
$select = 'a.*';
$from = '#__auto AS a';
$wheres[] = 'a.published = 1';
$query = "SELECT " . $select .
         "\n FROM " . $from .
         "\n WHERE " . implode( "\n AND ", $wheres );
return $query;
}
function getAutoList( $options=array() ){
$query = $this->_getAutoQuery( $options );
$result = $this->_getList( $query );
return @$result;
}
}
?>

Listing 15.10: /components/com_auto/models/auto.php

Es gibt zwei wichtige Methoden im Modell. Das ist zum einen die Methode _getAutoQuery, die den Zugriff auf die Daten realisiert und den SQL-Befehl beinhaltet, und zum anderen die Methode getAutolist, die diesen Zugriff steuert und das Resultat zurückgibt. Sie können sich sicherlich noch an die Datei views.html.php erinnern. Dort gibt es diese Zeile:

$rows = $model->getAutoList();

Sie greift auf das Objekt des Modells zu und speichert das Ergebnis in $rows. Innerhalb der Klasse AutoModelAuto wird diese Rückgabe durch die Methode getAutoList realisiert.

Fazit

Es ist an der Zeit, sich mit PHP und objektorientierter Programmierung zu befassen. Ohne das Wissen um Klassen, Methoden, Vererbung und ähnlichen Dingen sind sie nicht unbedingt verloren, aber doch vermutlich verwirrt.

Wenn man sich aber erst mal auf den MVC-Pfad von Joomla! einlässt, sieht man schnell Zusammenhänge. Wenn Sie beispielsweise die com_hallo-Komponente mit diesem Teil der com_auto-Komponente vergleichen, so waren eigentlich 80% des Codes identisch (bis auf das Modell).

15.2.3 Die com_auto-Administration

War das reine Anzeigen der Daten auf der Website noch verhältnismäßig einfach, so ist das Verwalten der Daten naturgemäß ein wenig aufwändiger. Hier müssen Sie die Daten als Administrator anzeigen, ändern, neu einfügen, löschen und veröffentlichen können – also erheblich mehr Interaktivität als in der einfachen Liste auf der Website.

Die Komponenten-Tabelle

Joomla! verwaltet übrigens alle Menüeinträge der Komponenten in der Tabelle [prefix]components. Auch die Menüeinträge aller Komponenten im Administrationsbereich müssen hier vermerkt werden. Die com_auto-Komponente wurde dort auch eingetragen (Abbildung 15.9).

Die components Tabelle in phpMyAdmin
Abbildung 15.9: Die components Tabelle in phpMyAdmin

Außerdem wird dort eine Grafik vermerkt, die links neben dem Menü erscheinen soll ('js/ThemeOffice/component.png'). Die Grafiken finden Sie im Ordner [PfadzuJoomla]/includes/js/ThemeOffice. (Abbildung 15.10).

Grafik neben den Menüeinträgen
Abbildung 15.10: Grafik neben den Menüeinträgen

Um die Administrationskomponente zu erstellen, benötigen Sie mehrere Dateien. Im Verzeichnis [PfadzuJoomla]/administration/components/com_auto/ finden Sie:

  • /administration/components/admin.auto.php
  • /administration/components/controller.php
  • /administration/components/controllers/auto.php
  • /administration/components/views/autos/view.html.php
  • /administration/components/views/autos/tmpl/default.php
  • /administration/components/views/auto/view.html.php
  • /administration/components/views/auto/tmpl/form.php
  • /administration/components/tables/auto.php
  • /administration/components/install.sql
  • /administration/components/uninstall.sql

Der Einstieg (/administration/components/admin.auto.php)

Auch im Administrationsbereich gibt es natürlich einen Einstieg (Listing 15.11).

<?php
defined('_JEXEC') or die('Restricted access');
$controller = JRequest::getVar('controller', 'auto');
require_once(JPATH_ADMINISTRATOR.DS.'controllers'.DS.$controller.'.php';
$classname = 'AutosController'.$controller;
$controller = new $classname();
$controller->execute( JRequest::getVar('task'));
$controller->redirect();
?>

Listing 15.11: /administration/components/admin.auto.php

Als Spezialist für Einstiege stellen wir fest, dass uns das schon sehr bekannt vorkommt. Bis auf die if-Abfrage, in der nach zusätzlichen Controllern gesucht wird, ist uns alles vertraut.

Controller (/administration/components/controller.php)

Auch der Basis Controller kommt uns bekannt vor (Listing 15.12):

<?php
jimport('joomla.application.component.controller');
class AutosController extends JController{
  function display(){
   parent::display();
 }
}
?>

Listing 15.12: /administration/components/controller.php

Noch ein Controller (/administration/components/controllers/auto.php)

Jetzt ändert sich etwas. Wir haben einen zusätzlichen Controller, und der hat es auch gleich in sich (Listing 15.13):

<?php
defined('_JEXEC') or die();
class AutosControllerAuto extends AutosController{
function __construct(){
parent::__construct();
$this->registerTask( 'add', 'edit' );
$this->registerTask( 'unpublish', 'publish');
}
function edit(){
JRequest::setVar( 'view', 'auto' );
JRequest::setVar( 'layout', 'form' );
JRequest::setVar('hidemainmenu', 1);
parent::display();
}
function save(){
$model = $this->getModel('auto');
if($model->store($post)) {
$msg = JText::_( 'Auto Saved!' );
}
else{
$msg = JText::_( 'Error Saving Auto' );
}
$link = 'index.php?option=com_auto';
$this->setRedirect($link, $msg);
}
function remove(){
$model = $this->getModel('auto');
if(!$model->delete()){
$msg = JText::_( 'Error: One or more Autos could not be Deleted' );
}
else {
    $msg = JText::_( 'Auto(s) Deleted' );
}
$this->setRedirect( 'index.php?option=com_auto', $msg );
}
function publish(){
$this->setRedirect( 'index.php?option=com_auto' );
$db =& JFactory::getDBO();
$user =& JFactory::getUser();
$cid = JRequest::getVar( 'cid', array(), 'post', 'array' );
$task = JRequest::getCmd( 'task' );
$publish = ($task == 'publish');
$n = count( $cid );
if (empty( $cid )) {
     return JError::raiseWarning( 500, JText::_( 'No items selected' ) );
}
JArrayHelper::toInteger( $cid );
$cids = implode( ',', $cid );
$query = 'UPDATE #__auto'
         . ' SET published = ' . (int) $publish
         . ' WHERE id IN ( '. $cids .' )';
$db->setQuery( $query );
if (!$db->query()) {
     return JError::raiseWarning( 500, $row->getError() );
}
$this->setMessage(
JText::sprintf( $publish ?
'Items published' :
'Items unpublished', $n ) );
}
function cancel(){
$msg = JText::_( 'Operation Cancelled' );
$this->setRedirect( 'index.php?option=com_auto', $msg );
}
}
?>

Listing 15.13: /administration/components/controllers/auto.php

Dieser Controller implementiert die Methoden edit, save, remove, publish und cancel. Innerhalb dieser Methoden wird das Modell instanziert und je nach Bedarf beispielsweise die store-Methode im Modell aufgerufen. Mit den statischen Klassen JText und JError werden Nachrichten über den Erfolg oder Misserfolg ausgegeben (Abbildung 15.11).

Meldungsfenster JText
Abbildung 15.11: Meldungsfenster JText

View für die Liste (/administration/components/views/autos/view.html.php)

Diesmal ist der View ein wenig größer, weil die Toolbar noch eingefügt werden muss (Listing 15.14).

<?php
defined('_JEXEC') or die();
jimport( 'joomla.application.component.view' );
class AutosViewAutos extends JView{
function display($tpl = null){
JToolBarHelper::title( JText::_( 'Auto Manager' ), 'generic.png' );
JToolBarHelper::publishList();
JToolBarHelper::unpublishList();
JToolBarHelper::deleteList();
JToolBarHelper::editListX();
JToolBarHelper::addNewX();
$items = &$this->get( 'Data');
$this->assignRef('items',    $items);
parent::display($tpl);
}
}

Listing 15.14: /administration/components/views/autos/view.html.php

Toolbar in der Liste der Autos
Abbildung 15.12: Toolbar in der Liste der Autos

Die Toolbar-Helper-Klasse übernimmt die Anzeige (Abbildung 15.12).

Template Liste (/administration/components/views/autos/tmpl/default.php)

Die Liste muss natürlich auch formatiert werden, und so wird hier das entsprechende Standard-Template bereitgestellt (Listing 15.15).

<?php   
defined('_JEXEC')
or die('Restricted access');
?>
<form action="index.php"
method="post"
name="adminForm">
<div id="editcell">
<table class="adminlist">
<thead><tr>
<th width="5">
<?php echo JText::_( 'NUM' ); ?>
</th>
<th width="20">
<input type="checkbox"
name="toggle"
value=""
      onclick="checkAll(<?php
echo count( $this->items ); ?>);" />
</th>
<th class="title">
<?php echo JHTML::_('grid.sort', 'Auto', 'a.text',
     @$lists['order_Dir'], @$this->lists['order'] );
?>
</th>
<th width="5%" align="center">
<?php echo JHTML::_('grid.sort','Published',
  'a.published', @$this->lists['order_Dir'],
@$this->lists['order'] ); ?>
</th>
<th width="1%" nowrap="nowrap">
<?php echo JHTML::_('grid.sort', 'ID', 'a.id',
   @$this->lists['order_Dir'],
@$this->lists['order'] ); ?></th>
</tr>
</thead>
<?php
$k = 0;
for ($i=0, $n=count($this->items); $i < $n; $i++){
$row = &$this->items[$i];
$published = JHTML::_('grid.published', $row, $i );
$link = JRoute::_(
'index.php?option=com_auto&controller=auto&task=edit&cid[]='.
   $row->id );
?>
<tr class="<?php echo "row$k"; ?>">
<td></td>
<td></td>
<td>
<a href="<?php echo $link; ?>">
<?php echo $row->text; ?></a>
</td>
<td align="center"><?php echo $published;?></td>
<td align="center"><?php echo $row->id; ?></td>
</tr>
<?php
$k = 1 - $k;
}
?>
</table>
</div>
<input type="hidden" name="option" value="com_auto" />
<input type="hidden" name="task" value="" />
<input type="hidden" name="boxchecked" value="0" />
<input type="hidden" name="controller" value="auto" />
</form>

Listing 15.15: /administration/components/views/autos/tmpl/default.php

Dieses Template besteht aus eher »schlichtem« HTML, eingepackt in ein Formular. Es sorgt für die Darstellung der Tabelle (Abbildung 15.13).

Tabelle der Autos
Abbildung 15.13: Tabelle der Autos

View Formular (/administration/components/views/auto/view.html.php)

Auch die Einzelansicht der Autos muss geregelt werden. Achten Sie auf den Namen des Unterverzeichnisses. Hier befinden wir uns im Ordner Auto, die Liste ist im Ordner Autos angesiedelt (Listing 15.16).

<?php
defined('_JEXEC') or die();
jimport( 'joomla.application.component.view' );
class AutosViewAuto extends JView{
function display($tpl = null){
$auto =& $this->get('Data');
$isNew = ($auto->id < 1);
$text = $isNew
? JText::_( 'New' )
: JText::_( 'Edit' );
JToolBarHelper::title(JText::_( 'Auto' ).':
<small>[ ' . $text.' ]</small>' );
JToolBarHelper::save();
if ($isNew) {
JToolBarHelper::cancel();
}
else {
     JToolBarHelper::cancel( 'cancel', 'Close' );
}
$this->assignRef('auto', $auto);
parent::display($tpl);
}
}

Listing 15.16: /administration/components/views/auto/view.html.php

Im Listing wird die Toolbar für die Einzelansicht aufgebaut (Abbildung 15.14). Dieser View ist verwendbar für das Hinzufügen und das Ändern von Datensätzen. In der Variable $isNew wird zwischen beiden Fällen unterschieden.

Toolbar für das Formular
Abbildung 15.14: Toolbar für das Formular

Template Formular (/administration/components/views/auto/tmpl/form.php)

In diesem Standard-Template wird das Formular für die Einzelansicht aufgebaut (Listing 15.17)

<?php 
defined('_JEXEC')
or die('Restricted access'); ?>
<script language="javascript"
type="text/javascript">
... Eingabeüberprüfung ...
</script>
<form action="index.php"
method="post"
name="adminForm"
id="adminForm">
<div>
<fieldset class="adminform">
<legend>
<?php echo JText::_( 'Details' ); ?>
</legend>
<table class="admintable">
<tr>
<td width="110" class="key">
<label for="title">
<?php echo JText::_( 'Text' ); ?>:
</label>
</td>
<td>
<input class="inputbox"
type="text"
name="text"
id="text"
size="60"
value="<?php
     echo $this->auto->text; ?>" />
</td>
</tr>
<tr>
... weitere Felder ...
</tr>
<tr>
<td width="120" class="key">
<?php echo JText::_( 'Published' ); ?>:
</td>
</tr>
</table>
</fieldset>
</div>
<div class="clr"></div>
<input type="hidden"
name="option"
value="com_auto" />
<input type="hidden"
name="id" value="<?php echo $this->auto->id; ?>" />
<input type="hidden"
name="task" value="" />
<input type="hidden"
name="controller" value="auto" />
</form>

Listing 15.17: /administration/components/views/auto/tmpl/form.php

Das Formular besteht wieder aus reinem HTML mit PHP-Variablen ($this->auto->id) und statischen Klassenaufrufen (JText). Das Ergebnis sehen Sie in Abbildung 15.15.

Auto-Datensatz – Änderungsformular
Abbildung 15.15: Auto-Datensatz – Änderungsformular

Tabelle auto (/administration/components/tables/auto.php)

Last, but not least, hier die Tabellen-Klasse. Irgendwie muss das Modell ja wissen, mit welchen Daten es eigentlich zu tun hat. Die JTable-Klasse erleichtert den Zugriff und das Verändern von Daten ungemein. Sie ist eine abstrakte Klasse (ein Interface), die es abgeleiteten Klassen ermöglicht, mit ihren Methoden die Struktur zu benutzen. Im Konstruktor werden der Tabellenname und der Primärschlüssel angegeben (Listing 15.18).

<?php
defined('_JEXEC') or die('Restricted access');
class TableAuto extends JTable{
  var $id = 0;
  var $text = '';
  var $hersteller = '';
  var $photo_klein = '';
  var $photo_gross = '';
  var $published = 0;
  function TableAuto(& $db) {
    parent::__construct('#__auto', 'id', $db);
}
}
?>

Listing 15.18: /administration/components/tables/auto.php

Installation (/administration/components/install.sql) und Deinstallation (/administration/components/uninstall.sql)

Bei der Installation/Deinstallation muss der Joomla!-Installer die benötigten Tabellen und Datensätze anlegen bzw. löschen. Hierzu werden zwei Dateien mitgegeben (Listing 15.19 und 15.20):

DROP TABLE IF EXISTS `#__auto`;

CREATE TABLE `#__auto` (
`id` int(11) NOT NULL auto_increment,
`text` text character set utf8 NOT NULL,
`hersteller` varchar(100) character set utf8 NOT NULL,
`photo_gross` varchar(200) character set utf8 NOT NULL,
`photo_klein` varchar(200) character set utf8 NOT NULL,
`published` tinyint(1) NOT NULL,
PRIMARY KEY (`id`)
)
ENGINE=MyISAM AUTO_INCREMENT=5 ;

INSERT INTO `#__auto` (`id`, `text`, `hersteller`,
`photo_gross`, `photo_klein`, `published`)
VALUES
(2, 'Smart fortwo', 'Smart',
'http://localhost/joomla150/images/stories/com_auto/smart_gross.jpg',
'http://localhost/joomla150/images/stories/com_auto/smart_klein.jpg',
1),
(4, 'Roadster', 'Smart',
'http://localhost/joomla150/images/stories/com_auto/roadster_gross.jpg',
'http://localhost/joomla150/images/stories/com_auto/roadster_klein.jpg',
1);

Listing 15.19: /administration/components/install.sql

DROP TABLE IF EXISTS `#__auto`;

Listing 15.20: administration/components/uninstall.sql

15.2.4 Test

Nachdem Sie alle Dateien durchgesehen haben, können Sie die Komponente testen und die Datensätze komplett über die Joomla!-Administration verwalten. Sie können neue Texte anlegen, vorhandene löschen, ändern und veröffentlichen. Durch ein vorheriges Ankreuzen der Einträge können Sie beim Veröffentlichen bzw. beim Löschen mehrere Einträge auf einmal bearbeiten. Versuchen Sie ein paar Dinge zu verändern und zu erweitern. Es ist gar nicht so schwer.

15.2.5 Installationspaket erstellen

Um ein Installationspaket für Ihre neue Komponente zu schnüren, benötigen Sie außer den beschriebenen Tabellen noch die obligatorische XML-Datei mit den Metadaten.

auto.xml

Hier beschreiben Sie Ihre neue Komponente gegenüber dem Joomla!-Installer. Sie müssen alle Informationen wie Metadaten und alle Dateinamen in XML-Tags einschließen. Der Joomla!-Installer liest diese Datei aus, erzeugt neue Unterverzeichnisse, kopiert die Dateien an den richtigen Platz und legt die benötigten Tabellen an (Listing 15.21).

<?xml   version="1.0" encoding="utf-8"?>
<!DOCTYPE install SYSTEM
"http://dev.joomla.org/xml/1.5/component-install.dtd">
<install type="component" version="1.5.0">
<name>Auto</name>
<creationDate>November 2007</creationDate>
<author>Hagen Graf</author>
<authorEmail>hagen.graf@gmail.com</authorEmail>
<authorUrl>http://www.cocoate.com</authorUrl>
<copyright>All rights reserved</copyright>
<license>GNU/GPL</license>
<version>Component Version String</version>
<description>Beschreibung der Komponente ...</description>
<files folder="site">
<filename>index.html</filename>
<filename>auto.php</filename>
<filename>controller.php</filename>
<filename>views/index.html</filename>
<filename>views/auto/index.html</filename>
<filename>views/auto/view.html.php</filename>
<filename>views/auto/tmpl/index.html</filename>
<filename>views/auto/tmpl/default.php</filename>
<filename>models/auto.php</filename>
</files>
<install>
<sql>
<file charset="utf8" driver="mysql">install.sql</file>
</sql>
</install>
<uninstall>
<sql>
<file charset="utf8" driver="mysql">uninstall.sql</file>
</sql>
</uninstall>
<administration>
<menu>Joomla! 1.5 Buch Auto</menu>
<files folder="admin">
<filename>index.html</filename>
<filename>admin.auto.php</filename>
<filename>controller.php</filename>
<filename>controllers/auto.php</filename>
<filename>controllers/index.html</filename>
<filename>models/auto.php</filename>
<filename>models/autos.php</filename>
<filename>models/index.html</filename>
<filename>views/autos/view.html.php</filename>
<filename>views/autos/index.html</filename>
<filename>views/autos/tmpl/default.php</filename>
<filename>views/autos/tmpl/index.html</filename>
<filename>views/auto/view.html.php</filename>
<filename>views/auto/tmpl/form.php</filename>
<filename>views/auto/index.html</filename>
<filename>views/auto/tmpl/index.html</filename>
<filename>tables/auto.php</filename>
<filename>tables/index.html</filename>
<filename>install.sql</filename>
<filename>uninstall.sql</filename>
</files>
</administration>
</install>

Listing 15.21: auto.xml

Um das Installationspaket zu erzeugen, müssen Sie alle erstellten Dateien in ein Verzeichnis kopieren und dieses Verzeichnis zu einem ZIP-Paket mit dem Namen der Komponente, in unserem Fall com_auto.zip, verpacken. Die Dateien für das Frontend kommen in einen Ordner site, die für den Administrationsbereich in einen Ordner admin (Abbildung 15.16).

Dateien der Komponente com_auto
Abbildung 15.16: Dateien der Komponente com_auto

Die zip-Datei können Sie jetzt mit dem Joomla!-Installer wie gewohnt installieren und, wenn Sie wollen, zum Download für andere Anwender bereitstellen. Bevor Sie das in Ihrer eigenen Installation tun, sollten Sie die per Hand erstellte Variante über den Joomla!- Installer deinstallieren. Klicken Sie dazu auf Erweiterungen - Installieren/Deinstallieren, markieren Sie Ihre Komponente und klicken Sie auf das Icon Deinstallieren.

15.1 Beispiel-Komponente hallowelt

Bevor wir richtig loslegen, aber erst mal ein einfacher »Hallo Welt«-Start. Komponenten teilen sich in Frontend- und Backend-Komponenten. Frontend-Komponenten werden auf Ihrer Website angezeigt, Backend-Komponenten werden für den Administrationsbereich entwickelt, meist um die Frontend-Komponenten zu verwalten. Aus der Sicht eines Besuchers Ihrer Website erkennen Sie eine Komponente an der Art des Aufrufs einer Joomla!-Seite:

Die URL

http://localhost/joomla150/index.php?option=com_contact

ruft beispielsweise die Frontend-Komponente com_contact auf.

Wenn Sie jetzt in Ihr Dateisystem schauen, finden Sie im Unterverzeichnis [PfadzuJoomla]/components eine ganze Menge Komponenten, unter anderem auch die com_contact-Komponente (Abbildung 15.1).

Joomla!-Komponenten
Abbildung 15.1: Joomla!-Komponenten

Innerhalb dieser Verzeichnisse sind je nach Komplexität der Komponenten weitere Verzeichnisse mit Dateien für Model, View und Controller enthalten.

15.1.1 Ein Haus für hallowelt

Jede Komponente »wohnt« in ihrem Verzeichnis. Der Weg, den Joomla! einschlägt, ist folgender:

  • Joomla! wertet die übergebenen Werte in der URL aus: /index.php?option=com_hallo.
  • Es sucht in der Komponenten-Tabelle nach der Komponente com_hallo.
  • Es sucht nach einem Verzeichnis com_hallo.
  • Es sucht in diesem Verzeichnis eine Datei hallo.php.
  • Es interpretiert diese Datei.

Damit richtig interpretiert wird, benötigen Sie mehrere Dateien.

  • hallo.php – der Einstiegspunkt in Ihre Komponenten
  • controller.php – die Steuerung
  • views/hallo/view.html.php – die Präsentation, die die Daten an das Template weitergibt
  • views/hallo/tmpl/default.php – ein Standard-Template, das von »richtigen« Templates überschrieben oder so, wie es ist, genutzt werden kann.
  • hallo.xml – die bekannte XML-Datei, die dem Installer erklärt, was im Paket ist, wo es installiert wird und wer dafür zuständig ist.
  • Auch wenn Sie jetzt innerlich zerbrechen und sich nach Joomla! 1.0 zurücksehnen (da ging das mit einer Datei :-)) – geben Sie der Idee eine Chance!

Installieren Sie sich am besten das Komponentenpaket com_hallo.zip über den Joomla!-Installer und legen Sie sich einen Menüpunkt an. Dann haben Sie gleich alle Dateien im richtigen Ordner und können den Quelltext leicht nachvollziehen.

15.1.2 Der Einstieg (/component/com_hallo/hallo.php)

Diese Datei wird als Erstes aufgerufen, wenn Sie die URL

http://localhost/joomla150/index.php?option=com_hallo&view=hallo

oder den entsprechenden Menüpunkt anklicken. Das Ergebnis ist recht unspektakulär. Sie sehen den Text »Hallo Welt« im Inhaltsfenster.

Der Quellcode der Datei sieht folgendermaßen aus (siehe Listing 15.1):

<?php
// kein direkter Zugriff
defined('_JEXEC') or die('Restricted access');
// Einlesen des basis Controllers
require_once (JPATH_COMPONENT.DS.'controller.php');
// Einen eigenen Controller erzeugen
$classname    = 'HalloController'
$controller = new $classname( );
// Nachsehen, ob Parameter angekommen sind (Requests)
$controller->execute( JRequest::getVar('task'));
// Umleitung innerhalb des Controllers
$controller->redirect();
?>

Listing 15.1: /component/com_hallo/hallo.php

defined('_JEXEC') or die('Restricted access');

Die erste Zeile ist ein Sicherheitscheck, ob die Datei von Joomla! aus aufgerufen wird oder direkt. Direkt aufgerufene Skripte werden sofort durch die Funktion die() beendet.

require_once (JPATH_COMPONENT.DS.'controller.php');

Danach wird der erste Controller eingelesen. JPATH_COMPONENT ist der absolute Pfad zur aktuellen Komponente (components/com_hallo). DS ist der directory separator des Betriebssystems. Windows hätte es gern andersherum als Unix-artige Systeme, also entweder / oder \. Dieser Wert wird von Joomla! automatisch gesetzt.

$classname    = 'HalloController';
$controller = new $classname( );

Jetzt können wir den eingelesenen Controller instanzieren und damit einen für uns »brauchbaren« Controller erzeugen. Wenn Sie »nur« einen Controller benötigen, wie es im Frontend oft der Fall ist, können Sie auch folgende Anweisung benutzen:

$controller = new HalloController();
$controller->execute( JRequest::getVar('task'));

Anweisungen an die Komponente werden in der Form index.php?option=com_ hallo&task=Aufgabe (save, edit, new, ...) über die URL angegeben. Mit dieser Zeile wird nachgeschaut, ob es da etwas zu lesen gibt.

$controller->redirect();

Unter Umständen leitet der Controller die Anfrage an eine andere Seite weiter, beispielsweise wenn etwas gespeichert wird, daher diese Zeile.

15.1.3 Der Controller (/component/com_hallo/controller.php)

Da die Komponente nun wirklich einfach ist, hat der Controller nur die Aufgabe, etwas anzuzeigen. Wir benötigen kein Datenmodell und nur eine display-Methode (Listing 15.2).

<?php
jimport('joomla.application.component.controller');
class HalloController extends JController{
  function display(){
    parent::display();
  }
}
?>

Listing 15.2: /component/com_hallo/controller.php

Der Aufruf der display-Methode bestimmt den Namen und das Layout des Views. Wenn Sie beispielsweise einen neuen Menülink anlegen, sehen Sie das Ergebnis dieser Display-Variante. Unsere Komponente kennt nur das Standardlayout.

Menülink für die »Hallo Welt«-Komponente
Abbildung 15.2: Menülink für die »Hallo Welt«-Komponente

15.1.4 Der View (/component/com_hallo/views/hallo/
views.html.php)

Und hier ist er schon, der Standard-View (Listing 15.3).

<?php
jimport( 'joomla.application.component.view');
class HalloViewHallo extends JView{
  function display($tpl = null){        
    $greeting = "Hallo Welt!";
    $this->assignRef( 'greeting', $greeting );
    parent::display($tpl);
  }
}
?>

Listing 15.3: /component/com_hallo/views/hallo/views.html.php

Der View, die Präsentation, erhält normalerweise Daten (aus dem Modell), bereitet sie auf und schickt sie an das Template.

$greeting = "Hallo Welt!";
$this->assignRef( 'greeting', $greeting );
parent::display($tpl);

Das Modell ist hier durch die Variablenzuweisung überflüssig. In der Variable $greeting könnte aber auch das Ergebnis einer Datenbankabfrage stehen. Mit der assignRef-Methode wird die Variable an das Template weitergeben.

parent::display($tpl);

ruft das Template auf.

15.1.5 Das Template (/component/com_hallo/views/hallo/tmpl/
default.php)

Und hier ist nun das Standard-Template. Es heißt immer default.php und sieht in seiner einfachsten Form aus wie in Listing 15.4.

defined('_JEXEC')or die('Restricted access'); ?>

<h1>
<?php echo $this->greeting; ?>
</h1>

Listing 15.4: /component/com_hallo/views/hallo/tmpl/default.php

15.1.6 Das Ergebnis

Auf der Website sieht alles zusammen sehr unspektakulär aus (Abbildung 15.3):

Hallo Welt
Abbildung 15.3: Hallo Welt

15.1.7 Die Installation

Alle Dateien müssen nun in ein zip-Paket gepackt werden und sind dann über den Joomla!-Installer installierbar. Die Frontend-Dateien in /component/com_hallo kommen in ein Verzeichnis site. Die Administrationsbereich-Dateien aus /com_hallo/ administrator/components kommen in ein Verzeichnis admin. Alle Dateien werden mit zusätzlichen Informationen in einer XML-Datei beschrieben, die so heißen muss wie die Komponente, also hallo.xml (Listing 15.5).

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE install SYSTEM "http://dev.joomla.org/xml/1.5/component-install.dtd">
<install type="component" version="1.5.0">
<name>Hallo</name>
<creationDate>November</creationDate>
<author>Niemand</author>
<authorEmail>niemand@example.org</authorEmail>
<authorUrl>http://www.example.org</authorUrl>
<copyright>Copyright Info</copyright>
<license>License Info</license>
<version>Component Version String</version>
<description>Beschreibung der Komponente ...</description>
<!-- Site Main File Copy Section -->
<files folder="site">
<filename>index.html</filename>
<filename>hallo.php</filename>
<filename>controller.php</filename>
<filename>views/index.html</filename>
<filename>views/hallo/index.html</filename>
<filename>views/hallo/view.html.php</filename>
<filename>views/hallo/tmpl/index.html</filename>
<filename>views/hallo/tmpl/default.php</filename>
</files>
<administration>
<!-- Administration Menu Section -->
<menu>Hallo Welt!</menu>
<!-- Administration Main File Copy Section -->
<files folder="admin">
<!-- Site Main File Copy Section -->
  <filename>index.html</filename>
  <filename>admin.hallo.php</filename>
</files>        
</administration>
</install>

Listing 15.5: hallo.xml

Um alles in ein zip-Paket zu packen, müssen Sie auf die Struktur achten. Hier eine entsprechende Struktur für die com_hallo-Komponente (Abbildung 15.4):

Installationspaket packen
Abbildung 15.4: Installationspaket packen

Sie können dieses Paket jetzt ganz normal über den Joomla!-Installer hochladen und installieren lassen.

15.1.8 Fazit

Das, was Sie jetzt erstellt haben, ist tatsächlich gut pflegbar, erweiterbar und – wenn Sie mal ehrlich sind – eigentlich auch ganz übersichtlich. Auf den ersten Blick wirken die vielen Dateien etwas wirr, auf den zweiten Blick verlieren sie aber schnell ihren Schrecken. Das liegt daran, dass sich in jeder Datei relativ wenig Quellcode befindet und Sie somit eine bessere Übersicht haben. Genau dieser Effekt ist beim MVC-Muster erwünscht. Und wenn Sie einen besonders komplexen Template-Wunsch haben, dann überschreiben Sie in Ihrem selbstgebauten Template einfach den View (siehe Kapitel 13.3.8).

Inhalt abgleichen