Tirer parti des décorateurs de Zend_Form

2,580 views
2,491 views

Published on

Comment tirer parti des décorateurs de Zend_Form ?

Published in: Technology, Business
1 Comment
1 Like
Statistics
Notes
No Downloads
Views
Total views
2,580
On SlideShare
0
From Embeds
0
Number of Embeds
7
Actions
Shares
0
Downloads
43
Comments
1
Likes
1
Embeds 0
No embeds

No notes for slide
  • An interface
  • Concrete implementation of the interface
  • Traditional locked window; implements same interface as decorated object
  • Duck-typed locked window
  • UML diagram of strategy pattern. Concrete object allows attaching objects that implement a particular strategy, or interface; it then calls a defined method on the attached object
  • Calling object and interface for strategy
  • Implementations of the strategy
  • Generate simple markup with label and form input
  • Two ways to use the decorator: (1) attaching the instance (2) setting the plugin path and specifying it by short name
  • The markup generated
  • How layering works: * First decorator is passed empty content * First decorator replaces content by rendering a view helper and passing the rendered markup to the next decorator
  • How layering works: * Second decorator pulls the element description, if any, and renders it using formNote() view helper; then appends it and passes it to the third decorator
  • How layering works: * Third decorator pulls any validation errors, and, if found, passes them to the formErrors() view helper; this is also appended to the content passed in and passed to the fourth decorator
  • How layering works: * Fourth decorator wraps all the content with a <dd> tag and passes the aggregate content to the fifth decorator
  • How layering works: * Fifth decorator generates label using formLabel() view helper, and prepends it to the content; the aggregate is returned
  • Revised SimpleInput decorator; no label generation
  • Label decorator; only renders label
  • Typical approach to handling placement (typically append or prepend; replace would not use this strategy)
  • Specifying multiple decorators at instantiation
  • Specifying alternate placement for a specific decorator
  • Reflecting on the element in order to generate custom markup; renders the label and view helper decorators
  • Composite markup that might not be achieved otherwise... which leads into the next topic
  • Use the discrete value segments from the element to populate the form input
  • Tirer parti des décorateurs de Zend_Form

    1. 1. Tirer parti des décorateurs Zend_Form Mickaël Perraud Contributeur Zend Framework Responsable documentation française
    2. 2. Qu'est-ce qu'un décorateur ?
    3. 3. Dans Zend_Form … <ul><li>Combinaison des patrons de conception Decorator et Strategy </li></ul>
    4. 4. Patron de conception Décorateur “ En programmation orientée objet, le patron de conception décorateur est un motif qui permet l'ajout dynamique de fonctionnalités à un objet existant.” – Wikipedia, “Décorateur_(patron_de_conception)”
    5. 6. Deux techniques principales <ul><li>Définir une interface à laquelle tous les objets concrets et les décorateurs adhèrent </li><ul><li>Souvent, les décorateurs implémenteront une interface additionnelle </li></ul><li>Duck-typing </li><ul><li>Utilise une méthode/propriété dans le décorateur pour proxier vers l'objet décoré </li></ul></ul>
    6. 7. interface Window { public function isOpen(); public function open(); public function close (); }
    7. 8. class StandardWindow implements Window { protected $_open = false; public function isOpen() { return $this ->_open; } public function open() { if (! $this ->_open) { $this ->_open = true; } } public function close () { if ( $this ->_open) { $this ->_open = false; } } }
    8. 9. class LockedWindow implements Window { protected $_window ; public function __construct(Window $window ){ $this ->_window = $window ; $this ->_window-> close (); } public function isOpen() { return false; } public function open() { throw new Exception( 'Cannot open locked windows' ); } public function close () { $this ->_window-> close (); } }
    9. 10. class LockedWindow { protected $_window ; public function __construct(Window $window ) { $this ->_window = $window ; $this ->_window-> close (); } public function isOpen() { return false; } public function __call( $method , $args ) { if (! method_exists ( $this ->_window, $method )) { throw new Exception( 'Invalid method' ); } return $this ->_window-> $method (); } }
    10. 11. Patron de conception Stratégie “ Un patron de conception particulier, dans lequel les algorithmes peuvent être sélectionnés lors de l'exécution.” – Wikipedia, “Strategy_pattern”
    11. 13. class Window { public $strategy ; public function open() { $this -> $strategy ->open(); } } interface OpenStrategy { public function open(); }
    12. 14. class RaiseStrategy implements OpenStrategy { public function open() { } } class LeverStrategy implements OpenStrategy { public function open() { } }
    13. 15. Créer votre premier décorateur
    14. 16. L'interface <ul><li>Zend_Form_Decorator_Interface </li><ul><li>__construct($options = null);
    15. 17. setElement($element);
    16. 18. getElement();
    17. 19. setOptions(array $options);
    18. 20. setConfig(Zend_Config $config);
    19. 21. setOption($key, $value);
    20. 22. getOption($key);
    21. 23. getOptions();
    22. 24. removeOption($key);
    23. 25. clearOptions();
    24. 26. render($content); </li></ul></ul>
    25. 27. L'interface <ul><li>Ce qu'il est nécessaire de retenir : </li><ul><li>render($content); </li></ul></ul>
    26. 28. Un décorateur simple pour un champ texte <ul><li>Va rendre un label
    27. 29. Va rendre le champ texte
    28. 30. Va récupérer les méta-données à partir de l'élément et les utiliser pour définir l'affichage généré </li></ul>
    29. 31. class My_Decorator_SimpleInput extends Zend_Form_Decorator_Abstract { protected $_format = '<label for=&quot;%s&quot;>%s</label>' . '<input id=&quot;%s&quot; name=&quot;%s&quot; type=&quot;text&quot; value=&quot;%s&quot;/>' ; 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, $name , $label , $id , $name , $value ); return $markup ; } }
    30. 32. $decorator = new My_Decorator_SimpleInput(); $element = new Zend_Form_Element( 'foo' , array ( 'label' => 'Foo' , 'belongsTo' => 'bar' , 'value' => 'test' , 'decorators' => array ( $decorator ), )); $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' ), ));
    31. 33. <label for = &quot;bar[foo]&quot; > Foo </label> <input id = &quot;bar-foo&quot; name = &quot;bar[foo]&quot; type = &quot;text&quot; value = &quot;test&quot; />
    32. 34. Où en sommes-nous ? <ul><li>Ré-utilisation
    33. 35. Échappement automatique
    34. 36. Sélection des attributs que vous souhaitez propager et afficher au moment de l'exécution </li></ul>
    35. 37. Superposer les décorateurs
    36. 38. L'argument $content <ul><li>Utilisé pour agréger les sorties des multiples décorateurs
    37. 39. Des décorateurs individuels peuvent être spécialisés dans la création d'affichages liés à certaines métadonnées de l'élément
    38. 40. L'affichage final est le produit de tous les décorateurs </li></ul>
    39. 41. Décorateurs par défaut <ul><li>ViewHelper – utilise une aide de vue pour effectuer le rendu de l'élément de formulaire lui-même
    40. 42. Description – effectue le rendu de la description de l'élément, si besoin
    41. 43. Errors – effectue le rendu des erreurs de validation, si besoin
    42. 44. HtmlTag – enveloppe tout le contenu généré dans une balise
    43. 45. Label – utilise une aide de vue pour effectuer le rendu du libellé de l'élément ; l'ajoute devant le contenu agrégé </li></ul>
    44. 51. Comment superposer les décorateurs ? <ul><li>Utilisez l'option “placement”
    45. 52. Choississez de : </li><ul><li>Ajouter après le contenu fourni (APPEND)
    46. 53. Ajouter avant le contenu fourni (PREPEND)
    47. 54. Remplacer le contenu fourni (REPLACE) </li></ul><li>Utiliser un placement par défaut sain ; les options vous permettront de le surcharger </li></ul>
    48. 55. class My_Decorator_SimpleInput extends Zend_Form_Decorator_Abstract { protected $_format = '<input id=&quot;%s&quot; name=&quot;%s&quot; type=&quot;text&quot; value=&quot;%s&quot;/>' ; 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 ); // On gère le placement ici... return $markup ; } }
    49. 56. class My_Decorator_SimpleLabel extends Zend_Form_Decorator_Abstract { protected $_format = '<label for=&quot;%s&quot;>%s</label>' ; public function render( $content ) { $element = $this ->getElement(); $id = htmlentities ( $element ->getId()); $label = htmlentities ( $element ->getLabel()); $markup = sprint( $this ->_format, $id , $label ); // On gère le placement ici... return $markup ; } }
    50. 57. $placement = $this ->getPlacement(); $separator = $this ->getSeparator(); switch ( $placement ) { case self::PREPEND: return $markup . $separator . $content ; case self::APPEND: default: return $content . $separator . $markup ; } switch ( $placement ) { case self::APPEND: return $content . $separator . $markup ; case self::PREPEND: default: return $markup . $separator . $content ; }
    51. 58. $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' , ), ));
    52. 59. $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' )), ), ));
    53. 60. Rendre les décorateurs individuellement
    54. 61. Pourquoi ? <ul><li>Simplement parfois, parce que le balisage est trop complexe pour être assemblé par superposition
    55. 62. Vos designers peuvent vouloir avoir plus de visibilité sur ce qui est rendu </li></ul>
    56. 63. Comment ? $decorator = $element ->getDecorator( 'SimpleInput' ); echo $decorator ->render( '' ); Facile : echo $element ->getDecorator( 'SimpleInput' ) ->render( '' ); Plus facile : echo $element ->renderSimpleInput(); Encore plus facile :
    57. 64. <div class = &quot;element&quot; > <?php echo $form ->title->renderLabel() . $form ->title->renderViewHelper(); if ( $form ->title->hasErrors()) { echo '<p class=&quot;error&quot;>There were errors validating</p>' ; } ?> </div>
    58. 65. <div class = &quot;element&quot; > <?php echo $form ->dateOfBirth->renderLabel() ?> <?php echo $this ->formText( 'dateOfBirth[day]' , '' , array ( 'size' => 2, 'maxlength' => 2)) ?> / <?php echo $this ->formText( 'dateOfBirth[month]' , '' , array ( 'size' => 2, 'maxlength' => 2)) ?> / <?php echo $this ->formText( 'dateOfBirth[year]' , '' , array ( 'size' => 4, 'maxlength' => 4)) ?> </div>
    59. 66. Créer des éléments composés
    60. 67. Exemple : une date d'anniversaire <ul><li>Stocke la date en interne sous la forme d'un timestamp
    61. 68. Pour une question de confort, on demande à l'utilisateur de renseigner indépendamment le jour, le mois et l'année
    62. 69. … mais typiquement ces décorateurs représentent un seul élément de formulaire </li></ul>
    63. 70. Création de l'élément <ul><li>Accepte les valeurs de type entier
    64. 71. Accepte les valeurs sous la forme de tableau avec des clés jour/mois/année
    65. 72. Accepte les valeurs sous forme de chaîne
    66. 73. Possède des accesseurs pour chaque segment de la date (jours, mois et année) </li></ul>
    67. 74. class My_Form_Element_Date extends Zend_Form_Element_Xhtml { public function setValue( $value ) { if ( is_int ( $value )) { $this ->_value = new DateTime( date ( 'Y-m-d' , $value )); } elseif ( is_string ( $value )) { $this ->_value = new DateTime( $value ); } elseif ( is_array ( $value )) { $this ->_value = new DateTime(); $this ->_value->setDate( $value [ 'year' ], $value [ 'month' ], $value [ 'day' ] ); } else { throw new Exception( 'Invalid date value provided' ); } return $this ; } public function getDay() { } public function getMonth() { } public function getYear() { } }
    68. 75. Création d'un décorateur <ul><li>Ajoute un proxy duck-type vers l'élément
    69. 76. S'assure de la présence d'une vue
    70. 77. Récupère les méta-données à partir de l'élément
    71. 78. Fournit les méta-données aux aides de vues existantes pour créer le balisage </li></ul>
    72. 79. class My_Form_Decorator_Date extends Zend_Form_Decorator_Abstract { public function render( $content ) { $element = $this ->getElement(); if (! $element instanceof My_Form_Element_Date) { return $content ; } $view = $element ->getView(); if (! $view instanceof Zend_View_Interface) { return $content ; } $day = $element ->getDay(); $month = $element ->getMonth(); $year = $element ->getYear(); $name = $element ->getFullyQualifiedName(); // ... } }
    73. 80. class My_Form_Decorator_Date extends Zend_Form_Decorator_Abstract { public function render( $content ) { // ... $params = array ( 'size' => 2, 'maxlength' => 2); $yearParams = array ( 'size' => 4, 'maxlength' => 4); $markup = $view ->formText( $name . '[day]' , $day , $params ) . ' / ' . $view ->formText( $name . '[month]' , $month , $params ) . ' / ' . $view ->formText( $name . '[year]' , $year , $yearParams ); // Placement et valeur de retour ... } }
    74. 81. Mettre à jour l'élément <ul><li>S'assure que l'élément sait où il peut trouver ces décorateurs
    75. 82. Spécifie les décorateurs par défaut </li></ul>
    76. 83. class My_Form_Element_Date extends Zend_Form_Element_Xhtml { // ... public function __construct( $spec , $options = null ) { $this ->addPrefixPath( 'My_Form_Decorator' , 'My/Form/Decorator' , 'decorator' ); parent::__construct( $spec , $options ); } // ... }
    77. 84. class My_Form_Element_Date extends Zend_Form_Element_Xhtml { // ... public function loadDefaultDecorators() { if ( $this ->loadDefaultDecoratorsIsDisabled()) { return ; } if (empty( $decorators = $this ->getDecorators() )) { $this ->addDecorator( 'Date' ) // ... ; } } // ... }
    78. 85. Maintenant on l'utilise ! $d = new My_Form_Element_Date( 'dateOfBirth' ); $d ->setLabel( 'Date of Birth: ' ) ->setView(new Zend_View()); // Ceux-ci sont équivalents : $d ->setValue( '20 April 2009' ); $d ->setValue( array ( 'year' => '2009' , 'month' => '04' , 'day' => '20' ));
    79. 86. <dt id = &quot;dateOfBirth-label&quot; ><label for = &quot;dateOfBirth&quot; class = &quot;optional&quot; > Date of Birth: </label></dt> <dd id = &quot;dateOfBirth-element&quot; > <input type = &quot;text&quot; name = &quot;dateOfBirth[day]&quot; id = &quot;dateOfBirth-day&quot; value = &quot;20&quot; size = &quot;2&quot; maxlength = &quot;2&quot; > / <input type = &quot;text&quot; name = &quot;dateOfBirth[month]&quot; id = &quot;dateOfBirth-month&quot; value = &quot;4&quot; size = &quot;2&quot; maxlength = &quot;2&quot; > / <input type = &quot;text&quot; name = &quot;dateOfBirth[year]&quot; id = &quot;dateOfBirth-year&quot; value = &quot;2009&quot; size = &quot;4&quot; maxlength = &quot;4&quot; > </dd>
    80. 87. Conclusion
    81. 88. Qu'avons-nous couvert ? <ul><li>Ce que sont les décorateurs, et par quelle approche Zend_Form les utilise
    82. 89. Comment créer vos propres décorateurs
    83. 90. Comment superposer les décorateurs
    84. 91. Comment rendre individuellement les décorateurs
    85. 92. Comment créer des éléments constitués d'entrées composées </li></ul>
    86. 93. Pour aller plus loin <ul><li>Zend Framework Workshop (DPC 2010) : http://www.slideshare.net/weierophinney/zend-framework-workshop-4480868
    87. 94. Créer un visuel personnalisé en utilisant Zend_Form_Decorator : http://framework.zend.com/manual/fr/zend.form.decorators.html </li></ul>
    88. 95. Merci ! <ul><li>Zend Framework: http://framework.zend.com/
    89. 96. Twitter/IRC nick: mikaelkael
    90. 97. Mon blog: http://mikaelkael.fr/ </li></ul>

    ×