Development

Documentation/de_DE/book/1.0/14-Generators

You must first sign up to be able to contribute.

Version 9 (modified by Jan.Kunzmann, 10 years ago)
100% rough translation

ARBEITSENTWURF ROHÜBERSETZUNG / TRANSLATION WORKING DRAFT
Originaldokument: http://svn.symfony-project.com/doc/branches/1.1/book/14-Generators.txt, Version 7994 vom 2008-03-19
Übersetzungsfortschritt: 100%
Übersetzung: JK
Korrekturfortschritt: 0%

Kapitel 14 - Generatoren

Viele Anwendungen basieren auf Daten aus einer Datenbank und bieten ein Interface an, um darauf zuzugreifen. Symfony automatisiert diese sich häufig wiederholende Aufgabe, basierend auf einem Propel-Objekt ein Modul zu schreiben, dass Möglichkeiten zur Datenveränderung anbietet. Wenn Ihr Objektmodell ordentlich angelegt ist, kann Symfony sogar die gesamte Administrationsoberfläche automatisch erstellen. Dieses Kapitel wird Ihnen etwas über die zwei Generatoren erläutern, die in Symfony integriert sind: Das Grundgerüst (engl. "Scaffolding") und den Administrations-Generator. Letzterer benötigt eine spezielle Konfigurationsdatei mit komplexer Syntax, so dass der größte Teil dieses Kapitels sich mit den weitläufigen Möglichkeiten des Administrations-Generators beschäftigen wird.

Codeerzeugung auf Basis des Datenmodells

In einer Webanwendung können Datenzugriffe wie folgt kategorisiert werden:

  • Erzeugen eines Datensatzes (Create)
  • Ermitteln eines Datensatzes (Retrieve)
  • Aktualisieren eines Datensatzes (und Veränderung seiner Spalten) (Update)
  • Löschen eines Datensatzes (Delete)

Diese Operationen sind so alltäglich, dass ihnen ein eigenes Acronym gewidmet ist: CRUD (Anm. d. Übers.: Es setzt sich aus den Anfangsbuchstabend der englischen Bezeichnungen für diese Operationen zusammen). Viele Seiten können auf eine davon zurückgeführt werden. Beispielsweise ist in einer Forums-Anwendung die Liste der neuesten Postings ein "Ermitteln" (Retrieve), und das Antworten auf einen Beitrag entspricht einem "Erzeugen" (Create).

Die grundlegenden Actions und Templates, die CRUD-Operationen für eine bestimmte Tabelle durchführen, werden in Webanwendungen wiederholt erzeugt. Die Model-Schicht in Symfony enthält jedoch genügend Informationen, um den Code für CRUD-Operationen automatisch zu erzeugen, um so die frühen Entwicklungsschitte oder die Erstellung einer Backend-Benutzerschnittstelle zu beschleunigen.

Alle Tasks zum Erzeugen von Code, der auf dem Datenmodell beruht, erzeugen ein komplettes Modul und ergeben sich aus einem einzigen Aufruf des Symfony-Kommandozeilenwerkzeugs, der jeweils folgende Gestalt hat:

> symfony <TASK_NAME> <ANWENDUNGS_NAME> <MODUL_NAME> <KLASSEN_NAME>

Die entsprechenden Tasks heißen propel-init-crud, propel-generate-crud und propel-init-admin.

Grundgerüst und Administration

Während der Anwendungsentwicklung kann die Codeerzeugung hauptsächlich aus zwei Gründen verwendet werden:

  • Ein Grundgerüst (scaffolding) ist die grundlegende Struktur (Actions und Templates), die notwendig ist, um CRUD-Operationen auf einer bestimmten Tabelle durchzuführen. Dabei ist der Code relativ minimalistisch, denn er ist dazu gedacht, als Leitfaden für die weitere Entwicklung zu dienen. Es ist ein Fundament, das Sie auf Ihre Logik und Anforderungen anpassen müssen. Grundgerüste werden meist während der Entwicklungsphase verwendet, um Webzugriff auf die Datenbank zu gewährleisten, einen Prototyp zu erstellen oder ein Modul zu erzeugen, das primär auf einer Tabelle basiert.
  • Eine "Administration" ist eine durchdachte Benutzerschnittstelle zur Datenmanipulation, in erster Linie zur Administration des Backends. Die Administration unterscheidet sich vom Grundgerüst, da der Code eigentlich nicht manuell verändert werden sollte. Sie kann angepasst, erweitert oder durch Konfiguration und Vererbung zusammengesetzt werden. Ihre Präsentation ist wichtig, und sie nutzt zusätlziche Features wie Sortieren, Paginieren und Filtern. Eine Administration kann einfach erzeugt werden, um dann dem Kunden als fertiger Teil des Softwareprodukts übergeben werden.

Das Symfony-Kommandozeilenwerkzeug verwendet das Wort "crud" für das Grundgerüst, und "admin" für eine Administration.

Initiieren oder Erzeugen von Code

Symfony bietet zwei Wege, um Code zu erzeugen: zum einen durch Vererbung (init) oder durch tatsächliche Code-Generierung (generate).

Sie können ein Modul initiieren, also eine leere Klasse erzeugen, die von Framework erbt. Dies verbirgt den PHP-Code der Actions und Templates und vermeidet, dass diese verändert werden. Es ist nützlich, wenn Ihre Datenstrukturen noch nicht endgültig sind, oder Sie einfach einen schnellen Weg in eine Datenbank benötigen, um Datensätze zu manipulieren. Der Code, der zur Laufzeit ausgeführt wird, liegt nicht in Ihrer Anwendung, sondern im Cache. Kommandozeilen-Tasks für diese Art der Generierung beginnen mit propel-init-.

Der initiierte Code einer Action ist leer. So sehen z.B. Actions in einem initiierten Modul artikel so aus:

[php]
class artikelActions extends autoartikelActions
{
}

Andererseits können Sie den Code von Actions und Templates auch so erzeugen, dass Sie ihn verändern können. Das resultierende Modul ist daher unabhängig von den Klassen des Frameworks und kann nicht durch Konfigurationsdateien verändert werden. Kommandozeilen-Tasks für diese Art der Generierung beginnen mit propel-generate-.

Da die Grundgerüste als Basis für die weitere Entwicklung dienen sollen, ist es häufig am sinnvollsten, ein solches Grundgerüst zu generieren. Andererseits sollte eine Administration einfach durch eine Änderung in der Konfiguration zu aktualisieren sein, und sie sollte benutzbar bleiben, selbst wenn sich das Datenmodell ändert. Aus diesem Grund werden Administationen nur initiiert.

Unser Beispiel-Datenmodell

Die Listings in diesem Kapitel werden die Fähigkeiten der Symfony-Generatoren anhand eines einfachen Beispiels demonstrieren, das Sie an Kapitel 8 erinnern könnte. Es ist das wohlbekannte Beispiel einer Weblog-Anwendung, welche die zwei Klassen Artikel und Kommentar besitzt. Listing 14-1 zeigt das Schema, das in Abbildung 14-1 nochmals dargestellt ist.

Listing 14-1 - schema.yml der Beispiel-Anwendung "Weblog"

propel:
  blog_artikel:
    _attributes: { phpName: Artikel }
    id:
    titel:       varchar(255)
    inhalt:      longvarchar
    created_at:
  blog_kommentar:
    _attributes: { phpName: Kommentar }
    id:
    artikel_id:
    autor:       varchar(255)
    inhalt:      longvarchar
    created_at:

Abbildung 14-1 - Beispiel-Datenmodell

Beispiel-Datenmodell

Die Codegenerierung setzt keine Regeln voraus, an die Sie sich während der Erzeugung des Schemas halten müssten. Symfony verwendet das Schema so, wie es ist, und interpretiert dessen Attribute, um ein Grundgerüst oder eine Administration zu erzeugen.

TIP Um das meiste aus diesem Kapitel herauszuholen, müssen Sie die Beispiele auch umsetzen. Sie erhalten ein besseres Verständnis darüber, was Symfony erzeugt und was mit dem erzeugten Code geschehen kann, wenn Sie auf jeden Schritt, der in den Listings beschrieben ist, einen genauen Blick werfen können. Somit sind Sie also eingeladen, eine Datenstruktur wie die oben beschriebene zu erzeugen, eine Datenbank mit den Tabellen blog_artikel und blog_kommentar zu erzeugen und diese Datenbank dann mit Beispieldaten zu befüllen.

Grundgerüst (Scaffolding)

Ein Grundgerüst ist grade am Beginn der Anwendungsentwicklung von großem Nutzen. Mit einem einzigen Kommando erzeugt Symfony ein komplettes Modul, basierend auf der Beschreibung einer bestimmten Tabelle.

Ein Grundgerüst erzeugen

Um das Grundgerüst für ein artikel-Modul zu erstellen, welches auf der Modellklasse Artikel basiert, geben Sie folgendes ein:

> symfony propel-generate-crud frontend artikel Artikel

Symfony liest die Definition der Klasse Artikel aus der schema.yml und erzeugt daraus im Verzeichnis frontend/modules/artikel/ einen Satz Templates und Actions.

Das erzeugte Modul enthält drei Views. Der list-View, der auch der Standardview ist, zeigt beim Aufruf von http://localhost/frontend_dev.php/artikel die Zeilen der Tabelle blog_artikel, wie in Abbildung 14-2 wiedergegeben.

Abbildung 14-2 - list-View des Moduls artikel

list-View des Moduls artikel

Wenn Sie auf die ID eines Artikels klicken, wird der show-View angezeigt. Die Deteils einer Zeile erscheinen auf einer Seite, wie in Abbildung 14-3 zu sehen.

Abbildung 14-3 - show-View des Moduls artikel

show-View des Moduls artikel

Wenn Sie auf den Link "edit" klicken, um einen Artikel zu bearbeiten, oder im list-View auf den Link "create", um einen neuen zu erzeugen, wird der View edit angezeigt, wie in Abbildung 14-4 dargestellt.

Mit diesem Modul können Sie jetzt neue Artikel erzeugen oder bestehende verändern und löschen. Der erzeugte Code ist eine gute Ausgangsplattform für die weitere Entwicklung. Listing 14-2 zeigt die erzeugen Actions und Templates in dem neuen Modul.

Abbildung 14-4 - edit-View des Moduls artikel

edit-View des Moduls artikel

Listing 14-2 - Erzeugte CRUD-Elemente, in frontend/modules/artikel/

// In actions/actions.class.php
index           // Weiterleitung zur list-Action
list            // Zeigt eine Liste mit allen Datensätzen der Tabelle an
show            // Zeigt eine Liste mit allen Spalten eines Datensatzes an
edit            // Zeigt ein Formular, mit dem man die Spalten eines Datensatzes bearbeiten kann
update           // Action, die von dem edit-Formular aufgerufen wird
delete           // Lösch einen Datensatz
create           // Erzeugt einen neuen Datensatz

// In templates/
editSuccess.php  // Formular zum Bearbeiten eines Datensatzes (edit-View)
listSuccess.php  // Liste aller Datensätze (list-View)
showSuccess.php  // Details eines Datensatzes (show-View)

Die Logik dieser Actions und Templates ist einfach und eingängig, weshalb Listing 14-3 nur einen kurzen Blick auf die erzeugte Action-Klasse zeigt, anstatt den kompletten Code ausführlich zu erklären.

Listing 14-3 - Erzeugte Action-Klasse, in frontend/modules/artikel/actions/actions.class.php

[php]
class artikelActions extends sfActions
{
  public function executeIndex()
  {
    return $this->forward('artikel', 'list');
  }

  public function executeList()
  {
    $this->artikels = ArtikelPeer::doSelect(new Criteria());
  }

  public function executeShow()
  {
    $this->artikel = ArtikelPeer::retrieveByPk($this->getRequestParameter('id'));
    $this->forward404Unless($this->artikel);
  }
  ...

Verändern Sie den Code, damit er Ihren Anforderungen genügt, und wiederholen Sie die CRUD-Erzeugung für alle Tabellen, mit denen Sie interagieren wollen - und schon haben Sie eine Grundlage für eine funktionierende Anwendung. Die Erzeugung eines Grundgerüsts legt das Fundament für die Entwicklung; lassen Sie Symfony die Drecksarbeit erledigen und konzentrieren Sie sich auf das Interface und die Feinheiten.

Initiieren eines Grundgerüsts

Das Initiieren eines Grundgerüsts ist vor allem dann nützlich, wenn Sie überprüfen möchten, ob Sie auf die Daten in der Datenbank zugreifen können. Es ist schnell zu erstellen und sobald Sie sicher sind, dass alles funktioniert, ist es genauso schnell wieder gelöscht.

Um ein Propel-Grundgerüst zu initiieren, dass ein artikel-Modul erstellt, das sich mit den Datensätzen der Modelklasse Artikel beschäftigt, geben Sie folgendes ein:

> symfony propel-init-crud frontend artikel Artikel

Sie können dann über die Standardaction auf den list-View zugreifen:

http://localhost/frontend_dev.php/artikel

Die Ergebnisseiten sind genau die gleichen wie bei einem erzeugten Grundgerüst. Sie können sie als einfaches Webinterface für die Datenbank verwenden.

Wenn Sie sich die neu erzeugte action.class.php im Modul artikel ansehen, werden Sie feststellen, dass sie leer ist. Alles ist von einer automatisch erzeugten Klasse geerbt. Genauso verhält es sich mit den Templates: keine einzige Templatedatei befindet sich im Verzeichnis templates/. Der Code hinter den initiierten Actions und Templates ist genau der gleiche wie beim erzeugten Grundgerüst, aber er liegt im Anwendungscache (meinprojekt/cache/meineapp/prod/module/autoArtikel/).

Während der Anwendungsentwicklung initiieren Entwickler solche Grundgerüste, um mit den Daten unabhängig von der Benutzerschnittstelle zu arbeiten. Der Code ist nicht dazu gedacht, angepasst zu werden; ein initiiertes Grundgerüst kann viel mehr als einfache Alternative zu PHPmyadmin gesehen werden, um Daten zu bearbeiten.

Administration

Symfony kann für das Back-End Ihrer Anwendung noch fortgeschrittenere Module erzeugen, die auf der Klassendefinition des Models aus der Datei schema.yml basieren. Sie können die gesamte Administration ihrer Webseite nur mit generierten Administrationsmodulen erzeugen. Die Beispiele dieses Abschnitts wird Administrationsmodule für eine Anwendung namens backend beschreiben. Wenn Ihr Projekt noch keine solche Anwendung hat, können Sie jetzt die Hülle dafür erzeugen, indem Sie den init-app-Task aufrufen:

> symfony init-app backend

Die Administrations-Module verarbeiten das Model anhand einer Konfigurationsdatei namens generator.yml, die verändert werden kann, um die erzeugten Komponenten und das Look'n'Feel des Moduls zu erweitern. Solche Module profitieren von den üblichen Modulmechanismen, die in den vorherigen Kapiteln beschrieben wurden (Layoutl, Validierung, Routing, Konfiguration, Autoloading usw.). Sie können die erzeugten Actions und Templates auch überschreiben, um eigene Funktionen in die erzeugte Administration zu integrieren. generator.yml sollte jedoch für die gängigsten Anforderungen ausreichend sein und beschränkt so die Notwendigkeit von PHP-Code auf die speziellen Anwendungsfälle.

Initiierung eines Administrations-Moduls

In Symfony erzeugen Sie die Administration modulweise. Ein Modul wird anhand eines Propel-Objekts mit dem Task propel-init-admin erstellt, der einen ähnlichen Syntax verwendet wie derjenige, der zum Initiieren des Grundgerüsts verwendet wurde:

> symfony propel-init-admin backend artikel Artikel

Dieser Aufruf genügt, um in der Anwendung backend ein Modul artikel basierend auf der Klassendefinition von Artikel zu erstellen. Darauf zugegriffen wird dann wie folgt:

http://localhost/backend.php/artikel

Das Look'n'Feel eines so erzeugten Moduls, wie die Abbildungen 14-5 und 14-6 zeigen, ist ausreichend, damit es Out-of-the-Box in kommerziellen Anwendungen einsatzfähig ist.

Abbildung 14-5 - list-View des Moduls artikel in der Anwendung backend

list-View des Moduls artikel in der Anwendung backend

Abbildung 14-6 - edit-View des Moduls artikel in der Anwendung backend

edit-View des Moduls artikel in der Anwendung backend

Der Unterschied zwischen der Benutzerschnittstelle, die über ein Grundgerüst erzeugt wird, und derjenigen, die von der Administration erstellt wird, mag im Moment noch nicht weltbewegend sein. Die Konfigurierbarkeit der Administration wird es Ihnen aber ermöglichen, das Grundlayout mit vielen zusätzlichen Funktionen anzureichern, ohne eine einzige Zeile PHP zu schreiben.

NOTEMessages Administrationsmodule können nur initiiert werden (nicht generiert).

Ein Blick auf den erzeugten Code

Der Code des Administrationsmoduls für Artikel im Verzeichnis apps/backend/modules/artikel/ ist leer, da er nur initiiert wurde. Der beste Weg, um den erzeugten Code dieses Moduls anzusehen, ist, es zunächst einmal im Browser anzusprechen und dann den Inhalt des Ordners cache/ zu prüfen. Listing 14-4 zeigt die erzeugten Actions und Templates, die Sie im Cache vorfinden werden.

Listing 14-4 - Erzeugte Elemente einer Administration, in cache/backend/ENV/modules/artikel/

// In actions/actions.class.php
create           // Weiterleitung auf "edit"
delete           // Löscht einen Datensatz
edit             // Zeigt ein Formular, mit dem man die Spalten eines Datensatzes bearbeiten kann
                 // und verarbeitet die Formularübertragung
index            // Weiterleitung auf "list"
list             // Zeigt eine Liste mit allen Datensätzen der Tabelle an
save             // Weiterleitung auf "edit"

// In templates/
_edit_actions.php
_edit_footer.php
_edit_form.php
_edit_header.php
_edit_messages.php
_filters.php
_list.php
_list_actions.php
_list_footer.php
_list_header.php
_list_messages.php
_list_td_actions.php
_list_td_stacked.php
_list_td_tabular.php
_list_th_stacked.php
_list_th_tabular.php
editSuccess.php
listSuccess.php

Hierbei sehen Sie, dass ein erzeugtes Adminstrationsmodul hauptsächlich aus zwei Views besteht, edit und list. Wenn Sie einen Blick in den Code werfen, werden Sie feststellen, dass er sehr modular, lesbar und erweiterbar ist.

Die Konfigurationsdatei generator.yml

Der Hauptunterschied zwischen einem Grundgerüst und einer Administration (außer der Tatsache, dass die erzeugten Administrations-Module keine show-Action haben) ist, dass die Administration auf den Parametern aus der Konfigurationsdatei generator.yml beruht. Um sich die voreingestellte Konfiguration eines neu erzeugten Administrations-Moduls anzusehen, öffnen Sie die Datei generator.yml. Sie befindet sich unter backend/modules/artikel/config/generator.yml und ist im folgenden Listing 14-5 wiedergegeben.

Listing 14-5 - Standardmäßige Generator-Konfiguration, in backend/modules/artikel/config/generator.yml

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Artikel
    theme:            default

Diese Konfiguration reicht aus, um eine einfache Administration zu erzeugen. Jede Anpassung wird unterhalb des Schlüssels param nach der Zeile theme angefügt (was bedeutet, dass alle Zeilen am Ende der Datei generator.yml zumindest mit vier Leerzeichen beginnen müssen, um korrekt eingerückt zu sein). Listing 14-6 zeigt eine typisch angepasste generator.yml.

Listing 14-6 - Komplette typische Konfiguration für einen Generator

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Artikel
    theme:            default

    fields:
      autor_id:       { name: Autor des Artikels }

    list:
      title:          Liste aller Artikel
      display:        [title, autor_id, kategorie_id]
      fields:
        veroeffentlicht_am: { params: date_format='dd/MM/yy' }
      layout:         stacked
      params:         |
        %%ist_veroeffentlicht%%<strong>%%=titel%%</strong><br /><em>von %%autor%%
        in %%kategorie%% (%%veroeffentlicht_am%%)</em><p>%%zusammenfassung%%</p>
      filters:        [titel, kategorie_id, autor_id, ist_veroeffentlicht]
      max_per_page:   2

    edit:
      title:          Artikel "%%titel%%" bearbeiten
      display:
        "Beitrag":    [titel, kategorie_id, inhalt]
        "Workflow":   [autor_id, ist_veroeffentlicht, erzeugt_am]
      fields:
        kategorie_id:        { params: disabled=true }
        ist_veroeffentlicht: { type: plain}
        erzeugt_am:          { type: plain, params: date_format='dd/MM/yy' }
        author_id:           { params: size=5 include_custom=>> Wählen Sie einen Autoren << }
        veroeffentlicht_am:  { credentials:  }
        inhalt:              { params: rich=true tinymce_options=height:150 }

Die folgenden Abschnitte erklären detailliert alle Parameter, die Sie in dieser Konfigurationsdatei verwenden können.

Konfiguration des Generators

Die Konfigurationsdatei für den Generator ist sehr mächtig und erlaubt Ihnen, die erzeugte Administration in vielfältiger Art und Weise zu verändern. Solche Fähigkeiten haben allerdings auch ihren Preis: die gesamte Syntaxbeschreibung ist sehr langwierig zu lesen und zu lernen, und das macht dieses Kapitel zu einem der längsten dieses Buchs. Auf der Symfony-Webseite finden Sie ein nützliches Hilfsmittel, um diesen Syntax zu lernen: Den Spickzettel für den Administrations-Generator, hier wiedergegeben in Abbildung 14-7. Laden Sie ihn von http://www.symfony-project.org/uploads/assets/sfAdminGeneratorRefCard.pdf herunter und halten Sie ihn griffbereit, wenn Sie die folgenden Beispiele dieses Kapitels lesen.

Die Beispiele in diesem Abschnitt werden das Administrationsmodul artikel tunen, ebenso das Administrationsmodul kommentar, das auf der Klassendefinition Kommentar beriht. Erzeugen Sie letzteres mit dem Task propel-init-admin:

> symfony propel-init-admin backend kommentar Kommentar

Abbildung 14-7 - Der Spickzettel für den Administrations-Generator

Der Spickzettel für den Administrations-Generator

Felder

Standardmäßig sind die Spalten des list-Views und die Felder des edit-Views die Spalten, die in schema.yml definiert sind. In der generator.yml können Sie wählen, welche Felder angezeigt werden sollen und welche versteckt sind, und Sie können eigene Felder hinzufügen - selbst wenn Sie keine direkte Entsprechung im Objektmodell haben.

Einstellungen für Felder

Der Administrations-Generator erzeugt ein Feld für jede Spalte in der Datei schema.yml. Unter dem Schlüssel field können Sie anpassen, wie jedes Feld angezeigt, formatiert usw. wird. Die Feld-Einstellungen in Listing 14-7 definieren beispielweise ein eigenes Label und eine eigene Eingabeart für das Feld titel, und ein Label sowie einen Tooltip für das Feld inhalt. Die folgenden Abschnitte beschreiben detailliert, wie jeder Parameter arbeitet.

Listing 14-7 - Ein eigenes Label für eine Spalte festlegen

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Artikel
    theme:            default

    fields:
      titel:          { name: Titel des Artikels, type: textarea_tag, params: class=foo }
      inhalt:         { name: Inhalt, help: Geben Sie den Artikel-Inhalt ein }

Zusätzlich zu diesen Standardangaben für alle Views können Sie die Feld-Einstellungen für einen bestimmten View (list und edit) übersteuern, wie in Listing 14-8 gezeigt.

Listing 14-8 - Globale Einstellungen für jeden View übersteuern

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Artikel
    theme:            default

    fields:
      titel:          { name: Titel des Artikels }
      inhalt:         { name: Inhalt }

    list:
      fields:
        titel:        { name: Titel }

    edit:
      fields:
        inhalt:       { name: Inhalt des Artikels }

Dies ist ein allgemeines Prinzip: Alle Einstellungen, die für das gesamte Modul unterhalb des Schlüssels fields gemacht werden, können durch nachfolgende, view-spezifische Einträge (list und edit) übersteuern werden.

Felder zur Anzeige hinzufügen

Die Felder, sie Sie im Abschnitt fields definieren, können für jeden View angezeigt, versteckt, sortiert und vielfältig gruppiert werden. Für diesen Zweck dient der Schlüssel display. Um beispielsweise die Felder des Moduls kommentar zu arrangieren, verwenden Sie den Code aus Listing 14-9.

Listing 14-9 - Felder für die Anzeige auswählen, in modules/kommentar/config/generator.yml

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Kommentar
    theme:            default

    fields:
      artikel_id:     { name: Artikel }
      created_at:     { name: Veröffentlicht am }
      inhalt:         { name: Inhalt }

    list:
      display:        [id, artikel_id, inhalt]

    edit:
      display:
        NONE:         [artikel_id]
        Editierbar:   [autor, inhalt, created_at]

Der View list wird jetzt drei Spalten anzeigen, wie in Abbildung 14-8 zu sehen, und das edit-Formular zeigt vier Felder in zwei Gruppen an, wie in Abbildung 14-9 gezeigt.

Abbildung 14-8 - Angepasste Spalteneinstellungen im list-View des Moduls kommentar

Angepasste Spalteneinstellungen im list-View des Moduls kommentar

Abbildung 14-9 - Gruppierte Felder im edit-View des Moduls kommentar

Gruppierte Felder im edit-View des Moduls kommentar

Sie können also die Einstellung display für zwei Dinge verwenden:

  • Um die anzuzeigenden Spalten auszuwählen und ihre Reihenfolge festzulegen, schreiben Sie die Felder einfach in ein Array - wie oben im list-View.
  • Um die Felder zu gruppieren, verwenden Sie ein assoziatives Array mit dem Gruppennamen als Schlüssel (NONE steht für eine namenlose Gruppe). Der Wert ist dann wieder ein Array mit den Spaltennamen in der anzuzeigenden Reihenfolge.

TIP Standardmäßig werden die Primärschlüssel in keinem der Views angezeigt.

Selbstdefinierte Felder

Es ist tatsächlich so, dass die Felder, die Sie in generator.yml konfigurieren, nicht einmal den tatsächlichen Spalten im Schema entsprechen müssen. Wenn die entsprechende Klasse einen selbstgeschriebenen Getter besitzt, kann dieser als Feld für den list-View verwendet werden; wenn es einen Getter und/oder einen Setter gibt, auch im edit-View. Sie können beispielsweise das Model Artikel um eine Methode getNbComments() erweitern, ähnlich der in Listing 14-10.

Listing 14-10 - Einen selbstgeschriebenen Getter in das Model einfügen, in lib/model/Artikel.php

[php]
public function getAnzKommentare()
{
  return $this->countKommentars();
}

Danach ist anz_kommentare als Feld im erzeugten Modul verfügbar (beachten Sie, dass die Schreibweise Getter eine camelCase-Version des Feldnamens darstellt), wie in Listing 14-11 gezeigt.

Listing 14-11 - Selbstgeschriebene Getter stellen zusätzliche Spalten für ein Administrations-Modul bereit, in backend/modules/artikel/config/generator.yml

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Artikel
    theme:            default

    list:
      display:        [id, titel, anz_kommentare, created_at]

Der hieraus resultierende list-View des artikel-Moduls ist in Abbildung 14-10 zu sehen.

Abbildung 14-10 - Selbstdefiniertes Feld im list-View des artikel-Moduls

Selbstdefiniertes Feld im list-View des artikel-Moduls

Selbstdefinierte Felder können sogar HTML-Code zurückgeben, um mehr als nur reine Daten anzuzeigen. Sie könnten beispielsweise die Klasse Kommentar um eine Methode getArtikelLink() erweitern, wie in Listing 14-12 gezeigt.

Listing 14-12 - Einen selbstgeschriebenen Getter einfügen, der HTML zurückgibt, in lib/model/Kommentar.class.php

[php]
public function getArtikelLink()
{
  return link_to($this->getArtikel()->getTitel(), 'artikel/edit?id='.$this->getArtikelId());
}

Sie könenn diesen neuen Getter als selbstdefiniertes Feld im kommentar/list-View auf die gleiche Art wie in Listing 14-11 einbinden. Das Beispiel sehen Sie in 14-13, und das Ergebnis in Abbildung 14-11, wo der HTML-Code des Getters (ein Link auf den Artikel) als zweite Spalte anstelle des Primärschlüssels des Artikels erscheint.

Listing 14-13 - Selbstgeschriebene, HTML ausgebende Getter können auch als zusätzliche Spalten verwendet werden, in modules/kommentar/config/generator.yml

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Komentar
    theme:            default

    list:
      display:        [id, artikel_link, inhalt]

Abbildung 14-11 - Selbstdefiniertes Feld im list-View des kommentar-Moduls

Selbstdefiniertes Feld im list-View des kommentar-Moduls

Partial-Felder

Der Code im Modell muss unabhängig von der Präsentation sein. Das vorige Beispiel mit der Methode getArtikelLink() verletzt dieses Prinzip der Ebenentrennnung, da sich Code für den View in der Modell-Ebene befindet. Um das gleiche Ziel auf korrektem Weg zu erreichen, schreiben Sie den HTML ausgebenden Code für selbstdefinierte Felder besser in ein Partial. Praktischerweise erlaubt dies der Administrations-Generator, indem Sie dem Feldnamen einen Unterstrich voranstellen. In diesem Fall müssten Sie die generator.yml aus Listing 14-13 so modifizieren wie in Listing 14-14 gezeigt.

Listing 14-14 - Partials können als zusätzliche Spalten verwendet werden - benutzen Sie das Prefix _

    list:
      display:        [id, _artikel_link, created_at]

Damit dies funktioniert, müssen sie ein Partial namens _artikel_link.php im Verzeichnis modules/kommentar/templates/ anlegen, wie in Listing 14-15 gezeigt.

Listing 14-15 - Beispiel-Parital für den list-View, in modules/kommentar/templates/_artikel_link.php

<?php echo link_to($kommentar->getArtikel()->getTitel(), 'artikel/edit?id='.$kommentar->getArtikelId()) ?>

Wie Sie sehen, hat das Template eines Partial-Felds durch eine Variable, die genauso heißt wie die Klasse (im Beispiel $kommentar) Zugriff auf das aktuelle Objekt. In einem Modul für eine Klassen UserGroup könnte das Partial über die Variable $user_group auf das aktuelle Objekt zugreifen.

Das Ergebnis ist identisch zu dem in Abbildung 14-11, außer dass die Ebenentrennung berücksichtigt ist. Wenn Sie sich daran gewöhnen, auf die saubere Trennung der Ebenen zu achten, sind Ihre Anwendungen letztlich besser wartbar.

Wenn Sie die Parameter eines Partial-Felds anpassen müssen, können Sie dies genauso wie bei einem normalen Feld unter dem Schlüssel field tun. Verwenden Sie dabei jedoch nicht den führenden Unterstrich (_) im Schlüssel, wie das Beispiel in Listing 14-16 zeigt.

Listing 14-16 - Eigenschaften von Partial-Feldern können durch den fields-Schlüssel angepasst werden

      fields:
        artikel_link:   { name: Artikel }

Wenn Ihr Partial mit Programmlogik überfüllt wird, sollten Sie es besser durch eine Komponente ersetzen. Ändern Sie den Prefix _ auf ~, um ein Komponenten-Feld zu definieren, wie Sie in Listing 14-17 sehen können.

Listing 14-17 - Komponenten können als zusätzliche Spalten verwendet werden - benutzen Sie das Prefix ~

    ...
    list:
      display:        [id, ~artikel_link, created_at]

Im erzeugten Template wird dies zu einem Aufruf der Komponente artikelLink im aktuellen Modul führen.

HINWEIS Selbstdefinierte Felder und Partial-Felder können im list-View, im edit-View und für Filter verwendet werden. Wenn Sie das gleiche Partial für mehrere Views verwenden wollen, können Sie den Kontext ('list', 'edit' bzw. 'filter') über die Variable $type abfragen.

Anpassen des Views

Um das Aussehen des edit- und list-Views zu verändern, könnten Sie versucht sein, die Templates zu ändern. Da diese jedoch automatisch erzeugt werden, ist das keine so gute Idee. Statt dessen sollten Sie die Konfigurationsdatei generator.yml verwenden, da Sie dort fast alles einstellen können, was Sie wollen, ohne die Modularität aufzugeben.

Verändern des View-Titels

Neben den Feldern können Sie auch den Seitentitel der list- und edit-Seiten anpassen. Wenn Sie beispielsweise den Titel des artikel-Views anpassen wollen, tun Sie dies wie in Listing 14-18 gezeigt. Der sich ergebende edit-View ist in Abbildung 14-12 zu sehen.

Listing 14-18 - Anpassen des Titels für die Views, in backend/modules/artikel/config/generator.yml

    list:
      title:          Übersicht über die Artikel
      ...

    edit:
      title:          Inhalt des Artikels %%titel%%
      display:        [inhalt]

Abbildung 14-12 - Angepasster Titel im edit-View des artikel-Moduls

Angepasster Titel im edit-View des artikel-Moduls

Da der Standardwert des Titels den Klassennamen beinhaltet, ist er meist ausreichend - vorausgesetzt, Ihr Modell verwendet beschreibende Klassennamen.

TIPP In den String-Werten von generator.yml können Sie auf die Inhalte von Feldern zurückgreifen, indem Sie den Namen des Feldes mit %% umschließen.

Tooltips hinzufügen

In den list- und edit-Views können Sie Tooltipps hinzufügen, um die angezeigten Felder näher zu erklären. Um beispielweise einen Tooltip an das Feld artikel_id im View edit des Moduls kommentar hinzuzufügen, ergänzen Sie die fields-Angabe um die Eigenschaft help, wie in Listing 14-19. Das Ergebnis sehen Sie in Abbildung 14-13.

Listing 14-19 - Einen Tooltip im edit-View setzen, in modules/kommentar/config/generator.yml

    edit:
      fields:
        ...
        artikel_id:   { help: Der aktuelle Kommentar bezieht sich auf diesen Artikel }

Abbildung 14-13 - Tooltip im edit-View des kommentar-Moduls

Tooltip im edit-View des kommentar-Moduls

Im list-View werden die Tooltips in den Spaltenköpfen angezeigt; im edit-View erscheinen sie unterhalb der Eingabefelder.

Datumsformate verändern

Datumswerte können mit einem bestimmten Format angezeigt werden, indem Sie den Parameter date_format verwenden, wie in Listing 14-20 gezeigt wird.

Listing 14-20 - Formatieren eines Datumswerts im list-View

    list:
      fields:
        created_at:         { name: Veröffentlichung, params: date_format='dd/MM' }

Die Formatierungsangabe ist die gleiche wie beim format_date()-Helfer, der im vorherigen Kapitel beschrieben wurde.

SIDEBAR Die Templates der Administration sind I18N-fähig

Der komplette Text, der in den erzeugten Templates verwendet wird, ist internationalisierbar (d.h. er wird durch aufrufe des __()-Helfers ausgegeben). Das bedeutet, dass Sie die erzeugte Administration ganz einfach übersetzen können, indem Sie die Übersetzungen der Phrasen in eine XLIFF-Datei im Verzeichnis apps/frontend/i18n/ schreiben, wie im vorherigen Kapitel erklärt.

Anpassung des Listen-Views

Der list-View kann die Details eines Datensatzes tabellarisch oder aufgereiht in einer Zeile darstellen. Darüber hinaus enthält er Funktionen zum Filtern, Paginieren und Sortieren. Diese Features können über die Konfiguration verändert werden, wie die nächsten Abschnitte zeigen.

Das Layout verändern

Standardmäßig ist der Link zwischen dem list-View und dem edit-View auf die Spalte mit dem Primärschlüssel gelegt. Wenn Sie sich an Abbildung 14-11 erinnern, sehen Sie, das die Spalte id in der Kommentarliste nicht nur den Primärschlüssel jedes Kommentars zeigt, sondern dem Anwender über einen Link auch den Zugriff auf den edit-View bereitstellt.

Wenn Sie den Link auf die Detailansicht auf einer anderen Spalte angezeigt haben wollen, stellen Sie dem Spaltennamen im Schlüssel display ein Gleichheitszeichen (=) voran. Listing 14-21 zeigt, wie Sie die id aus den angezeigten Feldern der Kommentarliste entfernen und statt dessen den Link auf dem Feld inhalt anzeigen. In Abbildung 14-14 sehen Sie den dazu gehörenden Screenshot.

Listing 14-21 - Im list-View den Link zum edit-View auf ein anderes Feld setzen, in modules/kommentar/config/generator.yml

    list:
      display:    [artikel_link, =inhalt]

Abbildung 14-14 - Den Link zum edit-View auf ein anderes Feld setzen, im list-View des Moduls kommentar

![Den Link zum edit-View auf ein anderes Feld setzen, im list-View des Moduls kommentar](/images/book/F1414.png "Den Link zum edit-View auf ein anderes Feld setzen, im list-View des Moduls kommentar")

In der Standardkonfiguration verwendet der list-View das Layout tabular (dt: tabellarisch), in welchem die Felder wie oben gezeigt als Spalten aufgeführt sind. Sie können jedoch auch das Layout stacked (dt: gestapelt) verwenden und die Felder hierdurch zu einer einzigen Zeichenkette zusammensetzen, die sich über die volle Breite der Tabelle erstreckt. Wenn Sie das stacked-Layout wählen, müssen Sie im Schlüssel params durch ein Muster vorgeben, wie jede Zeile der Liste aussieht. Listing 14-22 definiert beispielsweise ein gestapeltes Layout für den list-View im Modul kommentar. Das Resultat zeigt Abbildung 14-15.

Listing 14-22 - Verwendung eines gestapelten (stacked) Layouts im list-View, in modules/kommentar/config/generator.yml

    list:
      layout:  stacked
      params:  |
        %%=inhalt%% <br />
        (erstellt von %%autor%% am %%created_at%% über %%artikel_link%%)
      display:  [created_at, autor, inhalt]

Abbildung 14-15 - Gestapeltes Layout im list-View des Moduls kommentar

Gestapeltes Layout im list-View des Moduls kommentar

Beachten Sie, dass das tabellarische Layout ein Array mit Feldern unterhalb des Schlüssels display erwartet, das gestapelte Layout hingegen den Schlüssel param zur Angabe des HTML-Codes verwendet, der für jeden Datensatz erzeugt wird. Das Array display wird aber beim gestapelten Layout dennoch benötigt, um die Spaltenköpfe zum interaktiven Sortieren zu ermitteln.

Die Ergebnisse filtern

Im list-View können Sie Eingabeelemente zum Filtern definieren. Durch diese Filter können Benutzer weniger Ergebnisse anzeigen und die gewünschten Daten schneller erhalten. Filter werden im Schlüssel filters mit einem Array aus Feldnamen definiert. Fügen Sie beispielsweise - wie in Listing 14-23 gezeigt - Filter für artikel_id, autor und created_at zum list-View der Kommentare hinzu, um eine Filterbox ähnlich der in Abbildung 14-16 zu erhalten. Damit dies funktioniert, müssen Sie noch eine Methode __toString() in der Klasse Artikel angeben (die z.B. den Titel des Artikels zurückgibt).

Listing 14-23 - Filter für den list-View einstellen, in modules/kommentar/config/generator.yml

    list:
      filters: [artikel_id, autor, created_at]
      layout:  stacked
      params:  |
        %%=inhalt%% <br />
        (erstellt von %%autor%% am %%created_at%% über %%artikel_link%%)
      display:  [created_at, autor, inhalt]

Abbildung 14-16 - Filter für den list-View des Moduls kommentar

Filter für den list-View des Moduls kommentar

Wie die Filter von Symfony angezeigt werden, hängt vom Spaltentyp ab:

  • Bei Textspalten (wie das Feld autor im Modul kommentar) besteht der Filter aus einer Texteingabebox, in die eine Textsuche mit Wildcards (*) eingegeben werden kann.
  • Bei Fremdschlüsseln (wie beim Feld artikel_id im Modul kommentar) zeigt der Filter eine Dropdown-Liste mit den Datensätzen aus der referenzierten Tabelle. Wie bei object_select_tag() sind die Optionswerte der Dropdown-Liste diejenigen, die von der __toString()-Methode der entsprechenden Klasse zurückgegeben werden.
  • Bei Datumsspialten (wie das Feld created_at im Modul kommentar) besteht der Filter aus zwei erweiterten Eingabeboxen für Datumswerte (Textfelder, die von einem Kalender-Widget gefüllt werden können), die die Angabe eines Zeitintervalls ermöglichen.
  • Bei Spalten mit boolschen Werten zeigt der Filter eine Dropdown-Liste mit true, false und true or false - wobei der letzte Wert den Filter zurücksetzt.

So wie Sie auch Partial-Felder in Listen verwenden können, können Sie auf Partial-Filter zurückgreifen, um einen Filter zu erzeugen, den Symfony nicht selbst auswerten kann. Stellen Sie sich beispielsweise vor, Sie hätten ein status-Feld mit zwei möglichen Werten (offen und geschlossen), aber aus bestimmten Gründen speichern Sie die Werte direkt im Feld anstatt in einer relationalen Tabelle. Ein einfacher Filter für dieses Feld (das den Typ string hat) ergibt eine Eingabemaske für eine Textsuche; Sie möchten aber eher eine Dropdown-Liste mit den Werten. Dies können Sie durch einen Partial-Filter ganz einfach bewerkstelligen. Listing 14-24 zeigt eine beispielhafte Implementierung.

Listing 14-24 - Verwendung eines Partial-Filters

[php]
// Definieren Sie das Partial in templates/_status.php
<?php echo select_tag('filters[status]', options_for_select(array(
  '' => '',
  'offen' => 'offen',
  'geschlossen' => 'geschlossen',
), isset($filters['status']) ? $filters['status'] : '')) ?>

// Fügen Sie den Partialfilter zur Filterliste hinzu, in config/generator.yml
    list:
      filters:        [date, _status]

Das Partial hat Zugang zur Variable $filters, die nützlich ist, um die aktuellen Werte des Filters zu ermitteln.

Die letzte vorgestellte Option kann sehr nützlich sein, um nach leeren Werten zu prüfen. Stellen Sie sich vor, Sie wollen in der Liste der Kommentar filtern, dass nur noch die ohne Autor angezeigt werden. Das Problem ist, dass der Filter ignoriert wird, wenn Sie ihn leer lassen. Die Lösung ist, die Feldeinstellung filter_is_empty (dt: "Filter ist leer") auf true wie in Listing 14-25 zu setzen, wodurch der Filter eine weitere Checkbox anzeigt, die es Ihnen ermöglicht, nach leeren Werten zu suchen, wie in Abbildung 14-17 gezeigt.

Listing 14-25 - Filtern nach leeren Werten im Feld autor im list-View

    list:
      fields:
        autor:    { filter_is_empty: true }
      filters:    [artikel_id, autor, created_at]

Abbildung 14-17 - Filtern von leeren autor-Werten ermöglichen

Filtern von leeren autor-Werten ermöglichen

Sortieren der Liste

In einem list-View befinden sich Links in den Spaltenköpfen der Tabelle, die dazu dienen, die Liste neu zu sortieren, wie in Abbildung 14-18 gezeigt. Diese Köpfe werden sowohl im tabellarischen als auch im gestapelten Layout gezeigt. Sobald Sie darauf klicken, wird die Seite neu geladen, wobei der Parameter sort dafür sort, dass die Sortierung der Liste entsprechend erfolgt.

Abbildung 14-18 - Die Spaltenköpfe im list-View dienen zum Sortieren

Die Spaltenköpfe im list-View dienen zum Sortieren

Sie können das URL-Schema auch verwenden, um auf eine Liste zu verweisen, die sofort nach einer bestimmten Spalte sortiert sind:

[php]
<?php echo link_to('Kommentarliste sortiert nach Datum', 'kommentar/list?sort=created_at&type=desc' ) ?>

Sie können in generator.yml auch eine Sortierreihenfolge für den list-View vorgeben. Die Syntax ist analog zum Beispiel in Listing 14-26.

Listing 14-26 - Eine Sortierreihenfolge für den list-View vorgeben

    list:
      sort:   created_at
      # Alternative Syntax für die Angabe einer Sortierreihenfolge
      sort:   [created_at, desc]

NOTE Nur die Felder, die einer echten Spalte entsprechen, werden zum Sortieren mit einem entsprechenden Spaltenkopf versehen - nicht jedoch selbstdefinierte und Partial-Felder.

Anpassen der Paginierung

Die erzeugte Administration kann auch mit großen Tabellen sehr effektiv umgehen, da der list-View standardmäßig Paginierung verwendet. Wenn die tatsächliche Anzahl der Zeilen in einer Tabelle die maximalen Zeilen auf einer Seite überschreitet, werden am Ende der Liste Paginierungs-Elemente eingeblendet. Abbildung 14-19 zeigt eine Kommentarliste mit sechs Testeinträgen in der Tabelle, wobei auf der Seite ein Limit von fünf Kommentaren besteht. Die Paginierung sort dabei für eine gute Performanz, da letztlich nur die angezeigten Zeilen aus der Datenbank gelesen werdne, und für eine gute Usability, da selbst Tabellen mit Millionen von Datensätzen durch ein Administrationsmodul verwaltet werden können.

Abbildung 14-19 - Paginierungs-Elemente erscheinen bei langen Listen

Paginierungs-Elemente erscheinen bei langen Listen

Sie können die Anzahl der Datensätze, die auf einer Seite angezeigt werden, mit dem Parameter max_per_page einstellen:

    list:
      max_per_page:   5

Verwendung eines Joins zur Beschleunigung der Seitenauslieferung

Im Normalfall verwendet der Administrationsgenerator ein einfaches doSelect(), um die Datensätze zu holen. Wenn Sie allerdings in der Liste abhängige Objekte ansprechen, würde sich die Anzahl der Datenbankabfragen, die für das Anzeigen der Liste notwendig sind, drastisch erhöhen. Wenn Sie beispielsweise den Artikelnamen in der Liste der Kommentare anzeigen wollen, wird für jeden Beitrag in der Liste eine zusätzliche Abfrage für das abhängige Artikel-Objekt notwendig. Sie könnten daher den Pager zwingen, eine doSelectJoinXXX()-Methode zu verwenden, um die Anzahl der Abfragen zu reduzieren. Dies können Sie über den Parameter peer_method erreichen.

    list:
      peer_method:   doSelectJoinArtikel

In Kapitel 18 wird das Konzept der Joins ausführlicher erklärt.

Anpassung des Edit-Views

In einem edit-View kann der Benutzer den Wert jeder Spalte eines Datensatzes ändern. Symfony ermittelt die Art der anzuzeigenden Eingabebox anhand des Datentyps der Spalte. Dann erzeugt es einen object_*_tag()-Helfer und übergibt diesem Helfer das Objekt und die zu editierende Eigenschaft. Wenn beispielsweise die Konfiguration des edit-Views zu einem Artikels festlegt, dass der User das Feld titel bearbeiten kann:

    edit:
      display: [titel, ...]

dann zeigt die edit-Seite ein normales Texteingabefeld zur Bearbeitung von titel an, da diese Spalte im Schema als Typ varchar definiert ist.

[php]
<?php echo object_input_tag($artikel, 'getTitel') ?>

Verändern der Eingabeart

Die Standardregeln für die Übersetzung von Spaltentyp auf Eingabeart sind wie folgt:

  • Eine Spalte, die als integer, float, char oder varchar(größe) definiert ist, wird im edit-View als object_input_tag() angezeigt.
  • Eine Spalte, die als longvarchar definiert ist, wird als object_textarea_tag() angezeigt.
  • Eine Fremdschlüsselspalte wird als object_select_tag() angezeigt.
  • Eine Spalte, die als boolean definiert ist, wird als object_checkbox_tag() angezeigt.
  • Eine Spalte, die als timestamp oder date definiert ist, wird als object_input_date_tag() angezeigt.

Sie können diese Regeln überstimmen, indem Sie einen eigenen Eingabetyp für ein bestimmtes Feld definieren. Dazu setzen Sie den Parameter type in der Definition fields auf den Namen des enstprechenden Helfers. Die Optionen des erzeugten object_*_tag() können Sie mit dem Parameter params setzen. Listing 14-27 zeigt Ihnen einige Beispiele.

Listing 14-27 - Festlegen von Eingabearten und Parametern im edit-View

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Kommentar
    theme:            default

    edit:
      fields:
                      ## Keine Eingabebox, nur den reinen Text
        id:           { type: plain }
                      ## Die Eingabebox ist nicht editierbar
        autor:        { params: disabled=true }
                      ## Die Eingabebox ist ein Textarea-Tag (object_textarea_tag)
        inhalt:       { type: textarea_tag, params: rich=true css=user.css tinymce_options=width:330 }
                      ## Die Eingabebox ist ein Select-Tag (object_select_tag)
        artikel_id:   { params: include_custom=Wählen Sie einen Artikel }
         ...

Die Parameter in params werden dem erzeugten object_*_tag() als Optionen übergeben. Die params-Definition zu artikel_id im vorigen Beispiel wird im Template wie folgt umgesetzt:

<?php echo object_select_tag($kommentar, 'getArtikelId', 'related_class=Artikel', 'include_custom=Wählen Sie einen Artikel') ?>

Dies bedeutet, dass alle Optionen, die den Formularhelfern üblicherweise zur Verfügung stehen, zur Anpassung des edit-Views verwendet werden können.

Umgang mit Partial-Feldern

In einem edit-View können Sie Partial-Felder genauso wie im list-View verwenden. Der einzige Unterschied ist, dass Sie in der Action das Update der Spalten aus den Request-Parametern des Partial-Feldes händisch durchführen müssen. Symfony weiß, wie es mit regulären Feldern (die einer echten Spalte entsprechen) umgehen muss, aber es kann nicht ermitteln, wie die Eingaben zu behandeln sind, die Sie in einem Parital-Feld machen.

Stellen Sie sich ein Administrationsmodul für eine Klasse User vor, in der die Felder id, spitzname und passwort verwendet werden. Der Administrator der Webseite muss in der Lage sein, dass Passwort eines Users auf Anfrage zu ändern, aber der edit-View darf den Wert des Passwortfeldes aus Sicherheitsgründen nicht anzeigen. Statt dessen sollte das Formular eine leere Passworteingabe anzeigen, die der Administrator ausfüllen kann, um den Wert zu ändern. Die Einstellungen des Generators für einen solchen edit-View sind ähnlich denen in Listing 14-28.

Listing 14-28 - Einbinden eines Partial-Feldes in den edit-View

    edit:
      display:        [id, spitzname, _passwortneu]
      fields:
        passwortneu:  { name: Passwort, help: Geben Sie ein Passwort ein oder lassen Sie das Feld leer, um das aktuelle beizubehalten }

Das Partial unter templates/_passwortneu.php enthält in etwa folgendes:

[php]
<?php echo input_password_tag('passwortneu', '') ?>

Beachten Sie, dass dieses Partial einen einfachen Formularhelfer anstatt eines Objektformularhelfers verwendet, da es nicht sinnvoll ist, das Passwort des aktuellen User-Objekts vorweg in das Formularfeld einzutragen - es könnte das Passwort des Users verraten.

Um nun das Objekt mit dem Wert aus diesem Eingabefeld zu aktualisieren, müssen Sie in der Action die Methode updateUserFromRequest() ableiten. Um dies zu erreichen, erzeugen Sie eine Methode gleichen Namens in der Action-Klasse und fügen Sie die gewünschte Verarbeitung der Eingabe aus dem Partial-Felds hinzu, wie in Listing 14-29 gezeigt.

Listing 14-29 - Verarbeitung eines Partial-Feldes in der Action, in modules/user/actions/actions.class.php

[php]
class userActions extends autouserActions
{
  protected function updateUserFromRequest()
  {
    // Die Eingabe des Partial-Feldes verarbeiten
    $passwort = $this->getRequestParameter('passwortneu');

    if ($passwort)
    {
      $this->user->setPasswort($passwort);
    }

    // Die restlichen Felder verarbeitet Symfony
    parent::updateUserFromRequest();
  }
}

NOTE In einer produktiven Anwendung hätte der View user/edit zwei Passwort-Felder, wobei der Inhalt des zweiten mit dem des ersten übereinstimmen muss, um Tippfehler zu vermeiden. In der Praxis wird dies durch einen Validator erledigt, wie Sie in Kapitel 10 gesehen haben. Das vom Administrationsgenerator erzeugte Modul profitiert von diesem Mechanismus so wie normale Module auch.

Umgang mit Fremdschlüsseln

Wenn Ihr Schema Relationen zwischen Tabellen definiert, können die erzeugten Administrationsmodule daraus Nutzen ziehen und weitergehende Eingabemöglichkeiten anbieten, durch welche sich die Verwaltung von Relationen stark vereinfacht.

1:n-Beziehungen (One-to-Many)

1:n-Beziehungen werden vom Administrationsgenerator berücksichtigt. Wie bereits weiter oben in Abbildung 14-1 gezeigt, ist die Tabelle blog_kommentar über das Feld artikel_id mit der Tabelle blog_artikel verknüpft. Wenn Sie das Modul für die Klasse Kommentar mit dem Administrationsgenerator initialisieren, zeigt die Action kommentar/edit das Feld artikel_id als Dropdown-Liste mit den IDs der verfügbaren Datensätze aus der Tabelle blog_artikel an (dies ist weiter oben in Abbildung 14-9 zu sehen).

Definieren Sie dann zusätzlich eine __toString()-Methode in der Klasse Artikel, wird deren Rückgabewert für die Optionswerte der Dropdown-Liste verwendet anstelle der Primärschlüssel.

Wenn Sie im artikel-Modul eine Liste der Kommentare zu einem Artikel anzeigen wollen (n:1-Beziehung), müssen Sie dieses Modul durch ein Partial-Feld anpassen.

m:n-Beziehungen (Many-to-Many)

Auch m:n-Beziehungen kann Symfony berücksichtigen. Da Sie diese allerdings nicht im Schema definieren können, müssen Sie einige Parameter in generator.yml angeben.

Die Implementierung von m:n-Beziehungen benötigt eine Zwischentabelle. Wenn beispielsweise zwischen den Tabellen blog_artikel und blog_autor eine m:n-Beziehung besteht (ein Artikel kann von mehr als einem Autor geschrieben werden und ein Autor kann offensichtlich mehr als einen Artikel schreiben), wird Ihre Datenbank auf jeden Fall eine Tabelle namens blog_artikel_autor oder so ähnlich enthalten, wie in Abbildung 14-20 zu sehen.

Abbildung 14-20 - Verwendung einer "Zwischentabelle" zur Implementierung von m:n-Beziehungen

Verwendung einer "Zwischentabelle" zur Implementierung von m:n-Beziehungen

Das Modell hat somit eine Klasse namens ArtikelAutor, und dies ist auch schon alles, was der Administrationsgenerator benötigt - Sie müssen das nur im Parameter through_class angeben.

Wenn Sie generator.yml wie in Listing 14-30 verändern, dann fügen Sie im generierten Modul der Klasse Artikel ein Feld hinzu, um neue m:n-Verbindungen mit der Klasse Autor zu erzeugen.

Listing 14-30 - Umgang mit m:n-Beziehungen mit dem Parameter through_class

    edit:
      fields:
        artikel_autor: { type: admin_double_list, params: through_class=ArtikelAutor }

Ein derartiges Feld betrifft Verbindungen zwischen existierenden Objekten, weshalb eine normale Dropdown-Liste dafür nicht ausreicht. Sie müssen eine spezielle Eingabemethode dafür verwenden. Symfony bietet drei Widgets an, die helfen, Objekte aus zwei Listen zu verbinden (siehe Abbildung 14-21)

  • admin_double_list besteht aus zwei Listen, zwischen denen sich zwei Knöpfe befinden, um Elemente aus der ersten Liste (verfügbare Einträge) in die zweite (gewählte Einträge) zu verschieben.
  • admin_select_list ist eine Liste, in welcher Sie mehrere Einträge auswählen können.
  • admin_check_list ist eine Liste aus Checkboxen.

Abbildung 14-21 - Verfügbare Schnittstellen-Elemente für m:n-Beziehungen

Verfügbare Schnittstellen-Elemente für m:n-Beziehungen

Interaktionen hinzufügen

Die Administrationsmodule ermöglihcen es den Anwendern, die üblichen CRUD-Operationen durchzuführen; Sie können jedoch auch Ihre eigenen Interatktionen hinzufügen oder die möglichen Interaktionen für den View einschränken. Die Definition aus Listing 14-31 ermöglicht beispielweise Zugriff auf die standardmäßigen CRUD-Aktionen des Moduls aritkel.

Listing 14-31 - Interaktionen für jeden View definieren, in backend/modules/artikel/config/generator.yml

    list:
      title:          Liste aller Artikel
      object_actions:
        _edit:         ~
        _delete:       ~
      actions:
        _create:       ~

    edit:
      title:          Inhalt des Artikels %%titel%%
      actions:
        _list:         ~
        _save:         ~
        _save_and_add: ~
        _delete:       ~

Im list-View gibt es Einstellungen für zwei Gruppen von Actions: zum einen die Actions, die auf jedes Objekt anwendbar sind, und zum anderen diejenigen, die sich auf die ganze Seite beziehen. Die Listen-Interaktionen aus Listing 14-31 stellen sich wie in Abbildung 14-22 gezeigt dar. Jede Zeile enthält jeweils einen Button zum Editieren des Datensatzes und einen zum Löschen. Am Ende der Liste ermöglicht ein weiterer Button das Anlegen eines neuen Datensatzes.

Abbildung 14-22 - Interaktionen im list-View

![Interaktionen im list-View](/images/book/F1422.png "Interaktionen im list-View")

Da im edit-View immer nur ein Datensatz bearbeitet werden kann, kann auch nur eine Gruppe von Aktionen definiert werden. Die Editier-Interaktionen aus Listing 14-31 stellen sich wie in Abbildung 14-23 gezeigt dar. Sowohl die Action save als auch save_and_add speichern die aktuellen Veränderungen im Datensatz, allerdings zeigt save nach dem Speichern wieder den edit-View mit dem aktuellen Datensatz an, während save_and_add einen leeren edit-View zeigt, mit dem Sie einen neuen Datensatz hinzufügen können. Die Action save_and_add ist ein praktischer Shortcut, wenn Sie mehrere Datensätze in schneller Abfolge eingeben wollen. Anzumerken ist noch, dass die Position des Buttons für die Action delete räumlich von den anderen getrennt ist, damit die Benutzer nicht versehentlich darauf klicken.

Dass die Namen der Interaktionen mit einem Unterstrich (_) beginnen, zeigt Symfony, dass die Standard-Icons und -Actions für diese Interaktionen verwender werden sollen. Der Administrationsgenerator versteht die Werte _edit (Bearbeiten), _delete (Löschen), _create (Erzeugen), _list (Auflisten), _save (Speichern) und _save_and_add (Speichern und erzeugen).

Abbildung 14-23 - Interaktionen im edit-View

Interaktionen im edit-View

Sie können natürlich auch eigene Interaktionen hinzufügen, sofern Sie einen Namen verwenden, der nicht mit einem Unterstrich beginnt, wie in Listing 14-32 gezeigt.

Listing 14-32 - Definition von eigenen Interaktionen

    list:
      title:          Liste aller Artikel
      object_actions:
        _edit:          -
        _delete:        -
        neuerkommentar: { name: Kommentar hinzufügen, action: neuerKommentar, icon: backend/neuerkommentar.png }

Zu jedem Artikel in der Liste wird nun auch ein Knopf mit web/images/neuerkommentar.png eingeblendet, wie in Abbildung 14-24 gezeigt. Sobald man ihn anklickt, wird die Action neuerKommentar im aktuellen Modul aufgerufen. Der Primärschlüssel des aktuellen Objekts wird automatisch zu den Requestparametern hinzugefügt.

Abbildung 14-24 - Eigene Interaktion im list-View

 Eigene Interaktion im list-View

Die Action neuerKommentar könnte dann wie in Listing 14-33 implementiert werden.

Listing 14-33 - Implementierung einer Action für eine eigene Interaktion, in actions/actions.class.php

[php]
public function executeNeuerKommentar()
{
  $kommentar = new Kommentar();
  $kommentar->setArtikelId($this->getRequestParameter('id'));
  $kommentar->save();

  $this->redirect('kommentar/edit?id='.$kommentar->getId());
}

Noch ein letztes Wort zu Actions: Wenn Sie sie für eine Kategorie komplett unterdrücken wollen, geben Sie einfach eine leere Liste an, wie in Listing 14-34.

Listing 14-34 - Alle Actions im list-View unterbinden

    list:
      title:          Liste aller Artikel
      actions:        {}

Formular-Validierung

Wenn Sie das erzeugte Template _edit_form.php im Cache Ihres Projekts betrachten, werden Sie feststellen, dass die Formularfelder eine spezielle Namenskonvention verwendet. In einem erzeugten edit-View bestehen die Namen der Eingabefelder aus dem Klassennamen (in Unterstrich-Schreibweise) und dem Feldnamen in eckigen Klammern.

Wenn beispielsweise der edit-View der Klasse Artikel ein Feld titel hat, sieht das Template so aus wie in Listing 14-35, und das Feld wird als artikel[titel] geführt.

Listing 14-35 - Syntax der erzeugten Eingabefeldnamen

// generator.yml
generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Artikel
    theme:            default
    edit:
      display: [titel]

// Resultierendes Template _edit_form.php
<?php echo object_input_tag($artikel, 'getTitel', array('control_name' => 'artikel[titel]')) ?>

// Resultierendes HTML
<input type="text" name="artikel[titel]" id="artikel_titel" value="Mein Titel" />

Dies hat viele Vorteile während der Formularverarbeitung. Auf der anderen Seite macht es die Formularvalidierung etwas komplizierter, wie in Kapitel 10 erklärt, da Sie in der Felddefinition die eckigen Klammern '[' und ']' durch geschweifte Klammern { und } ersetzen müssen. Außerdem sollten Sie, wenn Sie einen Feldnamen als Parameter für einen Validator verwenden, den Namen so schreiben wie er im erzeugten HTML-Code steht (also mit den eckigen Klammern, aber in Anführungszeichen). In Listing 14-36 erfahren Sie alle Details der speziellen Validator-Syntax für generierte Formulare.

Listing 14-36 - Dateisyntax für Validatoren für Formulare des Administrations-Generators

## Ersetzen Sie in der Feldliste die eckigen Klammern durch geschweifte
fields:
  artikel{titel}:
    required:
      msg: Sie müssen einen Titel angeben.
    ## Parameter für Validatoren enthalten den unveränderten Feldnamen in Anführungszeichen
    sfCompareValidator:
      check:        "user[passwordneu]"
      compare_error: Die Passwortbestätigung stimmt nicht mit dem Passwort überein.

Beschränkung der Interaktion durch Benutzer-Rechte

Für jedes Modul der Administration können Sie gemäß den Rechten des eingeloggten Benutzers die angezeigten Felder und Interaktionen anpassen (Eine Beschreibung von Symfonys Sicherheitsfeatures lesen Sie in Kapitel 6).

Die Felder in generator.yml berücksichtigen den Parameter credentials und werden nur den Benutzern angezeigt, die die entsprechenden Rechte haben. Dies funktioniert für den list-View ebenso wie für den edit-View. Außerdem kann der Generator auch bestimmte Interaktionen verberten. Listing 14-37 zeigt diese Features.

Listing 14-37 - Verwendung von Benutzer-Rechten (Credentials) in generator.yml

## Die Spalte id wird nur Benutzern mit dem Recht "admin" angezeigt
    list:
      title:          Liste aller Artikel
      layout:         tabular
      display:        [id, =titel, inhalt, anz_kommentare]
      fields:
        id:           { credentials: [admin] }

## Die Interaktion "neuerkommentar" ist nur für Benutzer mit dem Recht "admin" zulässig
    list:
      title:          Liste aller Artikel
      object_actions:
        _edit:        -
        _delete:      -
        neuerkommentar: { credentials: [admin], name: Kommentar hinzufügen, action: neuerKommentar, icon: backend/neuerkommentar.png }

Darstellung der generierten Module verändern

Damit die Aufmachung der erzeugten Module bereits bestehenden grafischen Vorgaben genügt, können Sie sie zum einen durch ein eigenes Stylesheet anpassen, zum anderen, indem Sie die Standard-Templates überschreiben.

Verwendung eines eigenen Stylesheets

Weil das erzeugte HTML gut strukturiert ist, können Sie die Darstellung in viele Richtungen anpassen.

Sie können ein alternatives CSS festlegen, welches für ein Administrationsmodul anstelle des Standard-Styles verwendet wird, indem Sie den Parameter css in die Konfiguration des Generators einfügen, wie in Listing 14-38.

Listing 14-38 - Verwendung eines eigenen Stylesheets anstelle des Standard-Styles

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Kommentar
    theme:            default
    css:              meinstylesheet

Alternativ hierzu können Sie auch die Mechanismen verwenden, die von der view.yml der einzelnen Module zur Verfügung gestellt werden, um die Styles gezielt für einen View zu überschreiben.

Eigene Kopf- und Fußzeilen erzeugen

In den Views für list und edit wird systematisch ein Partial für Kopf- und Fußzeilen eingebunden. Standardmäßig fehlt dieses Partial im templates/-Verzeichnis eines Administrationsmoduls, aber Sie müssen einfach nur eines mit einem der folgenden Namen erstellen, damit es automatisch eingebunden wird:

_list_header.php
_list_footer.php
_edit_header.php
_edit_footer.php

Wenn Sie also einen selbstgeschriebenen Header (Kopfzeile) im View artikel/edit wollen, erzeugen Sie eine Datei namens _edit_header.php wie in Listing 14-39. Es wird dann ohne weiteres Zutun eingebunden.

Listing 14-39 - Beispiel eines Header-Partials für den edit-View, in modules/artikel/template/_edit_header.php

[php]
<?php if ($artikel->getAnzKommentare() > 0): ?>
  <h2>Dieser Artikel hat <?php echo $artikel->getAnzKommentare() ?> Kommentare.</h2>
<?php endif; ?>

Wie Sie sehen, hat ein edit-Partial stets über eine Variable, die nach dem Klassennamen benantn ist, Zugriff auf das aktuelle Objekt. Ein list-Partial kann über die Variable $pager auf den Pager zugreifen.

SIDEBAR Actions in der Administration mit eigenen Parametern aufrufen

Den Actions eines Administrations-Moduls können Sie auch selbstdefinierte Parameter übergeben, indem Sie das Argument query_string im Helfer link_to() verwenden. Um beispielweise das Partial _edit_header von oben mit einem Link zu den Kommentaren zu einem Artikel zu erweitern, schreiben Sie dies:

[php]
<?php if ($artikel->getAnzKommentare() > 0): ?>
  <h2>Dieser Artikel hat <?php echo link_to($artikel->getAnzKommentare().' Kommentare', 'kommentar/list', array('query_string' => 'filter=filter&filters%5Bartikel_id%5D='.$artikel->getId())) ?></h2>
<?php endif; ?>

Der Parameter query_string ist eine codierte Version des besser lesbaren

'filter=filter&filters[artikel_id]='.$artikel->getId()

Dies filtert die Liste der Kommentare, so dass nur noch die angezeigt werden, die sich auf $artikel beziehen. Über das Argument query_string können Sie die Sortierung und/oder Filterung der Listenansicht festlegen. Dies kann auch für selbstdefinierte Interaktionen sinnvoll sein.

Anpassung der Layout-Themes

Es gibt noch weitere Partials, die aus dem Framework vererbt werden und die Sie im templates/-Verzeichnis des Moduls überschreiben können, um sie Ihren eigenen Anforderungen anzupassen.

Die Templates des Generators sind in kleine Einheiten zerteilt, die unabhängig voneinander überschrieben werden können. Auch die Actions können jede für sich verändert werden.

Wenn Sie jedoch diese Angaben für verschiedene Module in der gleichen Weise verändern, sollten Sie besser ein wiederverwendbares Theme erzeugen. Ein Theme ist ein kompletter Satz aus Templates und Actions, die von einem Modul der Administration verwendet werden können, wenn dies am Anfang der generator.yml im Feld theme angegeben ist. Als Standard-Theme verwendet Symfony die Dateien aus $sf_symfony_data_dir/generator/sfPropelAdmin/default/.

Die Theme-Dateien müssen innerhalb der Verzeichnisstruktur des Projekts stehen, in einem Verzeichnis data/generator/sfPropelAdmin/[theme_name]/template/. Am besten beginnen Sie ein neues Theme, indem Sie die Dateien des Standard-Themes aus dem Verzeichnis $sf_symfony_data_dir/generator/sfPropelAdmin/default/template/ dorthin kopieren. Auf diese Weise können Sie sicher sein, dass alle notwendigen Dateien für Ihr selbstgeschriebenes Theme vorhanden sind:

// Partials, in [theme_name]/template/templates/
_edit_actions.php
_edit_footer.php
_edit_form.php
_edit_header.php
_edit_messages.php
_filters.php
_list.php
_list_actions.php
_list_footer.php
_list_header.php
_list_messages.php
_list_td_actions.php
_list_td_stacked.php
_list_td_tabular.php
_list_th_stacked.php
_list_th_tabular.php

// Actions, in [theme_name]/template/actions/actions.class.php
processFilters()     // Verarbeitet die Filter
addFiltersCriteria() // Fügt die Filter zum Criteria-Objekt hinzu
processSort()        // Verarbeitet die Sortierung
addSortCriteria()    // Fügt die Sortierung zum Criteria-Objekt hinzu

Beachten Sie, dass diese Template-Dateien in Wirklichkeit Templates von Templates sind, also PHP-Dateien, die von einem speziellen Programm verarbeitet werden, um daraus die eigentlichen Templates zu erzeugen, die auf den Einstellungen des Generators basieren (die sog. Übersetzungs-Schritt oder englisch Compilation Phase). Das erzeugte Template muss natürlich immer noch PHP-Code enthalten, der dann während des eigentlichen Anzeigevorgangs ausgeführt wird, und deshalb benutzen die Template-Templates eine alternative Syntax, um den PHP-Code während des ersten Durchgangs unausgeführt zu lassen. Listing 14-40 zeigt einen Ausschnitt aus einem Standard-Template-Template.

Listing 14-40 - Syntax bei Templates-Templates

[php]
<?php foreach ($this->getPrimaryKey() as $pk): ?>
[?php echo object_input_hidden_tag($<?php echo $this->getSingularName() ?>,'get<?php echo $pk->getPhpName() ?>') ?]
<?php endforeach; ?>

Der PHP-Code in diesem Listing, der durch <? eingeleitet wird, wird unmittelbar während der Übersetzung ausgeführt; der durch [? eingeleitete wird erst während der Ausführung ausgeführt. Der Template-Mechanismus verändert die [?-Tags in <?, so dass das daraus resultierende Template so aussieht:

[php]
<?php echo object_input_hidden_tag($artikel, 'getId') ?>

Der Umgang mit diesen Templates ist tückisch, deshalb sind Sie am besten beraten, wenn Sie Ihre eigenen Themes mit einer Kopie des Standard-Themes beginnen, es dann Schritt für Schritt anpassen und intensiv testen.

TIP Sie können ein Generator-Theme auch in ein Plugin verpacken, was es Ihnen ermöglicht, es noch besser wiederzuverwenden und mit verschiedenen Anwendungen auszuliefern. Kapitel 17 enthält hierzu weitere Informationen.

-

SIDEBAR Erstellung Ihrer eigenen Generatoren

Die Generatoren für das Grundgerüst und die Administration verwenden eine Menge interner Symfony-Komponenten, die die Erstellung von erzeugten Actions und Templates im Cache, die Verwendung von Themes und das Übersetzen von Template-Templates automatisieren.

Das heißt, dass Symfony für Sie auch alle Werkzeuge vorhält, um Ihren eigenen Generator zu schreiben, der so aussehen kann wie die bereits existierenden - oder komplett anders. Die Erzeugung eines Moduls wird von der Methode generate() in sfGeneratorManager verwaltet. Um eine Administration zu erstellen, ruft Symfony intern folgendes auf:

[php]
$generator_manager = new sfGeneratorManager();
$data = $generator_manager->generate('sfPropelAdminGenerator', $parameters);

Wenn Sie Ihren eigenen Generator schreiben wollen, sollten Sie die API-Dokumentation der Klassen sfGeneratorManager und sfGenerator studieren und die Klassen sfAdminGenerator und sfCRUDGenerator als Beispiele betrachten.

Zusammenfassung

Ob als Grundlage Ihrer Module oder um Ihre Backend-Anwendung automatisch zu erzeugen - die Basis ist stets ein wohldefiniertes Schema und ein Objekt-Modell. Den PHP-Code eines Grundgerüsts können Sie bearbeiten, die erzeugten Module der Administration dagegen können Sie in den meisten Fällen durch die Konfiguration verändern.

Die Datei generator.yml stellt das Herzstück für die Programmierung automatisch erzeugter Backends dar. Sie ermöglicht es Ihnen, Inhalt, Features sowie das Look'n'Feel der list- und edit-Views anzupassen. Sie können die Beschriftung der Felder, Tooltips, Filter, Sortierreihenfolge, Seitenlänge, Eingabearten, Fremdschlüsselbeziehungen, selbstdefinierte Interaktionen und Benutzerrechte direkt in der Datei YAML ändern, ohne eine einzige Zeile PHP zu schreiben.

Wenn der Administrations-Generator ein notwendiges Feature nicht von sich aus unterstützt, geben Ihnen die Partial-Felder sowie die Möglichkeit, Actions zu überschreiben, vollständige Erweiterbarkeit. Zusätzlich können Sie Ihre Anpassungen am Administrations-Generator dank des Theme-Mechanismus' wiederverwenden.