File: Zend_Form-Advanced.xml

package info (click to toggle)
zendframework 1.12.9%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: jessie-kfreebsd
  • size: 133,584 kB
  • sloc: xml: 1,311,829; php: 570,173; sh: 170; makefile: 125; sql: 121
file content (735 lines) | stat: -rw-r--r-- 27,854 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
<?xml version="1.0" encoding="UTF-8"?>
<!-- EN-Revision: 24249 -->
<!-- Reviewed: no -->
<sect1 id="zend.form.advanced">
    <title>Fortgeschrittene Verwendung von Zend_Form</title>

    <para>
        <classname>Zend_Form</classname> hat eine Vielzahl an Funktionalitäten, von denen viele auf
        fortgeschrittene Entwickler zugeschnitten sind. Dieses Kapitel beschreibt einige dieser
        Funktionalitäten mit Beispielen und Usecases.
    </para>

    <sect2 id="zend.form.advanced.arrayNotation">
        <title>Array Schreibweise</title>

        <para>
            Viele fortgeschrittene Entwickler gruppieren zusammengehörige Formularelemente durch
            Verwendung einer Array Schreibweise in den Namen der Elemente. Zum Beispiel, wenn man
            zwei Adressen hat die geholt werden sollen, eine Versand- und eine Rechnungsadresse,
            hat man identische Elemente; durch deren Gruppierung in einem Array, kann
            sichergestellt werden, dass sie separat geholt werden. Nehmen wir die folgende Form
            als Beispiel an:
        </para>

        <programlisting language="html"><![CDATA[
<form>
    <fieldset>
        <legend>Versandadresse</legend>
        <dl>
            <dt><label for="recipient">Versand an:</label></dt>
            <dd><input name="recipient" type="text" value="" /></dd>

            <dt><label for="address">Adresse:</label></dt>
            <dd><input name="address" type="text" value="" /></dd>

            <dt><label for="municipality">Stadt:</label></dt>
            <dd><input name="municipality" type="text" value="" /></dd>

            <dt><label for="province">Bundesland:</label></dt>
            <dd><input name="province" type="text" value="" /></dd>

            <dt><label for="postal">Postleitzahl:</label></dt>
            <dd><input name="postal" type="text" value="" /></dd>
        </dl>
    </fieldset>

    <fieldset>
        <legend>Rechnungsadresse</legend>
        <dl>
            <dt><label for="payer">Rechnung an:</label></dt>
            <dd><input name="payer" type="text" value="" /></dd>

            <dt><label for="address">Adresse:</label></dt>
            <dd><input name="address" type="text" value="" /></dd>

            <dt><label for="municipality">Stadt:</label></dt>
            <dd><input name="municipality" type="text" value="" /></dd>

            <dt><label for="province">Bundesland:</label></dt>
            <dd><input name="province" type="text" value="" /></dd>

            <dt><label for="postal">Postleitzahl:</label></dt>
            <dd><input name="postal" type="text" value="" /></dd>
        </dl>
    </fieldset>

    <dl>
        <dt><label for="terms">Ich stimme den AGBs zu</label></dt>
        <dd><input name="terms" type="checkbox" value="" /></dd>

        <dt></dt>
        <dd><input name="save" type="submit" value="Speichern" /></dd>
    </dl>
</form>
]]></programlisting>

        <para>
            In diesem Beispiel enthalten die Rechnungs- und Versanadresse einige identische
            Felder, was bedeueten würde, dass sie sich gegenseitig überschreiben. Wir können
            das durch die Verwendung der Array Schreibweise lösen:
        </para>

        <programlisting language="html"><![CDATA[
<form>
    <fieldset>
        <legend>Versandadresse</legend>
        <dl>
            <dt><label for="shipping-recipient">Versand an:</label></dt>
            <dd><input name="shipping[recipient]" id="shipping-recipient"
                type="text" value="" /></dd>

            <dt><label for="shipping-address">Adresse:</label></dt>
            <dd><input name="shipping[address]" id="shipping-address"
                type="text" value="" /></dd>

            <dt><label for="shipping-municipality">Stadt:</label></dt>
            <dd><input name="shipping[municipality]" id="shipping-municipality"
                type="text" value="" /></dd>

            <dt><label for="shipping-province">Bundesland:</label></dt>
            <dd><input name="shipping[province]" id="shipping-province"
                type="text" value="" /></dd>

            <dt><label for="shipping-postal">Postleitzahl:</label></dt>
            <dd><input name="shipping[postal]" id="shipping-postal"
                type="text" value="" /></dd>
        </dl>
    </fieldset>

    <fieldset>
        <legend>Rechnungsadresse</legend>
        <dl>
            <dt><label for="billing-payer">Rechnung an:</label></dt>
            <dd><input name="billing[payer]" id="billing-payer"
                type="text" value="" /></dd>

            <dt><label for="billing-address">Adresse:</label></dt>
            <dd><input name="billing[address]" id="billing-address"
                type="text" value="" /></dd>

            <dt><label for="billing-municipality">Stadt:</label></dt>
            <dd><input name="billing[municipality]" id="billing-municipality"
                type="text" value="" /></dd>

            <dt><label for="billing-province">Bundesland:</label></dt>
            <dd><input name="billing[province]" id="billing-province"
                type="text" value="" /></dd>

            <dt><label for="billing-postal">Postleitzahl:</label></dt>
            <dd><input name="billing[postal]" id="billing-postal"
                type="text" value="" /></dd>
        </dl>
    </fieldset>

    <dl>
        <dt><label for="terms">Ich stimme den AGBs zu</label></dt>
        <dd><input name="terms" type="checkbox" value="" /></dd>

        <dt></dt>
        <dd><input name="save" type="submit" value="Speichern" /></dd>
    </dl>
</form>
]]></programlisting>

        <para>
            In dem obigen Beispiel erhalten wir jetzt separate Adressen. In der übermittelten Form
            haben wir jetzt zwei Elemente, 'shipping' und 'billing', jedes mit Schlüsseln für
            deren verschiedene Elemente.
        </para>

        <para>
            <classname>Zend_Form</classname> versucht diesen Prozess mit seinen
            <link linkend="zend.form.forms.subforms">Unterformularen</link> zu automatisieren.
            Standardmäßig werden Unterformulare dargestellt, indem die Array Schreibweise, wie im
            vorherigen <acronym>HTML</acronym> Form Code gezeigt, komplett mit Ids, verwendet wird.
            Der Arrayname basiert auf dem Namen des Unterformulars, mit den Schlüsseln basierend auf
            den Elementen, die im Unterformualr enthalten sind. Unterformulare können beliebig tief
            verschachtelt sein, und das erzeugt verschachtelte Arrays um die Struktur zu
            reflektieren. Zusätzlich beachten die verschiedenen Prüfroutinen in
            <classname>Zend_Form</classname> die Arraystruktur, und stellen sicher, dass die form
            korrekt überprüft wird, egal wie tief verschachtelt die Unterformulare sind. Es muss
            nichts getan werden, um davon zu profitieren; dieses Verhalten ist standardmäßig
            aktiviert.
        </para>

        <para>
            Zusätzlich gibt es Möglichkeiten, die es erlauben die Array Schreibweise fallweise
            zu aktivieren, wie auch die Spezifikation eines speziellen Array zu welchem ein
            Element oder eine Sammlung gehört:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <methodname>Zend_Form::setIsArray($flag)</methodname>: Durch das Setzen dieses
                    Flags auf <constant>TRUE</constant>, kann angezeigt werden, dass das komplette
                    Formular als Array behandelt werden soll. Standardmäßig wird der Name des
                    Formulars als Name des Arrays verwendet, solange
                    <methodname>setElementsBelongTo()</methodname> aufgerufen wurde. Wenn das
                    Formular keinen Namen spezifiziert hat, oder
                    <methodname>setElementsBelongTo()</methodname> nicht gesetzt wurde, wird dieses
                    Flag ignoriert (da es kein Arraynamen gibt zu dem die Elemente gehören).
                </para>

                <para>
                    Man kann auswählen, ob ein Formular als Array behandelt wird, indem die
                    Zugriffsmethode <methodname>isArray()</methodname> verwendet wird.
                </para>
            </listitem>

            <listitem>
                <para>
                    <methodname>Zend_Form::setElementsBelongTo($array)</methodname>: Durch
                    Verwendung dieser Methode kann der Name eines Arrays spezifiziert werden dem
                    alle Elemente des Formulars angehören. Der Name kann durch Verwendung der
                    Zugriffsmethode <methodname>getElementsBelongTo()</methodname> eruiert werden.
                </para>
            </listitem>
        </itemizedlist>

        <para>
            Zusätzlich können auf dem Element Level, individuelle Elemente spezifiziert werden die
            bestimmten Arrays angehören indem die
            <methodname>Zend_Form_Element::setBelongsTo()</methodname> Methode verwendet wird. Um
            herauszufinden welcher Wert gesetzt ist -- egal ob dieser explizit gesetzt wurde oder
            implzit über das Formular -- kann die Zugriffsmethode
            <methodname>getBelongsTo()</methodname> verwendet werden.
        </para>
    </sect2>

    <sect2 id="zend.form.advanced.multiPage">
        <title>Mehrseitige Formulare</title>

        <para>
            Aktuell werden mehrseitige Formulare nicht offiziell in <classname>Zend_Form</classname>
            unterstützt; trotzdem ist die meiste Unterstützung für deren Implementierung bereits
            vorhanden und kann mit etwas extra Code angepasst werden.
        </para>

        <para>
            Der Schlüssel in der Erstellung von mehrseitigen Formularen, ist die Anpassung von
            Unterformularen, aber der Anzeige, von nur einem Unterformular pro Seite. Das erlaubt
            es, ein einzelnes Unterformular auf einmal zu übertragen und diese zu prüfen, aber das
            Formular nicht zu bearbeiten bis alle weiteren Unterformulare komplett sind.
        </para>

        <example id="zend.form.advanced.multiPage.registration">
            <title>Beispiel: Anmeldeformular</title>

            <para>
                Verwenden wir also ein Anmeldeformular als Beispiel. Für unsere Zwecke wollen wir
                auf der ersten Seite einen gewünschten Benutzernamen und Passwort holen, dann die
                Metadaten des Benutzers -- das heißt Vorname, Familienname und Wohnort -- und
                letztendlich die Auswahl welcher Mailingliste, wenn überhaupt, der Benutzer
                angehören will.
            </para>

            <para>
                Erstellen wir als erstes unser, eigenes, Formular und definieren in diesem die
                verschiedenen Unterformulare:
            </para>

            <programlisting language="php"><![CDATA[
class My_Form_Registration extends Zend_Form
{
    public function init()
    {
        // Erstellt die Benutzer Subform: Benutzername und Passwort
        $user = new Zend_Form_SubForm();
        $user->addElements(array(
            new Zend_Form_Element_Text('username', array(
                'required'   => true,
                'label'      => 'Benutzername:',
                'filters'    => array('StringTrim', 'StringToLower'),
                'validators' => array(
                    'Alnum',
                    array('Regex',
                          false,
                          array('/^[a-z][a-z0-9]{2,}$/'))
                )
            )),

            new Zend_Form_Element_Password('password', array(
                'required'   => true,
                'label'      => 'Passwort:',
                'filters'    => array('StringTrim'),
                'validators' => array(
                    'NotEmpty',
                    array('StringLength', false, array(6))
                )
            )),
        ));

        // Erstellt die Demographische Subform: Vorname,
        // Familienname und Ort
        $demog = new Zend_Form_SubForm();
        $demog->addElements(array(
            new Zend_Form_Element_Text('givenName', array(
                'required'   => true,
                'label'      => 'Vorname (erster):',
                'filters'    => array('StringTrim'),
                'validators' => array(
                    array('Regex',
                          false,
                          array('/^[a-z][a-z0-9., \'-]{2,}$/i'))
                )
            )),

            new Zend_Form_Element_Text('familyName', array(
                'required'   => true,
                'label'      => 'Familienname (letzter):',
                'filters'    => array('StringTrim'),
                'validators' => array(
                    array('Regex',
                          false,
                          array('/^[a-z][a-z0-9., \'-]{2,}$/i'))
                )
            )),

            new Zend_Form_Element_Text('location', array(
                'required'   => true,
                'label'      => 'Der eigene Ort:',
                'filters'    => array('StringTrim'),
                'validators' => array(
                    array('StringLength', false, array(2))
                )
            )),
        ));

        // Erstellt die Mailinglisten Subform
        $listOptions = array(
            'none'        => 'keine Listen bitte',
            'fw-general'  => 'Zend Framework General Liste',
            'fw-mvc'      => 'Zend Framework MVC Liste',
            'fw-auth'     => 'Zend Framework Authentication und ACL Liste',
            'fw-services' => 'Zend Framework Web Services Liste',
        );
        $lists = new Zend_Form_SubForm();
        $lists->addElements(array(
            new Zend_Form_Element_MultiCheckbox('subscriptions', array(
                'label'        =>
                    'Welche Liste wollen Sie erhalten?',
                'multiOptions' => $listOptions,
                'required'     => true,
                'filters'      => array('StringTrim'),
                'validators'   => array(
                    array('InArray',
                          false,
                          array(array_keys($listOptions)))
                )
            )),
        ));

        // Die Subformen der Hauptform anhängen
        $this->addSubForms(array(
            'user'  => $user,
            'demog' => $demog,
            'lists' => $lists
        ));
    }
}
]]></programlisting>

            <para>
                Es ist zu beachten, dass es keinen 'Abschicken' Button gibt, und, dass wir
                nichts mit den Dekoratoren des Unterformulars gemacht haben -- was bedeutet,
                dass Sie standardmäßig als Fieldsets angezeigt werden. Wir müssen das Überladen
                wenn wir jedes individuelle Unterformular anzeigen wollen und einen 'Abschicken'
                Button hinzufügen, damit wir sie dann bearbeiten können -- was auch Aktions- und
                Methodeneigenschaften benötigt. Füllen wir unsere Klasse ein wenig und bieten
                diese Information:
            </para>

            <programlisting language="php"><![CDATA[
class My_Form_Registration extends Zend_Form
{
    // ...

    /**
     * Eine Subform für die anzeige Vorbereiten
     *
     * @param  string|Zend_Form_SubForm $spec
     * @return Zend_Form_SubForm
     */
    public function prepareSubForm($spec)
    {
        if (is_string($spec)) {
            $subForm = $this->{$spec};
        } elseif ($spec instanceof Zend_Form_SubForm) {
            $subForm = $spec;
        } else {
            throw new Exception('Ungültiges Argument an ' .
                                __FUNCTION__ . '() übergeben');
        }
        $this->setSubFormDecorators($subForm)
             ->addSubmitButton($subForm)
             ->addSubFormActions($subForm);
        return $subForm;
    }

    /**
     * Form Dekoratore zu einer individuellen Subform hinzufügen
     *
     * @param  Zend_Form_SubForm $subForm
     * @return My_Form_Registration
     */
    public function setSubFormDecorators(Zend_Form_SubForm $subForm)
    {
        $subForm->setDecorators(array(
            'FormElements',
            array('HtmlTag', array('tag' => 'dl',
                                   'class' => 'zend_form')),
            'Form',
        ));
        return $this;
    }

    /**
     * Einen Sendebutton in einer individuellen Subform hinzufügen
     *
     * @param  Zend_Form_SubForm $subForm
     * @return My_Form_Registration
     */
    public function addSubmitButton(Zend_Form_SubForm $subForm)
    {
        $subForm->addElement(new Zend_Form_Element_Submit(
            'save',
            array(
                'label'    => 'Speichern und Fortfahren',
                'required' => false,
                'ignore'   => true,
            )
        ));
        return $this;
    }

    /**
     * Aktion und Methode der Subform hinzufügen
     *
     * @param  Zend_Form_SubForm $subForm
     * @return My_Form_Registration
     */
    public function addSubFormActions(Zend_Form_SubForm $subForm)
    {
        $subForm->setAction('/registration/process')
                ->setMethod('post');
        return $this;
    }
}
]]></programlisting>

            <para>
                Als nächstes benötigen wir das Grundgerüst für unseren Action Controller, und wir
                haben verschiedene Entscheidungen zu treffen. Zuerst müssen wir sicherstellen, dass
                die Formulardaten zwischen den Anfragen fixiert werden, sodass wir feststellen
                können wann abgebrochen wird. Zweitens wird etwas Logik benötigt, um festzustellen
                welche Formularteile bereits übermittelt wurden und welches Unterformular, basierend
                auf dieser Information, angezeigt werden soll. Wir verwenden
                <classname>Zend_Session_Namespace</classname> um Daten zu fixieren, was uns auch
                hilft die Frage zu beantworten welches Formular zu übertragen ist.
            </para>

            <para>
                Erstellen wir also unseren Controller, und fügen eine Methode für den Empfang der
                Formular Instanz hinzu:
            </para>

            <programlisting language="php"><![CDATA[
class RegistrationController extends Zend_Controller_Action
{
    protected $_form;

    public function getForm()
    {
        if (null === $this->_form) {
            $this->_form = new My_Form_Registration();
        }
        return $this->_form;
    }
}
]]></programlisting>

            <para>
                Füllen wir unseren Controller nun um die Funktionalität, zu erkennen, welches
                Formular angezeigt werden soll. Grundsätzlich müssen wir, bis das komplette
                Formular als gültig angenommen wird, die Darstellung der Formularabschnitte
                weiterführen. Zusätzlich müssen wir sicherstellen, dass sie in einer bestimmten
                Reihenfolge sind: Benutzer, Zusätze und dann Listen. Wir können feststellen, ob
                Daten übertragen wurden, indem wir im Namensraum der Session nach speziellen
                Schlüssen suchen, die jedes Unterformular repräsentieren.
            </para>

            <programlisting language="php"><![CDATA[
class RegistrationController extends Zend_Controller_Action
{
    // ...

    protected $_namespace = 'RegistrationController';
    protected $_session;

    /**
     * Den Session Namespace erhalten den wir verwenden
     *
     * @return Zend_Session_Namespace
     */
    public function getSessionNamespace()
    {
        if (null === $this->_session) {
            $this->_session =
                new Zend_Session_Namespace($this->_namespace);
        }

        return $this->_session;
    }

    /**
     * Eine Liste von bereits in der Session gespeicherten Forms erhalten
     *
     * @return array
     */
    public function getStoredForms()
    {
        $stored = array();
        foreach ($this->getSessionNamespace() as $key => $value) {
            $stored[] = $key;
        }

        return $stored;
    }

    /**
     * Eine Liste aller vorhandenen Subforms erhalten
     *
     * @return array
     */
    public function getPotentialForms()
    {
        return array_keys($this->getForm()->getSubForms());
    }

    /**
     * Welche Subform wurde übermittelt?
     *
     * @return false|Zend_Form_SubForm
     */
    public function getCurrentSubForm()
    {
        $request = $this->getRequest();
        if (!$request->isPost()) {
            return false;
        }

        foreach ($this->getPotentialForms() as $name) {
            if ($data = $request->getPost($name, false)) {
                if (is_array($data)) {
                    return $this->getForm()->getSubForm($name);
                    break;
                }
            }
        }

        return false;
    }

    /**
     * Die nächste Suboform für die Anzeige erhalten
     *
     * @return Zend_Form_SubForm|false
     */
    public function getNextSubForm()
    {
        $storedForms    = $this->getStoredForms();
        $potentialForms = $this->getPotentialForms();

        foreach ($potentialForms as $name) {
            if (!in_array($name, $storedForms)) {
                return $this->getForm()->getSubForm($name);
            }
        }

        return false;
    }
}
]]></programlisting>

            <para>
                Die obigen Methoden erlauben uns eine Schreibweise wie
                <command>$subForm = $this-&gt;getCurrentSubForm();</command> um das aktuelle
                Unterformular für die Prüfung zu erhalten, oder
                <command>$next = $this-&gt;getNextSubForm();</command>, um die nächste anzuzeigen.
            </para>

            <para>
                Sehen wir uns nun an, wie die verschiedenen Unterformulare bearbeitet und angezeigt
                werden können. Wir können <methodname>getCurrentSubForm()</methodname> verwenden um
                herauszufinden ob ein Unterformular übertragen wurde (<constant>FALSE</constant>
                Rückgabewerte zeigen an, dass keine angezeigt oder übertragen wurden) und
                <methodname>getNextSubForm()</methodname> empfängt die Form die angezeigt werden
                soll. Wir können die <methodname>prepareSubForm()</methodname> Methode des
                Formulars verwenden, um sicherzustellen, dass das Formular bereit zur Anzeige ist.
            </para>

            <para>
                Wenn ein Formular übertragen wird, kann das Unterformular bestätigt werden, und dann
                kann geprüft werden, ob das komplette Formular gültig ist. Um diese Arbeiten
                durchzuführen werden zusätzliche Methoden benötigt die sicherstellen, dass die
                übermittelten Daten der Session hinzugefügt werden, und, dass, wenn das komplette
                Formular geprüft wird, die Prüfung gegen alle Teile der Session durchgeführt wird:
            </para>

            <programlisting language="php"><![CDATA[
class RegistrationController extends Zend_Form
{
    // ...

    /**
     * Ist die Form gültig?
     *
     * @param  Zend_Form_SubForm $subForm
     * @param  array $data
     * @return bool
     */
    public function subFormIsValid(Zend_Form_SubForm $subForm,
                                   array $data)
    {
        $name = $subForm->getName();
        if ($subForm->isValid($data)) {
            $this->getSessionNamespace()->$name = $subForm->getValues();
            return true;
        }

        return false;
    }

    /**
     * Ist die komplette Form gültig?
     *
     * @return bool
     */
    public function formIsValid()
    {
        $data = array();
        foreach ($this->getSessionNamespace() as $key => $info) {
            $data[$key] = $info;
        }

        return $this->getForm()->isValid($data);
    }
}
]]></programlisting>

            <para>
                Jetzt, da die Kleinarbeiten aus dem Weg sind, können die Aktionen für diesen
                Controller erstellt werden. Es wird eine Grundseite für das Formular und dann
                eine 'process' (Bearbeiten) Aktion für die Bearbeitung des Formulars benötigt.
            </para>

            <programlisting language="php"><![CDATA[
class RegistrationController extends Zend_Controller_Action
{
    // ...

    public function indexAction()
    {
        // Entweder die aktuelle Seite nochmals anzeigen, oder
        // die nächste "next" (erste) Subform holen
        if (!$form = $this->getCurrentSubForm()) {
            $form = $this->getNextSubForm();
        }
        $this->view->form = $this->getForm()->prepareSubForm($form);
    }

    public function processAction()
    {
        if (!$form = $this->getCurrentSubForm()) {
            return $this->_forward('index');
        }

        if (!$this->subFormIsValid($form,
                                   $this->getRequest()->getPost())) {
            $this->view->form = $this->getForm()->prepareSubForm($form);
            return $this->render('index');
        }

        if (!$this->formIsValid()) {
            $form = $this->getNextSubForm();
            $this->view->form = $this->getForm()->prepareSubForm($form);
            return $this->render('index');
        }

        // Gültige Form!
        // Information in einer Prüfseite darstellen
        $this->view->info = $this->getSessionNamespace();
        $this->render('verification');
    }
}
]]></programlisting>

            <para>
                Wie festzustellen ist, ist der aktuelle Code für die Bearbeitung des Formulars
                relativ einfach. Wir prüfen, um zu sehen ob wir eine aktuelle Übertragung eines
                Unterformulars haben, oder nicht, und wir versuchen sie zu prüfen, und sie nochmals
                darzustellen wenn es fehlschlägt. Wenn das Unterformular gültig ist, muss
                anschließend geprüft werden, ob das Formular gültig ist, was dann bedeuten würde,
                dass wir fertig sind; wen nicht muss das nächste Formularsegment angezeigt werden.
                Letztendlich wird eine Prüfseite mit dem Inhalt der Session angezeigt.
            </para>

            <para>
                Die View Skripte sind sehr einfach:
            </para>

            <programlisting language="php"><![CDATA[
<?php // registration/index.phtml ?>
<h2>Registration</h2>
<?php echo $this->form ?>

<?php // registration/verification.phtml ?>
<h2>Danke für die Registrierung!</h2>
<p>
    Hier sind die angegebenen Informationen:
</p>

<?
// Dieses Konstrukt muß getan werden wegen dem Weg wie Elemente
// im Session Namespace gespeichert werden
foreach ($this->info as $info):
    foreach ($info as $form => $data): ?>
<h4><?php echo ucfirst($form) ?>:</h4>
<dl>
    <?php foreach ($data as $key => $value): ?>
    <dt><?php echo ucfirst($key) ?></dt>
    <?php if (is_array($value)):
        foreach ($value as $label => $val): ?>
    <dd><?php echo $val ?></dd>
        <?php endforeach;
       else: ?>
    <dd><?php echo $this->escape($value) ?></dd>
    <?php endif;
    endforeach; ?>
</dl>
<?php endforeach;
endforeach ?>
]]></programlisting>

            <para>
                Kommende Releases des Zend Frameworks werden Komponenten enthalten die
                mehrseitige Formulare einfacher machen - durch die Abstraktion der Session
                und der Reihungslogik. In der Zwischenzeit sollte das obige Beispiel als
                angemessene Grundlage dienen, wie diese Aufgabe für eigene Seiten realisiert
                werden kann.
            </para>
        </example>
    </sect2>
</sect1>