<?xml version="1.0" encoding="UTF-8"?>
<!-- EN-Revision: 24249 -->
<!-- Reviewed: no -->
<sect1 id="learning.form.decorators.layering">
    <title>Layeraufbau von Decorators</title>

    <para>
        Wenn man <link linkend="learning.form.decorators.simplest">dem vorigen Abschnitt</link>
        gut gefolgt ist, hat man festgestellt das die <methodname>render()</methodname> Methode
        eines Decorators ein einzelnes <varname>$content</varname> Argument entgegen nimmt. Es wird
        erwartet das dies ein String ist. <methodname>render()</methodname> nimmt dann diesen String
        und entscheidet ob er ersetzt, vorangestellt, oder angehängt wird. Das erlaubt es eine Kette
        von Decorators zu haben -- was es erlaubt Decorators zu erstellen welche nur ein Subset der
        Metadaten des Elements darstellt, und diese Decorators übereinander legt um das volle Markup
        des Elements zu bauen.
    </para>

    <para>
        Schauen wir uns an wie das in der Praxis arbeitet.
    </para>

    <para>
        Für die meisten Typen an Formularelementen werden die folgenden Decorators verwendet:
    </para>

    <itemizedlist>
        <listitem>
            <para>
                <classname>ViewHelper</classname> (stellt die Formulareingabe dar indem einer der
                standardmäßige View Helfer für Formulare verwendet wird).
            </para>
        </listitem>

        <listitem>
            <para>
                <classname>Errors</classname> (stellt Prüffehler durch eine unsortierte Liste dar).
            </para>
        </listitem>

        <listitem>
            <para>
                <classname>Description</classname> (stellt alle dem Element zugeordneten
                Beschreibungen dar; wird oft für Tooltips verwendet).
            </para>
        </listitem>

        <listitem>
            <para>
                <classname>HtmlTag</classname> (umhüllt alle oben stehenden mit einem
                <emphasis>&lt;dd&gt;</emphasis> Tag).
            </para>
        </listitem>

        <listitem>
            <para>
                <classname>Label</classname> (stellt das Label dar indem es dem oben stehenden
                vorangestellt wird und umhüllt es mit einem <emphasis>&lt;dt&gt;</emphasis> Tag).
            </para>
        </listitem>
    </itemizedlist>

    <para>
        Man wird feststellen das jeder dieser Decorators nur ein Ding tut, und auf einem speziellen
        Teil der Metadaten arbeitet die im Formularelement gespeichert sind: der
        <classname>Errors</classname> Decorator holt Prüffehler und stellt Sie dar; der
        <classname>Label</classname> Decorator holt die Überschrift und stellt Sie dar. Das erlaubt
        einzelnen Decorators sehr bündig, wiederholbar, und viel wichtiger, testbar zu sein.
    </para>

    <para>
        Hier kommt auch das <varname>$content</varname> Argument zum Einsatz: Jede
        <methodname>render()</methodname> Methode eines Decorators ist designt um Inhalte zu
        akzeptieren, und diesen dann entweder zu ersetzen (normalerweise indem er umhüllt wird),
        hinten anzuhängen, oder voranzustellen.
    </para>

    <para>
        Es ist also am Besten vom Prozess der Dekoration als Erstellung einer Zwiebel zu denken, von
        Innen nach Außen.
    </para>

    <para>
        Um den Prozess zu vereinfachen sehen wir in der Beispiel <link
            linkend="learning.form.decorators.simplest">des vorherigen Abschnitts</link>. Nochmals:
    </para>

    <programlisting language="php"><![CDATA[
class My_Decorator_SimpleInput extends Zend_Form_Decorator_Abstract
{
    protected $_format = '<label for="%s">%s</label>'
                       . '<input id="%s" name="%s" type="text" value="%s"/>';

    public function render($content)
    {
        $element = $this->getElement();
        $name    = htmlentities($element->getFullyQualifiedName());
        $label   = htmlentities($element->getLabel());
        $id      = htmlentities($element->getId());
        $value   = htmlentities($element->getValue());

        $markup  = sprintf($this->_format, $id, $label, $id, $name, $value);
        return $markup;
    }
}
]]></programlisting>

    <para>
        Jetzt entfernen wir die Funktionalität des Labels und bauen einen eigenen Decorator dafür.
    </para>

    <programlisting language="php"><![CDATA[
class My_Decorator_SimpleInput extends Zend_Form_Decorator_Abstract
{
    protected $_format = '<input id="%s" name="%s" type="text" value="%s"/>';

    public function render($content)
    {
        $element = $this->getElement();
        $name    = htmlentities($element->getFullyQualifiedName());
        $id      = htmlentities($element->getId());
        $value   = htmlentities($element->getValue());

        $markup  = sprintf($this->_format, $id, $name, $value);
        return $markup;
    }
}

class My_Decorator_SimpleLabel extends Zend_Form_Decorator_Abstract
{
    protected $_format = '<label for="%s">%s</label>';

    public function render($content)
    {
        $element = $this->getElement();
        $id      = htmlentities($element->getId());
        $label   = htmlentities($element->getLabel());

        $markup = sprintf($this->_format, $id, $label);
        return $markup;
    }
}
]]></programlisting>

    <para>
        Das könnte jetzt schön und gut aussehen, aber da ist ein Problem: wie gerade geschrieben
        gewinnt der letzte Decorator und überschreibt alles. Man endet nur mit der Eingabe oder nur
        dem Label, abhängig davon was als letztes registriert wurde.
    </para>

    <para>
        Um das zu verhindern, muss dass in <varname>$content</varname> übergebene irgendwie mit dem
        Markup verbunden werden:
    </para>

    <programlisting language="php"><![CDATA[
return $content . $markup;
]]></programlisting>

    <para>
        Das Problem mit dem obigen Ansatz kommt dann wenn man programmtechnisch wählen will ob der
        originale Inhalt das neue Markup angehängt oder vorangestellt werden soll. Glücklicherweise
        gibt es hierfür bereits einen Standardmechanismus;
        <classname>Zend_Form_Decorator_Abstract</classname> hat ein Konzept der Platzierung und
        definiert einige Konstanten um es anzusprechen. Zusätzlich erlaubt es die Spezifikation
        eines Separators der zwischen beide platziert wird. Verwenden wir Sie:
    </para>

    <programlisting language="php"><![CDATA[
class My_Decorator_SimpleInput extends Zend_Form_Decorator_Abstract
{
    protected $_format = '<input id="%s" name="%s" type="text" value="%s"/>';

    public function render($content)
    {
        $element = $this->getElement();
        $name    = htmlentities($element->getFullyQualifiedName());
        $id      = htmlentities($element->getId());
        $value   = htmlentities($element->getValue());

        $markup  = sprintf($this->_format, $id, $name, $value);

        $placement = $this->getPlacement();
        $separator = $this->getSeparator();
        switch ($placement) {
            case self::PREPEND:
                return $markup . $separator . $content;
            case self::APPEND:
            default:
                return $content . $separator . $markup;
        }
    }
}

class My_Decorator_SimpleLabel extends Zend_Form_Decorator_Abstract
{
    protected $_format = '<label for="%s">%s</label>';

    public function render($content)
    {
        $element = $this->getElement();
        $id      = htmlentities($element->getId());
        $label   = htmlentities($element->getLabel());

        $markup = sprint($this->_format, $id, $label);

        $placement = $this->getPlacement();
        $separator = $this->getSeparator();
        switch ($placement) {
            case self::APPEND:
                return $markup . $separator . $content;
            case self::PREPEND:
            default:
                return $content . $separator . $markup;
        }
    }
}
]]></programlisting>

    <para>
        Es sollte beachtet werden das wir das Standardverhalten für jeden verändern; die Annahme
        besteht darin das die Überschrift dem Inhalt folgt und die Eingabe vorangestellt wird.
    </para>

    <para>
        Erstellen wir jetzt ein Formularelement das Sie verwendet:
    </para>

    <programlisting language="php"><![CDATA[
$element = new Zend_Form_Element('foo', array(
    'label'      => 'Foo',
    'belongsTo'  => 'bar',
    'value'      => 'test',
    'prefixPath' => array('decorator' => array(
        'My_Decorator' => 'path/to/decorators/',
    )),
    'decorators' => array(
        'SimpleInput',
        'SimpleLabel',
    ),
));
]]></programlisting>

    <para>
        Wie arbeitet das? Wenn wir <methodname>render()</methodname> aufrufen, wird das Element
        durch die verschiedenen angehängten Decorators iterieren, indem auf jedem
        <methodname>render()</methodname> aufgerufen wird. Er übergibt einen leeren String zu dem
        allerersten, und was auch immer für ein Inhalt erstellt wird, wird dieser an den nächsten
        übergeben, und so weiter:
    </para>

    <itemizedlist>
        <listitem>
            <para>
                Der initiale Inhalt ist ein leerer String: ''.
            </para>
        </listitem>

        <listitem>
            <para>
                '' wird an den <classname>SimpleInput</classname> Decorator übergeben welcher dann
                eine Formulareingabe erstellt und diese an den leeren String anhängt:
                <emphasis>&lt;input id="bar-foo" name="bar[foo]" type="text"
                    value="test"/&gt;</emphasis>.
            </para>
        </listitem>

        <listitem>
            <para>
                Die Eingabe wird dann als Inhalt an den <classname>SimpleLabel</classname> Decorator
                übergeben, welche eine Überschrift erzeugt und diese dem originalen Inhalt
                voranstellt; der standardmäßige Separator ist ein <constant>PHP_EOL</constant>
                Zeichen, was uns folgendes gibt: <emphasis>&lt;label for="bar-foo"&gt;\n&lt;input
                    id="bar-foo" name="bar[foo]" type="text" value="test"/&gt;</emphasis>.
            </para>
        </listitem>
    </itemizedlist>

    <para>
        Einen Moment! Wenn wir wollen das aus irgendeinem Grund die Überschrift nach der Eingabe
        kommt, was dann? Erinnern wir uns an das "placement" Flag? Man kann es als Option an den
        Decorator übergeben. Der einfachste Weg das zu tun ist die Übergabe eines Arrays an
        Optionen an den Decorator wärend der Erstellung des Elements:
    </para>

    <programlisting language="php"><![CDATA[
$element = new Zend_Form_Element('foo', array(
    'label'      => 'Foo',
    'belongsTo'  => 'bar',
    'value'      => 'test',
    'prefixPath' => array('decorator' => array(
        'My_Decorator' => 'path/to/decorators/',
    )),
    'decorators' => array(
        'SimpleInput'
        array('SimpleLabel', array('placement' => 'append')),
    ),
));
]]></programlisting>

    <para>
        Es sollte beachtet werden das der Decorator bei der Übergabe von Optionen in einem Array
        umhüllt werden muss; das zeigt dem Constructor das Optionen vorhanden sind. Der Name des
        Decorators ist das erste Element des Arraqs, und optionen welche in einem Array an das
        zweite Element des Arrays übergeben werden.
    </para>

    <para>
        Das oben stehende führt zum Markup <emphasis>&lt;input id="bar-foo" name="bar[foo]"
            type="text" value="test"/&gt;\n&lt;label for="bar-foo"&gt;</emphasis>.
    </para>

    <para>
        Bei Verwendung dieser Technik kann man Decorators haben welche auf spezifische Metadaten
        eines Elements oder einem Formular abzielen und nur das für diese Metadaten relevante
        Markup erstellt; indem mehrere Decorators verwendet werden kann das komplette Markup des
        Elements gebaut werden. Unsere Zwiebel ist das Ergebnis.
    </para>

    <para>
        Es gibt Vor- und Nachteile für diesen Ansatz. Erst die Nachteile:
    </para>

    <itemizedlist>
        <listitem>
            <para>
                Komplexer zu implementieren. Man muss bei den Decorators und der Platzierung die man
                verwendet gut aufpassen um das Markup in der richtigen Sequenz zu erstellen.
            </para>
        </listitem>

        <listitem>
            <para>
                Ressourcenintensiver. Mehr Decorators bedeuten auch mehr Objekte; das muss mit der
                Anzahl der Elemente multipliziert werden die man im Formular hat, und man könnte in
                einer schweren Ressourcenverwendung enden. Caching kann hierbei helfen.
            </para>
        </listitem>
    </itemizedlist>

    <para>
        Die Vorteile sind wirklich überwältigend:
    </para>

    <itemizedlist>
        <listitem>
            <para>
                Wiederverwendbare Decorators. Man kann mit dieser Technik echte wiederverwendbare
                Decorators erstellen da man sich keine Sorgen über das komplette Markup machen muss,
                sondern nur ein oder ein paar Teile des Elements oder der Metadaten des Formulars.
            </para>
        </listitem>

        <listitem>
            <para>
                Ultimative Flexibilität. Man kann theoretisch jede Markupkombination die man will
                von einer kleinen Anzahl an Decorators erzeugen.
            </para>
        </listitem>
    </itemizedlist>

    <para>
        Wärend die oben stehenden Beispiele die geplante Verwendung der Decorators in
        <classname>Zend_Form</classname> zeigen, ist es oft hart zu erkennen wie Decorators
        untereinander interagieren um das endgültige Markup er bauen. Aus diesem Grund wurde in der
        Serie 1.7 etwas Flexibilität hinzugefügt um die Darstellung individueller Decorators zu
        ermöglichen -- das gibt eine Rails-artige Einfachheit der Darstellung von Formularen. Wir
        sehen uns das im nächsten Abschnitt an.
    </para>
</sect1>
