Leveraging Zend_Form Decorators
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Leveraging Zend_Form Decorators

on

  • 7,260 views

 

Statistics

Views

Total Views
7,260
Views on SlideShare
7,202
Embed Views
58

Actions

Likes
10
Downloads
0
Comments
0

3 Embeds 58

http://www.slideshare.net 53
http://coderwall.com 4
http://akrabat.com 1

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
  • 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

Leveraging Zend_Form Decorators Presentation Transcript

  • 1. Leveraging Zend_Form Decorators Matthew Weier O'Phinney Project Lead Zend Framework
  • 2. What is a decorator?
  • 3. In Zend_Form …
    • Combination Decorator and Strategy pattern
  • 4. Decorator Pattern “ In object-oriented programming, the decorator pattern is a design pattern that allows new/additional behaviour to be added to an existing object dynamically.” – Wikipedia, “Decorator_pattern”
  • 5.  
  • 6. Two principal techniques
    • Defined interface that all concrete objects and decorators adhere to
      • Often, decorators will implement an additional interface
    • Duck-typing
      • Use method/property overloading in the decorator to proxy to the decorated object
  • 7. interface Window { public function isOpen(); public function open(); public function close (); }
  • 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; } } }
  • 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 (); } }
  • 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 (); } }
  • 11. Strategy Pattern “ A particular software design pattern, whereby algorithms can be selected at runtime.” – Wikipedia, “Strategy_pattern”
  • 12.  
  • 13. class Window { public $strategy ; public function open() { $this -> $strategy ->open(); } } interface OpenStrategy { public function open(); }
  • 14. class RaiseStrategy implements OpenStrategy { public function open() { } } class LeverStrategy implements OpenStrategy { public function open() { } }
  • 15. Creating your first decorator
  • 16. The Interface
    • Zend_Form_Decorator_Interface
      • __construct($options = null);
      • 17. setElement($element);
      • 18. getElement();
      • 19. setOptions(array $options);
      • 20. setConfig(Zend_Config $config);
      • 21. setOption($key, $value);
      • 22. getOption($key);
      • 23. getOptions();
      • 24. removeOption($key);
      • 25. clearOptions();
      • 26. render($content);
  • 27. The Interface
    • What you really need to know about:
      • render($content);
  • 28. A simple decorator for text input
    • Will render a label
    • 29. Will render a text input
    • 30. Will grab metadata from the element and use it to define the generated output
  • 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 ; } }
  • 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' ), ));
  • 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; />
  • 34. What's the point?
    • Re-usable
    • 35. Automatic escaping
    • 36. Select the attributes you want to propagate and output at run-time
  • 37. Layering decorators
  • 38. The $content argument
    • Used to aggregate output from multiple decorators
    • 39. Individual decorators can specialize in creating output specific to pertinent element metadata
    • 40. Final output is the product of all decorators
  • 41. Default decorators
    • ViewHelper – uses a view helper to render the form input itself
    • 42. Description – renders element desc, if any
    • 43. Errors – renders validation errors, if any
    • 44. HtmlTag – wrap all generated content in a tag
    • 45. Label – uses a view helper to render the element label; prepends to aggregate content
  • 46.  
  • 47.  
  • 48.  
  • 49.  
  • 50.  
  • 51. How to layer
    • Make use of the “placement” option
    • 52. Choose whether to:
      • Prepend the content provided
      • 53. Append the content provided
      • 54. Replace the content provided
    • Have sane default placement; options allow passing in on override
  • 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 ); // determine placement here... return $markup ; } }
  • 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 ); // handle placement here... return $markup ; } }
  • 57. $placement = $this ->getPlacement(); $separator = $this ->getSeparator(); switch ( $placement ) { case self::PREPEND: return $markup . $separator . $content ; case self::APPEND: default: return $content . $separator . $markup ; }
  • 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' , ), ));
  • 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' )), ), ));
  • 60. Rendering decorators individually
  • 61. Why?
    • Quite simply, sometimes markup is too complex to assemble via layering
    • 62. Your designers may want to have more visibility into what is rendered
  • 63. How? $decorator = $element ->getDecorator( 'SimpleInput' ); echo $decorator ->render( '' ); Easy: echo $element ->getDecorator( 'SimpleInput' ) ->render( '' ); Easier: echo $element ->renderSimpleInput(); Easiest:
  • 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>
  • 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>
  • 66. Creating composite elements
  • 67. Example: Date of Birth
    • Store the date internally as a timestamp
    • 68. For usability, ask user to enter year, month, and day discretely
    • 69. … b ut typical decorators represent an element with a single form input
  • 70. Create an element
    • Accept value as an integer
    • 71. Accept value as an array with year/month/day segments
    • 72. Accept value as a string
    • 73. Accessors for each date segment (year, month, and day)
  • 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() { } }
  • 75. Create a decorator
    • Duck-type check the element
    • 76. Ensure we have a view
    • 77. Grab metadata from the element
    • 78. Pass metadata to existing view helpers to create markup
  • 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(); // ... } }
  • 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 and return ... } }
  • 81. Update the element
    • Ensure the element knows where to find its decorators
    • 82. Specify the default decorators
  • 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 ); } // ... }
  • 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' ) // ... ; } } // ... }
  • 85. Use it! $d = new My_Form_Element_Date( 'dateOfBirth' ); $d ->setLabel( 'Date of Birth: ' ) ->setView(new Zend_View()); // These are equivalent: $d ->setValue( '20 April 2009' ); $d ->setValue( array ( 'year' => '2009' , 'month' => '04' , 'day' => '20' ));
  • 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>
  • 87. Review
  • 88. What we've covered
    • What decorators are, and the approach Zend_Form uses
    • 89. How to create your own decorators
    • 90. How to layer decorators
    • 91. How to render individual decorators selectively
    • 92. How to create elements consisting of composite input
  • 93. Thank you!
    • Zend Framework: http://framework.zend.com/
    • 94. My twitter/IRC nick: weierophinney
    • 95. My blog: http://weierophinney.net/matthew/