• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Tirer parti des décorateurs de Zend_Form
 

Tirer parti des décorateurs de Zend_Form

on

  • 2,862 views

Comment tirer parti des décorateurs de Zend_Form ?

Comment tirer parti des décorateurs de Zend_Form ?

Statistics

Views

Total Views
2,862
Views on SlideShare
2,862
Embed Views
0

Actions

Likes
1
Downloads
40
Comments
1

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as OpenOffice

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel

11 of 1 previous next

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • 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 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 Tirer parti des décorateurs de Zend_Form Presentation Transcript

  • Tirer parti des décorateurs Zend_Form Mickaël Perraud Contributeur Zend Framework Responsable documentation française
  • Qu'est-ce qu'un décorateur ?
  • Dans Zend_Form …
    • Combinaison des patrons de conception Decorator et Strategy
  • 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)”
  •  
  • Deux techniques principales
    • Définir une interface à laquelle tous les objets concrets et les décorateurs adhèrent
      • Souvent, les décorateurs implémenteront une interface additionnelle
    • Duck-typing
      • Utilise une méthode/propriété dans le décorateur pour proxier vers l'objet décoré
  • interface Window { public function isOpen(); public function open(); public function close (); }
  • 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; } } }
  • 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 (); } }
  • 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 (); } }
  • 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”
  •  
  • class Window { public $strategy ; public function open() { $this -> $strategy ->open(); } } interface OpenStrategy { public function open(); }
  • class RaiseStrategy implements OpenStrategy { public function open() { } } class LeverStrategy implements OpenStrategy { public function open() { } }
  • Créer votre premier décorateur
  • L'interface
    • Zend_Form_Decorator_Interface
      • __construct($options = null);
      • setElement($element);
      • getElement();
      • setOptions(array $options);
      • setConfig(Zend_Config $config);
      • setOption($key, $value);
      • getOption($key);
      • getOptions();
      • removeOption($key);
      • clearOptions();
      • render($content);
  • L'interface
    • Ce qu'il est nécessaire de retenir :
      • render($content);
  • Un décorateur simple pour un champ texte
    • Va rendre un label
    • Va rendre le champ texte
    • 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é
  • 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 ; } }
  • $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' ), ));
  • <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; />
  • Où en sommes-nous ?
    • Ré-utilisation
    • Échappement automatique
    • Sélection des attributs que vous souhaitez propager et afficher au moment de l'exécution
  • Superposer les décorateurs
  • L'argument $content
    • Utilisé pour agréger les sorties des multiples décorateurs
    • 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
    • L'affichage final est le produit de tous les décorateurs
  • Décorateurs par défaut
    • ViewHelper – utilise une aide de vue pour effectuer le rendu de l'élément de formulaire lui-même
    • Description – effectue le rendu de la description de l'élément, si besoin
    • Errors – effectue le rendu des erreurs de validation, si besoin
    • HtmlTag – enveloppe tout le contenu généré dans une balise
    • Label – utilise une aide de vue pour effectuer le rendu du libellé de l'élément ; l'ajoute devant le contenu agrégé
  •  
  •  
  •  
  •  
  •  
  • Comment superposer les décorateurs ?
    • Utilisez l'option “placement”
    • Choississez de :
      • Ajouter après le contenu fourni (APPEND)
      • Ajouter avant le contenu fourni (PREPEND)
      • Remplacer le contenu fourni (REPLACE)
    • Utiliser un placement par défaut sain ; les options vous permettront de le surcharger
  • 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 ; } }
  • 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 ; } }
  • $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 ; }
  • $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' , ), ));
  • $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' )), ), ));
  • Rendre les décorateurs individuellement
  • Pourquoi ?
    • Simplement parfois, parce que le balisage est trop complexe pour être assemblé par superposition
    • Vos designers peuvent vouloir avoir plus de visibilité sur ce qui est rendu
  • Comment ? $decorator = $element ->getDecorator( 'SimpleInput' ); echo $decorator ->render( '' ); Facile : echo $element ->getDecorator( 'SimpleInput' ) ->render( '' ); Plus facile : echo $element ->renderSimpleInput(); Encore plus facile :
  • <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>
  • <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>
  • Créer des éléments composés
  • Exemple : une date d'anniversaire
    • Stocke la date en interne sous la forme d'un timestamp
    • Pour une question de confort, on demande à l'utilisateur de renseigner indépendamment le jour, le mois et l'année
    • … mais typiquement ces décorateurs représentent un seul élément de formulaire
  • Création de l'élément
    • Accepte les valeurs de type entier
    • Accepte les valeurs sous la forme de tableau avec des clés jour/mois/année
    • Accepte les valeurs sous forme de chaîne
    • Possède des accesseurs pour chaque segment de la date (jours, mois et année)
  • 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() { } }
  • Création d'un décorateur
    • Ajoute un proxy duck-type vers l'élément
    • S'assure de la présence d'une vue
    • Récupère les méta-données à partir de l'élément
    • Fournit les méta-données aux aides de vues existantes pour créer le balisage
  • 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(); // ... } }
  • 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 ... } }
  • Mettre à jour l'élément
    • S'assure que l'élément sait où il peut trouver ces décorateurs
    • Spécifie les décorateurs par défaut
  • 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 ); } // ... }
  • 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' ) // ... ; } } // ... }
  • 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' ));
  • <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>
  • Conclusion
  • Qu'avons-nous couvert ?
    • Ce que sont les décorateurs, et par quelle approche Zend_Form les utilise
    • Comment créer vos propres décorateurs
    • Comment superposer les décorateurs
    • Comment rendre individuellement les décorateurs
    • Comment créer des éléments constitués d'entrées composées
  • Pour aller plus loin
    • Zend Framework Workshop (DPC 2010) : http://www.slideshare.net/weierophinney/zend-framework-workshop-4480868
    • Créer un visuel personnalisé en utilisant Zend_Form_Decorator : http://framework.zend.com/manual/fr/zend.form.decorators.html
  • Merci !
    • Zend Framework: http://framework.zend.com/
    • Twitter/IRC nick: mikaelkael
    • Mon blog: http://mikaelkael.fr/