RESTful Applications with Zend Framework 11 June 2010, RAI, Amsterdam
premise How do you  detect  and  respond   to  REST  requests  in your Zend Framework application?
roadmap <ul><li>Good Modelling
Introduction to  Zend_Rest_Route
HTTP Fundamentals
Overview of context switching in ZF
Getting content-type-specific parameters </li></ul>
Good Modelling
create entities that implement __toString()  and  toArray() <ul><li>Makes serializing simple
Makes passing to data access layer simple
Simplifies caching (particularly when coupled with a  fromArray()  method) </li></ul>
serialization class   Foo { public   function   __toString() { return   'foo' ; } public   function   toArray() { $array  ...
caching if   (! $data   =   $cache ->load( 'foo_collection' )) { $data   =   $this ->fooObjects->toArray(); $cache ->save(...
return collections as paginators <ul><li>Consumers do not need to be aware of data format
Consumers can provide offset and limit
Consumers can decide how to cast </li><ul><li>Zend_Paginator  implements  IteratorAggregate ,  toJson() </li></ul><li>Most...
Zend_Paginator basics <ul><li>setCurrentPageNumber($page)
setItemCountPerPage($int)
setPageRange($int)  (number of pages to show in paginator)
Constructor accepts an adapter capable of generating a  count()  and returning items based on an offset and item count </l...
repository returning paginator public function  fetchAll() { $select   =   $this ->getDbTable()->select() ->where( 'disabl...
paginator use in view script $this ->users->setItemCountPerPage(   $this ->numItems) ->setCurrentPageNumber(   $this ->pag...
going generic <ul><li>Alternately, define a “collection” type </li><ul><li>e.g., to accept a  MongoCursor
Implement  Countable,  and  Iterator ; optionally also some serialization interfaces
Define methods like  skip()  and  limit()  (or  page() ),   and  sort()  and  setItemClass() </li></ul></ul>
mapper returning generic collection public function  find(   array   $query ,  array   $fields  =  array () ) { $query   =...
collection used in view script $this ->entries->start( $this ->offset) ->limit( $this ->numItems); echo   $this ->json(   ...
service layers Define your  application's API   and implement  your application  in the  service layer .
example namespace  BlogService; class   Entries { public function  create(array  $data ) {} public function   fetch( $perm...
what's found in Service Layers? <ul><li>Resource marshalling and Dependency Injection
Application-specific logic: </li><ul><li>Authentication and Authorization (ACLs)
Input filtering/data validation
Search indexing
Caching
etc. </li></ul></ul>
Zend_Rest_Route basics
REST uses HTTP verbs <ul><li>GET  with no identifier: list
GET  with identifier: resource
POST : create
PUT  (with identifier): update
DELETE  (with identifier): delete </li></ul>
Zend_Rest_Route Resource HTTP Method Controller Action
defining all routes RESTful // Enable for all controllers $front   =   Zend_Controller_Front::getInstance(); $restRoute   ...
defining select modules as RESTful // Enable for blog module only $front   =     Zend_Controller_Front::getInstance(); $re...
Defining select controllers as RESTful // Enable for specific controllers only $front   =     Zend_Controller_Front::getIn...
sample REST controller class  EntryController    extends  Zend_Rest_Controller { public function  index Action() { } publi...
retrieving the identifier if  (! $id  =  $this ->getUserParam( 'id' , false)) { // redirect, error, etc. }
HTTP Fundamentals
request headers <ul><li>Content-Type   (what it's providing)
Accept   (what it expects in response) </li></ul>
Upcoming SlideShare
Loading in...5
×

Building RESTful Zend Framework Applications

17,201

Published on

Short tutorial covering several techniques for automating HTTP header detection and sending appropriate content.

Published in: Technology
2 Comments
17 Likes
Statistics
Notes
No Downloads
Views
Total Views
17,201
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
368
Comments
2
Likes
17
Embeds 0
No embeds

No notes for slide
  • Been working on a new design for 6 months. Host of new features that old did not have. Look and feel have been reinvented New concepts in place like member directory, online calendar, online map – both using tech from Google Info more easily updated Special pags - Committee pages, ministry pages, youth pages, missions pages Home page that gives quick access to current news, information, and links Archives section to store video, audio, documents, images that can be searched Members area for sensitive information Possible Email Newsletter &amp; Photo slideshows
  • Building RESTful Zend Framework Applications

    1. 1. RESTful Applications with Zend Framework 11 June 2010, RAI, Amsterdam
    2. 2. premise How do you detect and respond to REST requests in your Zend Framework application?
    3. 3. roadmap <ul><li>Good Modelling
    4. 4. Introduction to Zend_Rest_Route
    5. 5. HTTP Fundamentals
    6. 6. Overview of context switching in ZF
    7. 7. Getting content-type-specific parameters </li></ul>
    8. 8. Good Modelling
    9. 9. create entities that implement __toString() and toArray() <ul><li>Makes serializing simple
    10. 10. Makes passing to data access layer simple
    11. 11. Simplifies caching (particularly when coupled with a fromArray() method) </li></ul>
    12. 12. serialization class Foo { public function __toString() { return 'foo' ; } public function toArray() { $array = array (); foreach ( $this ->_nodes as $node ) { $array [] = $node ->name; } return $array ; } }
    13. 13. caching if (! $data = $cache ->load( 'foo_collection' )) { $data = $this ->fooObjects->toArray(); $cache ->save( $data , 'foo-collections' ); } if (! $data = $cache ->load( 'foo-item' )) { $data = ( string ) $this ->foo; $cache ->save( $data , 'foo-item' ); }
    14. 14. return collections as paginators <ul><li>Consumers do not need to be aware of data format
    15. 15. Consumers can provide offset and limit
    16. 16. Consumers can decide how to cast </li><ul><li>Zend_Paginator implements IteratorAggregate , toJson() </li></ul><li>Most paginator adapters will only operate once results are requested </li></ul>
    17. 17. Zend_Paginator basics <ul><li>setCurrentPageNumber($page)
    18. 18. setItemCountPerPage($int)
    19. 19. setPageRange($int) (number of pages to show in paginator)
    20. 20. Constructor accepts an adapter capable of generating a count() and returning items based on an offset and item count </li></ul>
    21. 21. repository returning paginator public function fetchAll() { $select = $this ->getDbTable()->select() ->where( 'disabled = ?' , 0); $paginator = new Zend_Paginator( new Zend_Paginator_Adapter_DbSelect( $select ) ); return $paginator ; }
    22. 22. paginator use in view script $this ->users->setItemCountPerPage( $this ->numItems) ->setCurrentPageNumber( $this ->page); echo $this ->users->toJson();
    23. 23. going generic <ul><li>Alternately, define a “collection” type </li><ul><li>e.g., to accept a MongoCursor
    24. 24. Implement Countable, and Iterator ; optionally also some serialization interfaces
    25. 25. Define methods like skip() and limit() (or page() ), and sort() and setItemClass() </li></ul></ul>
    26. 26. mapper returning generic collection public function find( array $query , array $fields = array () ) { $query = $this ->_formatQuery( $query ); // mongo collection: $cursor = $this ->getCollection() ->find( $query , $fields ); // paginator: $collection = new Collection( $cursor ); $collection ->setItemClass( 'Entry' ); return $collection ; }
    27. 27. collection used in view script $this ->entries->start( $this ->offset) ->limit( $this ->numItems); echo $this ->json( $this ->entries->toArray() );
    28. 28. service layers Define your application's API and implement your application in the service layer .
    29. 29. example namespace BlogService; class Entries { public function create(array $data ) {} public function fetch( $permalink ) {} public function fetchCommentCount( $permalink ) {} public function fetchComments( $permalink ) {} public function fetchTrackbacks( $permalink ) {} public function addComment( $permalink , array $comment ) {} public function addTrackback( $permalink , array $comment ) {} public function fetchTagCloud() {} }
    30. 30. what's found in Service Layers? <ul><li>Resource marshalling and Dependency Injection
    31. 31. Application-specific logic: </li><ul><li>Authentication and Authorization (ACLs)
    32. 32. Input filtering/data validation
    33. 33. Search indexing
    34. 34. Caching
    35. 35. etc. </li></ul></ul>
    36. 36. Zend_Rest_Route basics
    37. 37. REST uses HTTP verbs <ul><li>GET with no identifier: list
    38. 38. GET with identifier: resource
    39. 39. POST : create
    40. 40. PUT (with identifier): update
    41. 41. DELETE (with identifier): delete </li></ul>
    42. 42. Zend_Rest_Route Resource HTTP Method Controller Action
    43. 43. defining all routes RESTful // Enable for all controllers $front = Zend_Controller_Front::getInstance(); $restRoute = new Zend_Rest_Route( $front ); $front ->getRouter()->addRoute( 'default' , $restRoute );
    44. 44. defining select modules as RESTful // Enable for blog module only $front = Zend_Controller_Front::getInstance(); $restRoute = new Zend_Rest_Route( $front , array (), array ( 'blog' ) ); $front ->getRouter()->addRoute( 'rest' , $restRoute );
    45. 45. Defining select controllers as RESTful // Enable for specific controllers only $front = Zend_Controller_Front::getInstance(); $restRoute = new Zend_Rest_Route( $front , array (), array ( 'blog' => array ( 'comment' , 'trackback' , ), ) ); $front ->getRouter()->addRoute( 'rest' , $restRoute );
    46. 46. sample REST controller class EntryController extends Zend_Rest_Controller { public function index Action() { } public function postAction() { } public function getAction() { } public function putAction() { } public function deleteAction() { } }
    47. 47. retrieving the identifier if (! $id = $this ->getUserParam( 'id' , false)) { // redirect, error, etc. }
    48. 48. HTTP Fundamentals
    49. 49. request headers <ul><li>Content-Type (what it's providing)
    50. 50. Accept (what it expects in response) </li></ul>
    51. 51. status codes <ul><li>201 (Created)
    52. 52. 400 (Bad Request) (Failed validations)
    53. 53. 401 (Unauthorized)
    54. 54. 204 (No Content) (useful with DELETE)
    55. 55. 500 (Application Error) </li></ul>
    56. 56. response headers <ul><li>Content-Type (what you're returning)
    57. 57. Vary (what and when to cache) </li></ul>
    58. 58. Context Switching
    59. 59. context switching is … <ul><li>Inspect HTTP request headers, and/or the request URI, and vary the response
    60. 60. In ZF, using the ContextSwitch and/or AjaxContext action helper
    61. 61. They need some configuration … </li></ul>
    62. 62. basics <ul><li>Map actions to allowed contexts
    63. 63. A “format” request parameter indicates the detected context
    64. 64. When a context is detected, an additional suffix is added to the view script
    65. 65. Optionally, send some additional headers </li><ul><li>Push the response object into the view to facilitate </li></ul></ul>
    66. 66. mapping contexts to actions class Blog_CommentController extends Zend_Controller_Action { public function init() { $contextSwitch = $this ->_helper->getHelper( 'contextSwitch' ); $contextSwitch ->addActionContext( 'post' , 'xml' ) ->initContext(); } }
    67. 67. add view scripts per-context blog |-- views | |-- scripts | | |-- comment | | | |-- post.phtml | | | `-- post.xml.phtml
    68. 68. injecting the response and request into the view use Zend_Controller_Action_HelperBroker as HelperBroker; class InjectRequestResponse extends Zend_Controller_Plugin_Abstract { public function dispatchLoopStartup( Zend_Controller_Request_Abstract $request ) { $vr = HelperBroker::getStaticHelper( 'ViewRenderer' ); $vr ->view->assign( array ( 'request' => $request , 'response' => $this ->getResponse(), )); } }
    69. 69. “html” view <?php $comment = $this ->entryUrl . '#' . $this ->comment->getId(); $response = $this ->response; $response ->setHttpResponseCode(201) ->setHeader( 'Location' , $comment );
    70. 70. “xml” view <?php $comment = $this ->entryUrl . '#' . $this ->comment->getId(); ?> <?xml version= &quot;1.0&quot; encoding= &quot;utf-8&quot; ?> <response> <status>success</status> <entry> <url><?php echo $comment ?></url> </entry> </response>
    71. 71. Getting the context <ul><li>Two common options: </li><ul><li>Via the URI (often, using a suffix; e.g., “.xml”, “.json”)
    72. 72. Via HTTP headers (the Accept header) </li></ul></ul>
    73. 73. via a chained route use Zend_Controller_Router_Route as StandardRoute, Zend_Controller_Router_Route_Regex as RegexRoute; $format = new RegexRoute( '(?<format>xml|json)' ); $entry = new StandardRoute( 'blog/entry/:id' , array ( 'module' => 'blog' , 'controller' => 'entry' , 'action' => 'view' , )); $entry ->chain( $format , '.' ); $router ->addRoute( 'entry' , $entry );
    74. 74. via Accept detection class AcceptHandler extends Zend_Controller_Plugin_Abstract { public function dispatchLoopStartup( Zend_Controller_Request_Abstract $request ) { $this ->getResponse()->setHeader( 'Vary' , 'Accept' ); $header = $request ->getHeader( 'Accept' ); switch (true) { case (strstr( $header , 'application/json' )): $request ->setParam( 'format' , 'json' ); break ; case (strstr( $header , 'application/xml' ) && (!strstr( $header , 'html' ))): $request ->setParam( 'format' , 'xml' ); break ; default : break ; } } }
    75. 75. Content-Type specific params
    76. 76. the problem <ul><li>Non form-encoded Content-Types typically mean parameters are passed in the raw request body
    77. 77. Additionally, they likely need to be decoded and serialized to a PHP array
    78. 78. Use an action helper to automate the process </li></ul>
    79. 79. Content-Type detection class Params extends Zend_Controller_Action_Helper_Abstract { public function init() { $request = $this ->getRequest(); $contentType = $request ->getHeader( 'Content-Type' ); $rawBody = $request ->getRawBody(); if (! $rawBody ) { return ; }
    80. 80. Content-Type detection (cont.) switch (true) { case (strstr( $contentType , 'application/json' )): $this ->setBodyParams( Zend_Json::decode( $rawBody )); break ; case (strstr( $contentType , 'application/xml' )): $config = new Zend_Config_Xml( $rawBody ); $this ->setBodyParams( $config ->toArray()); break ; default : if ( $request ->isPut()) { parse_str( $rawBody , $params ); $this ->setBodyParams( $params ); } break ; }
    81. 81. Content-Type detection (cont.) public function getSubmitParams() { if ( $this ->hasBodyParams()) { return $this ->getBodyParams(); } return $this ->getRequest()->getPost(); } public function direct() { return $this ->getSubmitParams(); }
    82. 82. using the helper public function postAction() { $params = $this ->_helper->params(); if (! $item = $this ->service->create( $params )) { // ... } // ... }
    83. 83. Summary
    84. 84. takeaways <ul><li>Think about what behaviors you want to expose, and write models that do so.
    85. 85. Use HTTP wisely; examine request headers and send appropriate response headers.
    86. 86. Perform context switching based on the Accept header; check for the Content-Type when you examine the request. </li></ul>
    87. 87. most importantly … Keep it simple and predictable.
    88. 88. Thank You Feedback: http://joind.in/1542 http://twitter.com/weierophinney
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×