Building RESTful Zend Framework Applications
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Building RESTful Zend Framework Applications

on

  • 18,350 views

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

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

Statistics

Views

Total Views
18,350
Views on SlideShare
17,886
Embed Views
464

Actions

Likes
14
Downloads
339
Comments
2

6 Embeds 464

http://www.comingx.com 433
http://www.slideshare.net 20
http://blog.comingx.com 5
http://coderwall.com 4
http://trunk.ly 1
http://a0.twimg.com 1

Accessibility

Categories

Upload Details

Uploaded via as OpenOffice

Usage Rights

CC Attribution-NonCommercial-ShareAlike LicenseCC Attribution-NonCommercial-ShareAlike LicenseCC Attribution-NonCommercial-ShareAlike License

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
  • 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 & Photo slideshows

Building RESTful Zend Framework Applications Presentation Transcript

  • 1. RESTful Applications with Zend Framework 11 June 2010, RAI, Amsterdam
  • 2. premise How do you detect and respond to REST requests in your Zend Framework application?
  • 3. roadmap
    • Good Modelling
    • 4. Introduction to Zend_Rest_Route
    • 5. HTTP Fundamentals
    • 6. Overview of context switching in ZF
    • 7. Getting content-type-specific parameters
  • 8. Good Modelling
  • 9. create entities that implement __toString() and toArray()
    • Makes serializing simple
    • 10. Makes passing to data access layer simple
    • 11. Simplifies caching (particularly when coupled with a fromArray() method)
  • 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. 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. return collections as paginators
    • Consumers do not need to be aware of data format
    • 15. Consumers can provide offset and limit
    • 16. Consumers can decide how to cast
      • Zend_Paginator implements IteratorAggregate , toJson()
    • Most paginator adapters will only operate once results are requested
  • 17. Zend_Paginator basics
    • setCurrentPageNumber($page)
    • 18. setItemCountPerPage($int)
    • 19. setPageRange($int) (number of pages to show in paginator)
    • 20. Constructor accepts an adapter capable of generating a count() and returning items based on an offset and item count
  • 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. paginator use in view script $this ->users->setItemCountPerPage( $this ->numItems) ->setCurrentPageNumber( $this ->page); echo $this ->users->toJson();
  • 23. going generic
    • Alternately, define a “collection” type
      • e.g., to accept a MongoCursor
      • 24. Implement Countable, and Iterator ; optionally also some serialization interfaces
      • 25. Define methods like skip() and limit() (or page() ), and sort() and setItemClass()
  • 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. collection used in view script $this ->entries->start( $this ->offset) ->limit( $this ->numItems); echo $this ->json( $this ->entries->toArray() );
  • 28. service layers Define your application's API and implement your application in the service layer .
  • 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. what's found in Service Layers?
    • Resource marshalling and Dependency Injection
    • 31. Application-specific logic:
      • Authentication and Authorization (ACLs)
      • 32. Input filtering/data validation
      • 33. Search indexing
      • 34. Caching
      • 35. etc.
  • 36. Zend_Rest_Route basics
  • 37. REST uses HTTP verbs
    • GET with no identifier: list
    • 38. GET with identifier: resource
    • 39. POST : create
    • 40. PUT (with identifier): update
    • 41. DELETE (with identifier): delete
  • 42. Zend_Rest_Route Resource HTTP Method Controller Action
  • 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. 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. 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. 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. retrieving the identifier if (! $id = $this ->getUserParam( 'id' , false)) { // redirect, error, etc. }
  • 48. HTTP Fundamentals
  • 49. request headers
    • Content-Type (what it's providing)
    • 50. Accept (what it expects in response)
  • 51. status codes
    • 201 (Created)
    • 52. 400 (Bad Request) (Failed validations)
    • 53. 401 (Unauthorized)
    • 54. 204 (No Content) (useful with DELETE)
    • 55. 500 (Application Error)
  • 56. response headers
    • Content-Type (what you're returning)
    • 57. Vary (what and when to cache)
  • 58. Context Switching
  • 59. context switching is …
    • Inspect HTTP request headers, and/or the request URI, and vary the response
    • 60. In ZF, using the ContextSwitch and/or AjaxContext action helper
    • 61. They need some configuration …
  • 62. basics
    • Map actions to allowed contexts
    • 63. A “format” request parameter indicates the detected context
    • 64. When a context is detected, an additional suffix is added to the view script
    • 65. Optionally, send some additional headers
      • Push the response object into the view to facilitate
  • 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. add view scripts per-context blog |-- views | |-- scripts | | |-- comment | | | |-- post.phtml | | | `-- post.xml.phtml
  • 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. “html” view <?php $comment = $this ->entryUrl . '#' . $this ->comment->getId(); $response = $this ->response; $response ->setHttpResponseCode(201) ->setHeader( 'Location' , $comment );
  • 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. Getting the context
    • Two common options:
      • Via the URI (often, using a suffix; e.g., “.xml”, “.json”)
      • 72. Via HTTP headers (the Accept header)
  • 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. 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. Content-Type specific params
  • 76. the problem
    • Non form-encoded Content-Types typically mean parameters are passed in the raw request body
    • 77. Additionally, they likely need to be decoded and serialized to a PHP array
    • 78. Use an action helper to automate the process
  • 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. 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. Content-Type detection (cont.) public function getSubmitParams() { if ( $this ->hasBodyParams()) { return $this ->getBodyParams(); } return $this ->getRequest()->getPost(); } public function direct() { return $this ->getSubmitParams(); }
  • 82. using the helper public function postAction() { $params = $this ->_helper->params(); if (! $item = $this ->service->create( $params )) { // ... } // ... }
  • 83. Summary
  • 84. takeaways
    • Think about what behaviors you want to expose, and write models that do so.
    • 85. Use HTTP wisely; examine request headers and send appropriate response headers.
    • 86. Perform context switching based on the Accept header; check for the Content-Type when you examine the request.
  • 87. most importantly … Keep it simple and predictable.
  • 88. Thank You Feedback: http://joind.in/1542 http://twitter.com/weierophinney