build restful zf2 applications - index-of.esindex-of.es/php/zf2_rest.pdf · model to zf2. areas of...

58
Build RESTful ZF2 Applications Matthew Weier O’Phinney @mwop http://www.mwop.net/

Upload: others

Post on 15-Sep-2020

9 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Build RESTful ZF2 ApplicationsMatthew Weier O’Phinney

@mwophttp://www.mwop.net/

Page 2: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Who I amJust this guy:

• Project Lead, ZendFramework

• Open Source enthusiast• Coffee lover• Chocolate lover• Beer lover

Page 3: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

What do I mean by“REST”?

Page 4: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Richardson Maturity Modelhttp://martinfowler.com/articles/richardsonMaturityModel.html

Page 5: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Level 0HTTP to tunnel RPC

Page 6: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Level 1Resources (multiple endpoints)

Page 7: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Level 2HTTP verbs

Page 8: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Level 3Hypermedia Controls

Page 9: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Level 3: Hypermedia Types:Representations// application/vnd.recipes+json{ "id": "identifier", "name": "Recipe name", "ingredients": [ // ingredient objects ], "directions": "Directions for cooking"}

Page 10: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Level 3: Linking{ "_links": { "self": { "href": "http://example.com/api/recipes/1234" }, "describedby": { "href": "http://example.com/api/resources/recipe" } } // ...}

Page 11: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Level 3: Embedding{ "_embedded": { "addresses": [ { "_links": {"self": { "href": "http://example.com/api/addresses/5678" }}, // a representation } ] } // ...}

Page 12: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Aside: HypermediaApplication Language

Page 13: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Which media type should I use?• Vendor-specific? (e.g., application/vnd.myorg.recipe+json)

• Fully generic? (e.g., application/json)

Page 14: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

A happy medium: HALapplication/hal+json

• Describes hypermedia links• Describes how to embed resources, either as parts of other

resources or parts of collections• Otherwise retains your object structure

Page 15: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

HAL: Resource{ "_links": { "self": { "href": "http://example.com/api/recipes/cacio-e-pepe" } }, "id": "cacio-e-pepe", "name": "Cacio e Pepe Pasta"}

Page 16: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

HAL: Embedded resource{ "_links": {"self": {"href": "..."}}, "_embedded": { "author": { "_links": {"self": {"href": "..."}}, "id": "mario", "name": "Mario Mario" } } // ...}

Page 17: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

HAL: Collections{ "_links": { "self": {"href": "..."}, "next": {"href": "..."}, "prev": {"href": "..."}, "first": {"href": "..."}, "last": {"href": "..."} }, // ...}

Page 18: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

HAL: Collections{ // ... "_embedded": { "recipes": [ { "_links": { "self": { "href": "..." } }, "id": "cacio-e-pepe", "name": "Cacio e Pepe Pasta" }, // ... ] }, "and-other-properties": "if desired"}

Page 19: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Translating theRichardson Maturity

Model to ZF2

Page 20: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Areas of Concern• Routing (unique URLs per resource, link generation)• AbstractRestfulController (HTTP method negotiation)• View Models and Renderers (media-type negotiation and

resource representations)

Page 21: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

The easy bit: routing

Page 22: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Segment routesSegment routes with an :id segment:

'recipe' => array( 'type' => 'Segment', 'options' => array( 'route' => '/api/recipes[/:id]', 'defaults' => array( 'controller' => 'Recipe\ApiController', ), ),)

Page 23: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Controllers

Page 24: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

AbstractRestfulControllerExtend Zend\Mvc\Controller\AbstractRestfulController

• Provides a method per HTTP method, and calls themaccordingly.

• Extracts the identifier from the route matches and passes itto the method, when available.

• Marshals data from the request and passes it to the method,when available.

Page 25: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Controller methodspublic function create($data); // POST to collectionpublic function delete($id); // DELETE to resourcepublic function deleteList(); // DELETE to collectionpublic function get($id); // GET to resourcepublic function getList(); // GET to collectionpublic function head($id = null); // HEAD to eitherpublic function options(); // OPTIONS to eitherpublic function patch($id, $data); // PATCH to resourcepublic function replaceList($data); // PUT to collectionpublic function update($id, $data); // PUT to resource

Page 26: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Options• You should tell the consumer what HTTP methods are

available for a resource.• You should restrict the consumer to those HTTP methods.• Use the options() method for the first, and write an event

listener for the second.

Page 27: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Example: optionsprotected $collectionOptions = array('GET', 'POST');protected $resourceOptions = array('DELETE', 'GET', 'PATCH', 'PUT');

public function options(){ if ($this->params->fromRoute('id', false)) { $options = $this->resourceOptions; } else { $options = $this->collectionOptions; } $response = $this->getResponse(); $response->getHeaders() ->addHeaderLine('Allow', implode(',', $options)); return $response;}

Page 28: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Example: listener (1)public function setEventManager(EventManagerInterface $events){ $this->events = $events; // Register a listener at high priority $events->attach('dispatch', array($this, 'checkOptions'), 10);}

Page 29: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Example: listener (2)public function checkOptions($e){ if ($this->params->fromRoute('id', false)) { $options = $this->resourceOptions; } else { $options = $this->collectionOptions; } if (in_array($e->getRequest()->getMethod(), $options)) { // HTTP method is allowed! return; } $response = $this->getResponse(); $response->setStatusCode(405); // Method Not Allowed return $response;}

Page 30: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Example: createpublic function create($data){ // if JSON Content-Type, returns decoded data; for // application/x-www-form-urlencoded, returns array $resource = $this->myComposedService->create($data); $response = $this->getResponse(); $response->setStatusCode(201) // Created $response->getHeaders()->addHeaderLine( 'Location', $this->url('recipe', array('id', $resource->id)) ); return $resource; // More on this later}

Page 31: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Media-type negotiation

Page 32: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Media-type negotiation• Choose view model based on Accept header. (Potentially

write custom view models for custom media types.)• Potentially restrict access to specific media types.• Return the appropriate Content-Type in the response.

Page 33: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

AcceptableViewModelSelectorSelect view model type based on Accept header.

$criteria = array( 'Zend\View\Model\JsonModel' => array( 'application/json', 'text/json', ),);

$viewModel = $this->acceptableViewModelSelector($criteria);$viewModel->setVariable('resource', $resource);

Page 34: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Use your own view model$criteria = array( 'Recipe\View\RecipeJsonModel' => array( 'application/json', 'text/json', ),);

Page 35: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Raise a 406if (!$viewModel instanceof RecipeJsonModel) { $response = $this->getResponse(); $response->setStatusCode(406); // Not Acceptable return $response;}

Page 36: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Set the Content-Type// in a controller$response = $this->getResponse();$response->getHeaders() ->addHeaderLine('Content-Type', 'application/hal+json');

Page 37: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Set the Content-Type (2)// In a "render" listenerfunction ($e) { $viewModel = $e->getViewModel(); if (!$viewModel instanceof RecipeJsonModel) { return; } $response = $e->getResponse(); $response->getHeaders() ->addHeaderLine('Content-Type', 'application/hal+json');}

Page 38: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Set the Content-Type (3)// In a "response" listener on the View objectfunction ($e) { $viewModel = $e->getModel(); if (!$viewModel instanceof RecipeJsonModel) { return; } $response = $e->getResponse(); $response->getHeaders() ->addHeaderLine('Content-Type', 'application/hal+json');}

Page 39: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Linking

Page 40: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Helpers and plugins• url() controller plugin and view helper• serverUrl() view helper

Page 41: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Url helper// These examples are true of both controllers// and view scripts.

// collection:$this->url('recipe');

// collection with query string:$this->url('recipe', array(), array('query' => true));

// resource:$this->url('recipe', array('id' => $id));

Page 42: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

ServerUrl helper// Generates fully qualified URL (vs. just path)$this->serverUrl($urlGeneratedViaHelper);

Page 43: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Renderers

Page 44: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Resource representations• The “R” in REST is for “Representational”• The root of “representational” is “presentation”• The View layer is where presentation is achieved

Page 45: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Approaches• Custom View Models• Custom Renderers

Page 46: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Extending JsonModel• Provides a serialize() method, which is called by theJsonRenderer

• Allows you to marshal what you want into the structure youwant for the representation

Page 47: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Example: view modelclass RecipeJsonModel extends JsonModel{ public function serialize() { $resource = $this->getVariable('resource'); $representation = array( 'id' => $resource->getId(), 'name' => $resource->getName(), ); return Json::encode($representation); }}

Page 48: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Custom renderer• Can provide helper capabilities (e.g., for links!).• Usually managed by the ServiceManager, allowing for

dependencies.• Can alter workflow based on view models detected, or

contents of view model.

Page 49: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Example: rendererclass RecipeJsonRenderer implements RendererInterface{ public function render($nameOrModel, $values = null) { if ($nameOrModel instanceof RecipeJsonModel) { $helper = $this->helpers->get('RenderRecipe'); } elseif ($nameOrModel instanceof RecipesJsonModel) { $helper = $this->helpers->get('RenderRecipes'); } else { throw new Exception('Cannot handle this!'); } // delegate to the selected helper! return $helper($nameOrModel); }}

Page 50: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Recap

Page 51: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Understand the RichardsonMaturity Modelhttp://martinfowler.com/articles/richardsonMaturityModel.html

• Level 0: RPC, POX• Level 1: Resources• Level 2: HTTP verbs• Level 3: Hypermedia controls

Page 52: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Get to know emerging RESTstandards

• Hypermedia Application Language(http://tools.ietf.org/html/draft-kelly-json-hal-05)

• Collection JSON(http://amundsen.com/media-types/collection/)

Page 53: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Understand the HTTPspecificationhttp://www.w3.org/Protocols/rfc2616/rfc2616.html

• HTTP methods, which are idempotent, and expectedresponse structure

• Accept and Content-Type headers, and how they relate• HTTP response status codes

Page 54: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Understand ZF2’s HTTPcapabilities

• Request, Response, and Headers objects from Zend\Http• AcceptableViewModelSelector MVC controller helper and theAccept HTTP header

Page 55: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Utilize ZF2’s event system• Use event listeners to check for Content-Type, HTTP method

used, Accept header, etc., and return early for bad requests• Use event listeners to shape the rendering cycle; use a

combination of application and view events

Page 56: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Utilize ZF2’s view layer• Use custom view models to “type” your responses• Use custom view renderers to ensure you return appropriate

representations• Use existing helpers such as url() and serverUrl() to

generate links• Create new helpers for implementing link relations

Page 57: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Topics not covered• API versioning (hint: use custom media types and/or headers)• Authentication/Authorization (hint: use OAuth tokens)• XML and XML formats (hint: PHP has lots of tools for this)• Probably tons more…

Page 58: Build RESTful ZF2 Applications - index-of.esindex-of.es/PHP/ZF2_REST.pdf · Model to ZF2. Areas of Concern • Routing (unique URLs per resource, link generation) • AbstractRestfulController

Thank You@mwop

http://www.mwop.net/http://framework.zend.com/