Zend_Form-Advanced.xml 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- EN-Revision: 15207 -->
  3. <!-- Reviewed: no -->
  4. <sect1 id="zend.form.advanced">
  5. <title>Fortgeschrittene Verwendung von Zend_Form</title>
  6. <para>
  7. <classname>Zend_Form</classname> hat eine Vielzahl an Funktionalitäten, von denen viele auf
  8. fortgeschrittene Entwickler zugeschnitten sind. Dieses Kapitel beschreibt einige dieser
  9. Funktionalitäten mit Beispielen und Usecases.
  10. </para>
  11. <sect2 id="zend.form.advanced.arrayNotation">
  12. <title>Array Schreibweise</title>
  13. <para>
  14. Viele fortgeschrittene Entwickler gruppieren zusammengehörige Formularelemente durch
  15. Verwendung einer Array Schreibweise in den Namen der Elemente. Zum Beispiel, wenn man
  16. zwei Adressen hat die geholt werden sollen, eine Versand- und eine Rechnungsadresse,
  17. hat man identische Elemente; durch deren Gruppierung in einem Array, kann
  18. sichergestellt werden, dass sie separat geholt werden. Nehmen wir die folgende Form
  19. als Beispiel an:
  20. </para>
  21. <programlisting role="html"><![CDATA[
  22. <form>
  23. <fieldset>
  24. <legend>Versandadresse</legend>
  25. <dl>
  26. <dt><label for="recipient">Versand an:</label></dt>
  27. <dd><input name="recipient" type="text" value="" /></dd>
  28. <dt><label for="address">Adresse:</label></dt>
  29. <dd><input name="address" type="text" value="" /></dd>
  30. <dt><label for="municipality">Stadt:</label></dt>
  31. <dd><input name="municipality" type="text" value="" /></dd>
  32. <dt><label for="province">Bundesland:</label></dt>
  33. <dd><input name="province" type="text" value="" /></dd>
  34. <dt><label for="postal">Postleitzahl:</label></dt>
  35. <dd><input name="postal" type="text" value="" /></dd>
  36. </dl>
  37. </fieldset>
  38. <fieldset>
  39. <legend>Rechnungsadresse</legend>
  40. <dl>
  41. <dt><label for="payer">Rechnung an:</label></dt>
  42. <dd><input name="payer" type="text" value="" /></dd>
  43. <dt><label for="address">Adresse:</label></dt>
  44. <dd><input name="address" type="text" value="" /></dd>
  45. <dt><label for="municipality">Stadt:</label></dt>
  46. <dd><input name="municipality" type="text" value="" /></dd>
  47. <dt><label for="province">Bundesland:</label></dt>
  48. <dd><input name="province" type="text" value="" /></dd>
  49. <dt><label for="postal">Postleitzahl:</label></dt>
  50. <dd><input name="postal" type="text" value="" /></dd>
  51. </dl>
  52. </fieldset>
  53. <dl>
  54. <dt><label for="terms">Ich stimme den AGBs zu</label></dt>
  55. <dd><input name="terms" type="checkbox" value="" /></dd>
  56. <dt></dt>
  57. <dd><input name="save" type="submit" value="Speichern" /></dd>
  58. </dl>
  59. </form>
  60. ]]></programlisting>
  61. <para>
  62. In diesem Beispiel enthalten die Rechnungs- und Versanadresse einige identische
  63. Felder, was bedeueten würde, dass sie sich gegenseitig überschreiben. Wir können
  64. das durch die Verwendung der Array Schreibweise lösen:
  65. </para>
  66. <programlisting role="html"><![CDATA[
  67. <form>
  68. <fieldset>
  69. <legend>Versandadresse</legend>
  70. <dl>
  71. <dt><label for="shipping-recipient">Versand an:</label></dt>
  72. <dd><input name="shipping[recipient]" id="shipping-recipient"
  73. type="text" value="" /></dd>
  74. <dt><label for="shipping-address">Adresse:</label></dt>
  75. <dd><input name="shipping[address]" id="shipping-address"
  76. type="text" value="" /></dd>
  77. <dt><label for="shipping-municipality">Stadt:</label></dt>
  78. <dd><input name="shipping[municipality]" id="shipping-municipality"
  79. type="text" value="" /></dd>
  80. <dt><label for="shipping-province">Bundesland:</label></dt>
  81. <dd><input name="shipping[province]" id="shipping-province"
  82. type="text" value="" /></dd>
  83. <dt><label for="shipping-postal">Postleitzahl:</label></dt>
  84. <dd><input name="shipping[postal]" id="shipping-postal"
  85. type="text" value="" /></dd>
  86. </dl>
  87. </fieldset>
  88. <fieldset>
  89. <legend>Rechnungsadresse</legend>
  90. <dl>
  91. <dt><label for="billing-payer">Rechnung an:</label></dt>
  92. <dd><input name="billing[payer]" id="billing-payer"
  93. type="text" value="" /></dd>
  94. <dt><label for="billing-address">Adresse:</label></dt>
  95. <dd><input name="billing[address]" id="billing-address"
  96. type="text" value="" /></dd>
  97. <dt><label for="billing-municipality">Stadt:</label></dt>
  98. <dd><input name="billing[municipality]" id="billing-municipality"
  99. type="text" value="" /></dd>
  100. <dt><label for="billing-province">Bundesland:</label></dt>
  101. <dd><input name="billing[province]" id="billing-province"
  102. type="text" value="" /></dd>
  103. <dt><label for="billing-postal">Postleitzahl:</label></dt>
  104. <dd><input name="billing[postal]" id="billing-postal"
  105. type="text" value="" /></dd>
  106. </dl>
  107. </fieldset>
  108. <dl>
  109. <dt><label for="terms">Ich stimme den AGBs zu</label></dt>
  110. <dd><input name="terms" type="checkbox" value="" /></dd>
  111. <dt></dt>
  112. <dd><input name="save" type="submit" value="Speichern" /></dd>
  113. </dl>
  114. </form>
  115. ]]></programlisting>
  116. <para>
  117. In dem obigen Beispiel erhalten wir jetzt separate Adressen. In der übermittelten Form
  118. haben wir jetzt zwei Elemente, 'shipping' und 'billing', jedes mit Schlüsseln für
  119. deren verschiedene Elemente.
  120. </para>
  121. <para>
  122. <classname>Zend_Form</classname> versucht diesen Prozess mit seinen
  123. <link linkend="zend.form.forms.subforms">Unterformularen</link> zu automatisieren.
  124. Standardmäßig werden Unterformulare dargestellt, indem die Array Schreibweise, wie im
  125. vorherigen HTML Form Code gezeigt, komplett mit Ids, verwendet wird. Der Arrayname
  126. basiert auf dem Namen des Unterformulars, mit den Schlüsseln basierend auf den
  127. Elementen, die im Unterformualr enthalten sind. Unterformulare können beliebig tief
  128. verschachtelt sein, und das erzeugt verschachtelte Arrays um die Struktur zu
  129. reflektieren. Zusätzlich beachten die verschiedenen Prüfroutinen in <classname>Zend_Form</classname>
  130. die Arraystruktur, und stellen sicher, dass die Form korrekt überprüft wird, egal wie
  131. tief verschachtelt die Unterformulare sind. Es muss nichts getan werden, um davon zu
  132. profitieren; dieses Verhalten ist standardmäßig aktiviert.
  133. </para>
  134. <para>
  135. Zusätzlich gibt es Möglichkeiten, die es erlauben die Array Schreibweise fallweise
  136. zu aktivieren, wie auch die Spezifikation eines speziellen Array zu welchem ein
  137. Element oder eine Sammlung gehört:
  138. </para>
  139. <itemizedlist>
  140. <listitem>
  141. <para>
  142. <classname>Zend_Form::setIsArray($flag)</classname>: Durch das Setzen dieses Flags auf
  143. <code>true</code>, kann angezeigt werden, dass das komplette Formular als Array
  144. behandelt werden soll. Standardmäßig wird der Name des Formulars als Name des
  145. Arrays verwendet, solange <code>setElementsBelongTo()</code> aufgerufen wurde.
  146. Wenn das Formular keinen Namen spezifiziert hat, oder <code>setElementsBelongTo()</code>
  147. nicht gesetzt wurde, wird dieses Flag ignoriert (da es kein Arraynamen gibt zu dem
  148. die Elemente gehören).
  149. </para>
  150. <para>
  151. Man kann auswählen, ob ein Formular als Array behandelt wird, indem die
  152. Zugriffsmethode <code>isArray()</code> verwendet wird.
  153. </para>
  154. </listitem>
  155. <listitem><para>
  156. <classname>Zend_Form::setElementsBelongTo($array)</classname>: Durch Verwendung dieser
  157. Methode kann der Name eines Arrays spezifiziert werden dem alle Elemente des
  158. Formulars angehören. Der Name kann durch Verwendung der Zugriffsmethode
  159. <code>getElementsBelongTo()</code> eruiert werden.
  160. </para></listitem>
  161. </itemizedlist>
  162. <para>
  163. Zusätzlich können auf dem Element Level, individuelle Elemente spezifiziert werden die
  164. bestimmten Arrays angehören indem die <classname>Zend_Form_Element::setBelongsTo()</classname>
  165. Methode verwendet wird. Um herauszufinden welcher Wert gesetzt ist -- egal ob dieser
  166. explizit gesetzt wurde oder implzit über das Formular -- kann die Zugriffsmethode
  167. <code>getBelongsTo()</code> verwendet werden.
  168. </para>
  169. </sect2>
  170. <sect2 id="zend.form.advanced.multiPage">
  171. <title>Mehrseitige Formulare</title>
  172. <para>
  173. Aktuell werden mehrseitige Formulare nicht offiziell in <classname>Zend_Form</classname>
  174. unterstützt; trotzdem ist die meiste Unterstützung für deren Implementierung bereits
  175. vorhanden und kann mit etwas extra Code angepasst werden.
  176. </para>
  177. <para>
  178. Der Schlüssel in der Erstellung von mehrseitigen Formularen, ist die Anpassung von
  179. Unterformularen, aber der Anzeige, von nur einem Unterformular pro Seite. Das erlaubt
  180. es, ein einzelnes Unterformular auf einmal zu übertragen und diese zu prüfen, aber das
  181. Formular nicht zu bearbeiten bis alle weiteren Unterformulare komplett sind.
  182. </para>
  183. <example id="zend.form.advanced.multiPage.registration">
  184. <title>Beispiel: Anmeldeformular</title>
  185. <para>
  186. Verwenden wir also ein Anmeldeformular als Beispiel. Für unsere Zwecke wollen wir
  187. auf der ersten Seite einen gewünschten Benutzernamen und Passwort holen, dann die
  188. Metadaten des Benutzers -- das heißt Vorname, Familienname und Wohnort -- und
  189. letztendlich die Auswahl welcher Mailingliste, wenn überhaupt, der Benutzer
  190. angehören will.
  191. </para>
  192. <para>
  193. Erstellen wir als erstes unser, eigenes, Formular und definieren in diesem die
  194. verschiedenen Unterformulare:
  195. </para>
  196. <programlisting role="php"><![CDATA[
  197. class My_Form_Registration extends Zend_Form
  198. {
  199. public function init()
  200. {
  201. // Erstellt die Benutzer Subform: Benutzername und Passwort
  202. $user = new Zend_Form_SubForm();
  203. $user->addElements(array(
  204. new Zend_Form_Element_Text('username', array(
  205. 'required' => true,
  206. 'label' => 'Benutzername:',
  207. 'filters' => array('StringTrim', 'StringToLower'),
  208. 'validators' => array(
  209. 'Alnum',
  210. array('Regex',
  211. false,
  212. array('/^[a-z][a-z0-9]{2,}$/'))
  213. )
  214. )),
  215. new Zend_Form_Element_Password('password', array(
  216. 'required' => true,
  217. 'label' => 'Passwort:',
  218. 'filters' => array('StringTrim'),
  219. 'validators' => array(
  220. 'NotEmpty',
  221. array('StringLength', false, array(6))
  222. )
  223. )),
  224. ));
  225. // Erstellt die Demographische Subform: Vorname,
  226. // Familienname und Ort
  227. $demog = new Zend_Form_SubForm();
  228. $demog->addElements(array(
  229. new Zend_Form_Element_Text('givenName', array(
  230. 'required' => true,
  231. 'label' => 'Vorname (erster):',
  232. 'filters' => array('StringTrim'),
  233. 'validators' => array(
  234. array('Regex',
  235. false,
  236. array('/^[a-z][a-z0-9., \'-]{2,}$/i'))
  237. )
  238. )),
  239. new Zend_Form_Element_Text('familyName', array(
  240. 'required' => true,
  241. 'label' => 'Familienname (letzter):',
  242. 'filters' => array('StringTrim'),
  243. 'validators' => array(
  244. array('Regex',
  245. false,
  246. array('/^[a-z][a-z0-9., \'-]{2,}$/i'))
  247. )
  248. )),
  249. new Zend_Form_Element_Text('location', array(
  250. 'required' => true,
  251. 'label' => 'Der eigene Ort:',
  252. 'filters' => array('StringTrim'),
  253. 'validators' => array(
  254. array('StringLength', false, array(2))
  255. )
  256. )),
  257. ));
  258. // Erstellt die Mailinglisten Subform
  259. $listOptions = array(
  260. 'none' => 'keine Listen bitte',
  261. 'fw-general' => 'Zend Framework General Liste',
  262. 'fw-mvc' => 'Zend Framework MVC Liste',
  263. 'fw-auth' => 'Zend Framework Authentication und ACL Liste',
  264. 'fw-services' => 'Zend Framework Web Services Liste',
  265. );
  266. $lists = new Zend_Form_SubForm();
  267. $lists->addElements(array(
  268. new Zend_Form_Element_MultiCheckbox('subscriptions', array(
  269. 'label' =>
  270. 'Welche Liste wollen Sie erhalten?',
  271. 'multiOptions' => $listOptions,
  272. 'required' => true,
  273. 'filters' => array('StringTrim'),
  274. 'validators' => array(
  275. array('InArray',
  276. false,
  277. array(array_keys($listOptions)))
  278. )
  279. )),
  280. ));
  281. // Die Subformen der Hauptform anhängen
  282. $this->addSubForms(array(
  283. 'user' => $user,
  284. 'demog' => $demog,
  285. 'lists' => $lists
  286. ));
  287. }
  288. }
  289. ]]></programlisting>
  290. <para>
  291. Es ist zu beachten, dass es keinen 'Abschicken' Button gibt, und, dass wir
  292. nichts mit den Dekoratoren des Unterformulars gemacht haben -- was bedeutet,
  293. dass Sie standardmäßig als Fieldsets angezeigt werden. Wir müssen das Überladen
  294. wenn wir jedes individuelle Unterformular anzeigen wollen und einen 'Abschicken'
  295. Button hinzufügen, damit wir sie dann bearbeiten können -- was auch Aktions- und
  296. Methodeneigenschaften benötigt. Füllen wir unsere Klasse ein wenig und bieten
  297. diese Information:
  298. </para>
  299. <programlisting role="php"><![CDATA[
  300. class My_Form_Registration extends Zend_Form
  301. {
  302. // ...
  303. /**
  304. * Eine Subform für die anzeige Vorbereiten
  305. *
  306. * @param string|Zend_Form_SubForm $spec
  307. * @return Zend_Form_SubForm
  308. */
  309. public function prepareSubForm($spec)
  310. {
  311. if (is_string($spec)) {
  312. $subForm = $this->{$spec};
  313. } elseif ($spec instanceof Zend_Form_SubForm) {
  314. $subForm = $spec;
  315. } else {
  316. throw new Exception('Ungültiges Argument an ' .
  317. __FUNCTION__ . '() übergeben');
  318. }
  319. $this->setSubFormDecorators($subForm)
  320. ->addSubmitButton($subForm)
  321. ->addSubFormActions($subForm);
  322. return $subForm;
  323. }
  324. /**
  325. * Form Dekoratore zu einer individuellen Subform hinzufügen
  326. *
  327. * @param Zend_Form_SubForm $subForm
  328. * @return My_Form_Registration
  329. */
  330. public function setSubFormDecorators(Zend_Form_SubForm $subForm)
  331. {
  332. $subForm->setDecorators(array(
  333. 'FormElements',
  334. array('HtmlTag', array('tag' => 'dl',
  335. 'class' => 'zend_form')),
  336. 'Form',
  337. ));
  338. return $this;
  339. }
  340. /**
  341. * Einen Sendebutton in einer individuellen Subform hinzufügen
  342. *
  343. * @param Zend_Form_SubForm $subForm
  344. * @return My_Form_Registration
  345. */
  346. public function addSubmitButton(Zend_Form_SubForm $subForm)
  347. {
  348. $subForm->addElement(new Zend_Form_Element_Submit(
  349. 'save',
  350. array(
  351. 'label' => 'Speichern und Fortfahren',
  352. 'required' => false,
  353. 'ignore' => true,
  354. )
  355. ));
  356. return $this;
  357. }
  358. /**
  359. * Aktion und Methode der Subform hinzufügen
  360. *
  361. * @param Zend_Form_SubForm $subForm
  362. * @return My_Form_Registration
  363. */
  364. public function addSubFormActions(Zend_Form_SubForm $subForm)
  365. {
  366. $subForm->setAction('/registration/process')
  367. ->setMethod('post');
  368. return $this;
  369. }
  370. }
  371. ]]></programlisting>
  372. <para>
  373. Als nächstes benötigen wir das Grundgerüst für unseren Action Controller, und wir
  374. haben verschiedene Entscheidungen zu treffen. Zuerst müssen wir sicherstellen, dass
  375. die Formulardaten zwischen den Anfragen fixiert werden, sodass wir feststellen
  376. können wann abgebrochen wird. Zweitens wird etwas Logik benötigt, um festzustellen
  377. welche Formularteile bereits übermittelt wurden und welches Unterformular, basierend
  378. auf dieser Information, angezeigt werden soll. Wir verwenden
  379. <classname>Zend_Session_Namespace</classname> um Daten zu fixieren, was uns auch hilft die
  380. Frage zu beantworten welches Formular zu übertragen ist.
  381. </para>
  382. <para>
  383. Erstellen wir also unseren Controller, und fügen eine Methode für den Empfang der
  384. Formular Instanz hinzu:
  385. </para>
  386. <programlisting role="php"><![CDATA[
  387. class RegistrationController extends Zend_Controller_Action
  388. {
  389. protected $_form;
  390. public function getForm()
  391. {
  392. if (null === $this->_form) {
  393. $this->_form = new My_Form_Registration();
  394. }
  395. return $this->_form;
  396. }
  397. }
  398. ]]></programlisting>
  399. <para>
  400. Füllen wir unseren Controller nun um die Funktionalität, zu erkennen, welches
  401. Formular angezeigt werden soll. Grundsätzlich müssen wir, bis das komplette
  402. Formular als gültig angenommen wird, die Darstellung der Formularabschnitte
  403. weiterführen. Zusätzlich müssen wir sicherstellen, dass sie in einer bestimmten
  404. Reihenfolge sind: Benutzer, Zusätze und dann Listen. Wir können feststellen, ob
  405. Daten übertragen wurden, indem wir im Namensraum der Session nach speziellen
  406. Schlüssen suchen, die jedes Unterformular repräsentieren.
  407. </para>
  408. <programlisting role="php"><![CDATA[
  409. class RegistrationController extends Zend_Controller_Action
  410. {
  411. // ...
  412. protected $_namespace = 'RegistrationController';
  413. protected $_session;
  414. /**
  415. * Den Session Namespace erhalten den wir verwenden
  416. *
  417. * @return Zend_Session_Namespace
  418. */
  419. public function getSessionNamespace()
  420. {
  421. if (null === $this->_session) {
  422. $this->_session =
  423. new Zend_Session_Namespace($this->_namespace);
  424. }
  425. return $this->_session;
  426. }
  427. /**
  428. * Eine Liste von bereits in der Session gespeicherten Forms erhalten
  429. *
  430. * @return array
  431. */
  432. public function getStoredForms()
  433. {
  434. $stored = array();
  435. foreach ($this->getSessionNamespace() as $key => $value) {
  436. $stored[] = $key;
  437. }
  438. return $stored;
  439. }
  440. /**
  441. * Eine Liste aller vorhandenen Subforms erhalten
  442. *
  443. * @return array
  444. */
  445. public function getPotentialForms()
  446. {
  447. return array_keys($this->getForm()->getSubForms());
  448. }
  449. /**
  450. * Welche Subform wurde übermittelt?
  451. *
  452. * @return false|Zend_Form_SubForm
  453. */
  454. public function getCurrentSubForm()
  455. {
  456. $request = $this->getRequest();
  457. if (!$request->isPost()) {
  458. return false;
  459. }
  460. foreach ($this->getPotentialForms() as $name) {
  461. if ($data = $request->getPost($name, false)) {
  462. if (is_array($data)) {
  463. return $this->getForm()->getSubForm($name);
  464. break;
  465. }
  466. }
  467. }
  468. return false;
  469. }
  470. /**
  471. * Die nächste Suboform für die Anzeige erhalten
  472. *
  473. * @return Zend_Form_SubForm|false
  474. */
  475. public function getNextSubForm()
  476. {
  477. $storedForms = $this->getStoredForms();
  478. $potentialForms = $this->getPotentialForms();
  479. foreach ($potentialForms as $name) {
  480. if (!in_array($name, $storedForms)) {
  481. return $this->getForm()->getSubForm($name);
  482. }
  483. }
  484. return false;
  485. }
  486. }
  487. ]]></programlisting>
  488. <para>
  489. Die obigen Methoden erlauben uns eine Schreibweise wie
  490. <code>$subForm = $this-&gt;getCurrentSubForm();</code> um das aktuelle Unterformular
  491. für die Prüfung zu erhalten, oder <code>$next = $this-&gt;getNextSubForm();</code>,
  492. um die nächste anzuzeigen.
  493. </para>
  494. <para>
  495. Sehen wir uns nun an, wie die verschiedenen Unterformulare bearbeitet und angezeigt
  496. werden können. Wir können <code>getCurrentSubForm()</code> verwenden um
  497. herauszufinden ob ein Unterformular übertragen wurde (<code>false</code> Rückgabewerte
  498. zeigen an, dass keine angezeigt oder übertragen wurden) und <code>getNextSubForm()</code>
  499. empfängt die Form die angezeigt werden soll. Wir können die <code>prepareSubForm()</code>
  500. Methode des Formulars verwenden, um sicherzustellen, dass das Formular bereit zur Anzeige
  501. ist.
  502. </para>
  503. <para>
  504. Wenn ein Formular übertragen wird, kann das Unterformular bestätigt werden, und dann kann
  505. geprüft werden, ob das komplette Formular gültig ist. Um diese Arbeiten durchzuführen
  506. werden zusätzliche Methoden benötigt die sicherstellen, dass die übermittelten Daten der
  507. Session hinzugefügt werden, und, dass, wenn das komplette Formular geprüft wird, die
  508. Prüfung gegen alle Teile der Session durchgeführt wird:
  509. </para>
  510. <programlisting role="php"><![CDATA[
  511. class RegistrationController extends Zend_Form
  512. {
  513. // ...
  514. /**
  515. * Ist die Form gültig?
  516. *
  517. * @param Zend_Form_SubForm $subForm
  518. * @param array $data
  519. * @return bool
  520. */
  521. public function subFormIsValid(Zend_Form_SubForm $subForm,
  522. array $data)
  523. {
  524. $name = $subForm->getName();
  525. if ($subForm->isValid($data)) {
  526. $this->getSessionNamespace()->$name = $subForm->getValues();
  527. return true;
  528. }
  529. return false;
  530. }
  531. /**
  532. * Ist die komplette Form gültig?
  533. *
  534. * @return bool
  535. */
  536. public function formIsValid()
  537. {
  538. $data = array();
  539. foreach ($this->getSessionNamespace() as $key => $info) {
  540. $data[$key] = $info;
  541. }
  542. return $this->getForm()->isValid($data);
  543. }
  544. }
  545. ]]></programlisting>
  546. <para>
  547. Jetzt, da die Kleinarbeiten aus dem Weg sind, können die Aktionen für diesen
  548. Controller erstellt werden. Es wird eine Grundseite für das Formular und dann
  549. eine 'process' (Bearbeiten) Aktion für die Bearbeitung des Formulars benötigt.
  550. </para>
  551. <programlisting role="php"><![CDATA[
  552. class RegistrationController extends Zend_Controller_Action
  553. {
  554. // ...
  555. public function indexAction()
  556. {
  557. // Entweder die aktuelle Seite nochmals anzeigen, oder
  558. // die nächste "next" (erste) Subform holen
  559. if (!$form = $this->getCurrentSubForm()) {
  560. $form = $this->getNextSubForm();
  561. }
  562. $this->view->form = $this->getForm()->prepareSubForm($form);
  563. }
  564. public function processAction()
  565. {
  566. if (!$form = $this->getCurrentSubForm()) {
  567. return $this->_forward('index');
  568. }
  569. if (!$this->subFormIsValid($form,
  570. $this->getRequest()->getPost())) {
  571. $this->view->form = $this->getForm()->prepareSubForm($form);
  572. return $this->render('index');
  573. }
  574. if (!$this->formIsValid()) {
  575. $form = $this->getNextSubForm();
  576. $this->view->form = $this->getForm()->prepareSubForm($form);
  577. return $this->render('index');
  578. }
  579. // Gültige Form!
  580. // Information in einer Prüfseite darstellen
  581. $this->view->info = $this->getSessionNamespace();
  582. $this->render('verification');
  583. }
  584. }
  585. ]]></programlisting>
  586. <para>
  587. Wie festzustellen ist, ist der aktuelle Code für die Bearbeitung des Formulars
  588. relativ einfach. Wir prüfen, um zu sehen ob wir eine aktuelle Übertragung eines
  589. Unterformulars haben, oder nicht, und wir versuchen sie zu prüfen, und sie nochmals
  590. darzustellen wenn es fehlschlägt. Wenn das Unterformular gültig ist, muss anschließend
  591. geprüft werden, ob das Formular gültig ist, was dann bedeuten würde, dass wir fertig
  592. sind; wen nicht muss das nächste Formularsegment angezeigt werden. Letztendlich wird
  593. eine Prüfseite mit dem Inhalt der Session angezeigt.
  594. </para>
  595. <para>
  596. Die View Skripte sind sehr einfach:
  597. </para>
  598. <programlisting role="php"><![CDATA[
  599. <?php // registration/index.phtml ?>
  600. <h2>Registration</h2>
  601. <?php echo $this->form ?>
  602. <?php // registration/verification.phtml ?>
  603. <h2>Danke für die Registrierung!</h2>
  604. <p>
  605. Hier sind die angegebenen Informationen:
  606. </p>
  607. <?
  608. // Dieses Konstrukt muß getan werden wegen dem Weg wie Elemente
  609. // im Session Namespace gespeichert werden
  610. foreach ($this->info as $info):
  611. foreach ($info as $form => $data): ?>
  612. <h4><?php echo ucfirst($form) ?>:</h4>
  613. <dl>
  614. <?php foreach ($data as $key => $value): ?>
  615. <dt><?php echo ucfirst($key) ?></dt>
  616. <?php if (is_array($value)):
  617. foreach ($value as $label => $val): ?>
  618. <dd><?php echo $val ?></dd>
  619. <?php endforeach;
  620. else: ?>
  621. <dd><?php echo $this->escape($value) ?></dd>
  622. <?php endif;
  623. endforeach; ?>
  624. </dl>
  625. <?php endforeach;
  626. endforeach ?>
  627. ]]></programlisting>
  628. <para>
  629. Kommende Releases des Zend Frameworks werden Komponenten enthalten die
  630. mehrseitige Formulare einfacher machen - durch die Abstraktion der Session
  631. und der Reihungslogik. In der Zwischenzeit sollte das obige Beispiel als
  632. angemessene Grundlage dienen, wie diese Aufgabe für eigene Seiten realisiert
  633. werden kann.
  634. </para>
  635. </example>
  636. </sect2>
  637. </sect1>
  638. <!--
  639. vim:se ts=4 sw=4 et:
  640. -->