Leveraging Zend_Form Decorators Matthew Weier O'Phinney Project Lead Zend Framework
What is a decorator?
In Zend_Form … <ul><li>Combination  Decorator and  Strategy  pattern </li></ul>
Decorator Pattern “ In object-oriented programming, the decorator pattern is a design pattern that allows new/additional b...
 
Two principal techniques <ul><li>Defined interface that all concrete objects and decorators adhere to </li><ul><li>Often, ...
interface Window { public   function   isOpen(); public   function   open(); public   function close (); }
class  StandardWindow implements Window { protected   $_open   =   false; public   function   isOpen() { return   $this ->...
class  LockedWindow implements Window { protected   $_window ; public   function   __construct(Window   $window ){ $this -...
class  LockedWindow { protected  $_window ; public   function   __construct(Window   $window ) { $this ->_window   =   $wi...
Strategy Pattern “ A particular software design pattern, whereby algorithms can be selected at runtime.” –  Wikipedia, “St...
 
class  Window { public   $strategy ; public   function   open() { $this -> $strategy ->open(); } } interface OpenStrategy ...
class  RaiseStrategy implements OpenStrategy { public   function   open() { } } class   LeverStrategy implements OpenStrat...
Creating your first decorator
The Interface <ul><li>Zend_Form_Decorator_Interface </li><ul><li>__construct($options = null);
setElement($element);
getElement();
setOptions(array $options);
setConfig(Zend_Config $config);
setOption($key, $value);
getOption($key);
getOptions();
removeOption($key);
clearOptions();
render($content); </li></ul></ul>
The Interface <ul><li>What you really need to know about: </li><ul><li>render($content); </li></ul></ul>
A simple decorator for text input <ul><li>Will render a label
Will render a text input
Will grab metadata from the element and use it to define the generated output </li></ul>
class   My_Decorator_SimpleInput extends   Zend_Form_Decorator_Abstract { protected   $_format   =   '<label for=&quot;%s&...
$decorator  = new My_Decorator_SimpleInput(); $element   =   new   Zend_Form_Element( 'foo' ,   array ( 'label'   =>   'Fo...
<label   for = &quot;bar[foo]&quot; > Foo </label> <input   id = &quot;bar-foo&quot;   name = &quot;bar[foo]&quot;   type ...
What's the point? <ul><li>Re-usable
Automatic escaping
Select the attributes you want to propagate and output at run-time </li></ul>
Layering decorators
The $content argument <ul><li>Used to  aggregate  output from multiple decorators
Individual decorators can specialize in creating output specific to pertinent element metadata
Final output is the product of all decorators </li></ul>
Default decorators <ul><li>ViewHelper  – uses a view helper to render the form input itself
Description  – renders element desc, if any
Errors  – renders validation errors, if any
HtmlTag  – wrap all generated content in a tag
Label  – uses a view helper to render the element label; prepends to aggregate content </li></ul>
 
 
 
 
 
How to layer <ul><li>Make use of the “placement” option
Choose whether to: </li><ul><li>Prepend the content provided
Append the content provided
Replace the content provided </li></ul><li>Have sane default placement; options allow passing in on override </li></ul>
class  My_Decorator_SimpleInput    extends  Zend_Form_Decorator_Abstract { protected   $_format   =   '<input id=&quot;%s&...
class  My_Decorator_SimpleLabel  extends  Zend_Form_Decorator_Abstract { protected   $_format   =   '<label for=&quot;%s&q...
Upcoming SlideShare
Loading in …5
×

Leveraging Zend_Form Decorators

7,444 views
7,359 views

Published on

Published in: Technology
0 Comments
10 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
7,444
On SlideShare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
0
Likes
10
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 &lt;dd&gt; 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

    1. 1. Leveraging Zend_Form Decorators Matthew Weier O'Phinney Project Lead Zend Framework
    2. 2. What is a decorator?
    3. 3. In Zend_Form … <ul><li>Combination Decorator and Strategy pattern </li></ul>
    4. 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 <ul><li>Defined interface that all concrete objects and decorators adhere to </li><ul><li>Often, decorators will implement an additional interface </li></ul><li>Duck-typing </li><ul><li>Use method/property overloading in the decorator to proxy to the decorated object </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. Strategy Pattern “ A particular software design pattern, whereby algorithms can be selected at runtime.” – 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. Creating your first decorator
    14. 16. The 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. The Interface <ul><li>What you really need to know about: </li><ul><li>render($content); </li></ul></ul>
    26. 28. A simple decorator for text input <ul><li>Will render a label
    27. 29. Will render a text input
    28. 30. Will grab metadata from the element and use it to define the generated output </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. What's the point? <ul><li>Re-usable
    33. 35. Automatic escaping
    34. 36. Select the attributes you want to propagate and output at run-time </li></ul>
    35. 37. Layering decorators
    36. 38. The $content argument <ul><li>Used to aggregate output from multiple decorators
    37. 39. Individual decorators can specialize in creating output specific to pertinent element metadata
    38. 40. Final output is the product of all decorators </li></ul>
    39. 41. Default decorators <ul><li>ViewHelper – uses a view helper to render the form input itself
    40. 42. Description – renders element desc, if any
    41. 43. Errors – renders validation errors, if any
    42. 44. HtmlTag – wrap all generated content in a tag
    43. 45. Label – uses a view helper to render the element label; prepends to aggregate content </li></ul>
    44. 51. How to layer <ul><li>Make use of the “placement” option
    45. 52. Choose whether to: </li><ul><li>Prepend the content provided
    46. 53. Append the content provided
    47. 54. Replace the content provided </li></ul><li>Have sane default placement; options allow passing in on override </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 ); // determine placement here... 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 ); // handle placement here... 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 ; }
    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. Rendering decorators individually
    54. 61. Why? <ul><li>Quite simply, sometimes markup is too complex to assemble via layering
    55. 62. Your designers may want to have more visibility into what is rendered </li></ul>
    56. 63. How? $decorator = $element ->getDecorator( 'SimpleInput' ); echo $decorator ->render( '' ); Easy: echo $element ->getDecorator( 'SimpleInput' ) ->render( '' ); Easier: echo $element ->renderSimpleInput(); Easiest:
    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. Creating composite elements
    60. 67. Example: Date of Birth <ul><li>Store the date internally as a timestamp
    61. 68. For usability, ask user to enter year, month, and day discretely
    62. 69. … b ut typical decorators represent an element with a single form input </li></ul>
    63. 70. Create an element <ul><li>Accept value as an integer
    64. 71. Accept value as an array with year/month/day segments
    65. 72. Accept value as a string
    66. 73. Accessors for each date segment (year, month, and day) </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. Create a decorator <ul><li>Duck-type check the element
    69. 76. Ensure we have a view
    70. 77. Grab metadata from the element
    71. 78. Pass metadata to existing view helpers to create markup </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 and return ... } }
    74. 81. Update the element <ul><li>Ensure the element knows where to find its decorators
    75. 82. Specify the default decorators </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. 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' ));
    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. Review
    81. 88. What we've covered <ul><li>What decorators are, and the approach Zend_Form uses
    82. 89. How to create your own decorators
    83. 90. How to layer decorators
    84. 91. How to render individual decorators selectively
    85. 92. How to create elements consisting of composite input </li></ul>
    86. 93. Thank you! <ul><li>Zend Framework: http://framework.zend.com/
    87. 94. My twitter/IRC nick: weierophinney
    88. 95. My blog: http://weierophinney.net/matthew/ </li></ul>

    ×