From f43dd535611dda9528f0bd974e00d09f35a718bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Gallego?= Date: Sun, 16 Mar 2014 16:03:22 +0100 Subject: [PATCH] Update doc --- docs/01. Introduction.md | 2 - docs/02. Quick Start.md | 27 ++++++++++-- docs/06. View layer.md | 13 ++---- docs/07. Cookbook.md | 32 ++++++++++---- .../View/Renderer/DefaultResourceRenderer.php | 44 ++++++++++++++++++- .../Renderer/DefaultResourceRendererTest.php | 18 ++++---- 6 files changed, 104 insertions(+), 32 deletions(-) diff --git a/docs/01. Introduction.md b/docs/01. Introduction.md index 754f246..80e1754 100644 --- a/docs/01. Introduction.md +++ b/docs/01. Introduction.md @@ -25,8 +25,6 @@ ZfrRest currently suffers from the following flaws: * ZfrRest only support POST and PUT for single resource (you cannot bulk create or bulk update) * ZfrRest only supports JSON output -* Cannot assemble URLs from the router -* Links are not yet supported (ZfrRest is currently for target for internal used APIs rather than exposed APIs) * ManyToMany associations are not yet supported (ie. URI like http://example.com/countries/capitals) ### Navigation diff --git a/docs/02. Quick Start.md b/docs/02. Quick Start.md index 856608e..1d5b788 100644 --- a/docs/02. Quick Start.md +++ b/docs/02. Quick Start.md @@ -497,14 +497,14 @@ return [ If you try to access to the `/users/1/tweets` URL now, you'll realize ZfrRest will return a 404 Not Found response. This is because, by default, ZfrRest block all associations. You manually need to add the `Association` annotation -at the association level to make it discoverable: +at the association level, and set the property `routable` to true: ```php /** * @var Tweet[] * * @ORM\OneToMany(targetEntity="Tweet", mappedBy="user") - * @REST\Association + * @REST\Association(routable=true) */ protected $tweets; ``` @@ -522,13 +522,34 @@ the URI will be `/users/1/tweets`. You may want to have your own, custom path. U * @var Tweet[] * * @ORM\OneToMany(targetEntity="Tweet", mappedBy="user") - * @REST\Association(path="special-tweets") + * @REST\Association(routable=true, path="special-tweets") */ protected $tweets; ``` Now, the URI will be `/users/1/special-tweets`. +### Customizing extraction + +The Association mapping also supports extraction information. When ZfrRest extracts your data to return it to the +client, it will automatically extract any association. You can have a very granular control over how you want +the association to be serialized: + +```php +/** + * @var Tweet[] + * + * @ORM\OneToMany(targetEntity="Tweet", mappedBy="user") + * @REST\Association(routable=true, extraction="NONE") + */ +protected $tweets; +``` + +In this example, when a user will be extracted, tweets property won't be outputted to the payload. Possible values +are "NONE", "ID" (default value, it only output identifier values - this can reduce the charge on your database +as no additional queries are made -) and "EMBED" (it will recursively extract the Tweet, as well as any other +association in Tweet that have an "EMBED" extraction strategy). + ### Navigation * Continue to [**Using HTTP exceptions for reporting errors**](/docs/03. Using HTTP exceptions for reporting errors.md) diff --git a/docs/06. View layer.md b/docs/06. View layer.md index 2e759d6..8ed2922 100644 --- a/docs/06. View layer.md +++ b/docs/06. View layer.md @@ -1,13 +1,12 @@ # View layer -ZfrRest provides a flexible way to automatically serialize resources. By default, ZfrRest ships with two renderers: +ZfrRest provides a flexible way to automatically serialize resources. By default, ZfrRest ships with one renderer: -* `SimpleResourceRenderer` -* `HalRenderer` +* `DefaultResourceRenderer` -By default, the `SimpleResourceRenderer` is registered. +By default, the `DefaultResourceRenderer` is registered. -## The `SimpleResourceRenderer` +## The `DefaultResourceRenderer` This is the simplest and most efficient resource renderer. The only thing it does is extracting data from the resource and encoding it to JSON. The following cases can happen: @@ -20,10 +19,6 @@ or the total count. This renderer is voluntarily simple, and does not output any complex things like links. -## The `HalRenderer` - -NOT DONE YET. - ## Creating a custom renderer If you want to create your own renderer, you just need to make sure your renderer implements the diff --git a/docs/07. Cookbook.md b/docs/07. Cookbook.md index 27254cf..eceec25 100644 --- a/docs/07. Cookbook.md +++ b/docs/07. Cookbook.md @@ -19,7 +19,7 @@ class MyController extends AbstractRestfulController } ``` -## How to customize the payload? +## How to customize the payload (like removing specific fields)? When returning a representation of your resource, you may want to customize heavily the payload. For instance, you may want to not return the `password` property. @@ -45,6 +45,24 @@ return [ ]; ``` +And the hydrator itself: + +```php +use Zend\Stdlib\Hydrator\ClassMethods; +use Zend\Stdlib\Hydrator\Filter\MethodMatchFilter; + +class UserHydrator extends ClassMethods +{ + public function __construct() + { + parent::__construct(); + + // Add a filter so that "getPassword" is not called, hence "password" is not outputted + $this->filterComposite->addFilter("password", new MethodMatchFilter('getPassword')); + } +} +``` + ## How to specify a custom input filter? ZfrRest uses input filters to validate data when POSTing and PUTing data. ZfrRest pulls all the input filters from @@ -149,7 +167,7 @@ class User Now, the hydrator: ```php -class CustomUserHydrator implements HydratorInterface +class CustomUserHydrator extends ClassMethods { protected $friendsService; @@ -160,12 +178,10 @@ class CustomUserHydrator implements HydratorInterface public function extract($object) { - return [ - 'id' => $object->getId(), - 'username' => $object->getUsername(), - 'email' => $object->getEmail(), - 'friends_count' => $this->friendsService->countByUser($object) - ]; + $extract = parent::extract($object); + $extract['friends_count'] = $this->friendsService->countByUser($object); + + return $extract; } } ``` diff --git a/src/ZfrRest/View/Renderer/DefaultResourceRenderer.php b/src/ZfrRest/View/Renderer/DefaultResourceRenderer.php index a714a88..884b756 100644 --- a/src/ZfrRest/View/Renderer/DefaultResourceRenderer.php +++ b/src/ZfrRest/View/Renderer/DefaultResourceRenderer.php @@ -28,7 +28,49 @@ use ZfrRest\View\Model\ResourceModel; /** + * This is a very simple renderer that only outputs the resource as JSON, either directly in the payload for a single + * resource, or wrapping it around a "data" top-level attributes for multiple resources * + * This renderer does not assume to render any links, it's voluntarily simple. Here is an example of the generated + * payload when asking a simple resource like GET /posts/1: + * + * { + * "id": 1, + * "title": "ZfrRest is awesome", + * "author": { + * "id": 50, + * "name": "Michaël Gallego" + * } + * } + * + * Or when using a collection: + * + * { + * "limit": 10, + * "offset": 50, + * "total": 600, + * "data": [ + * { + * "id": 1, + * "title": "PHP will domine the world!", + * "author": { + * "id": 56, + * "name": "Marco Pivetta" + * } + * }, + * { + * "id": 2, + * "title": "PHP generators are awesome", + * "author": { + * "id": 95, + * "name": "Daniel Gimenes" + * } + * } + * ] + * } + * + * Note that this renderer can recursively extracts any association, based on your mapping information. It also + * handles edge-cases like circular extraction * * @author Michaël Gallego * @licence MIT @@ -84,7 +126,7 @@ public function render($nameOrModel, $values = null) // If the resource is a collection, we render each item individually if ($resource->isCollection()) { foreach ($data as $item) { - $payload['items'][] = $this->renderItem($item, $resourceMetadata); + $payload['data'][] = $this->renderItem($item, $resourceMetadata); } } else { $payload = $this->renderItem($data, $resourceMetadata); diff --git a/tests/ZfrRestTest/View/Renderer/DefaultResourceRendererTest.php b/tests/ZfrRestTest/View/Renderer/DefaultResourceRendererTest.php index 700f299..e9e3ec3 100644 --- a/tests/ZfrRestTest/View/Renderer/DefaultResourceRendererTest.php +++ b/tests/ZfrRestTest/View/Renderer/DefaultResourceRendererTest.php @@ -218,7 +218,7 @@ public function testCanRenderCollectionResourceWithoutAssociation() $payload = $this->resourceRenderer->render($resourceModel); $expectedPayload = [ - 'items' => [ + 'data' => [ [ 'id' => 2, 'username' => 'bakura' @@ -278,7 +278,7 @@ public function testCanRenderCollectionResourceWithAssociationAsId() $payload = $this->resourceRenderer->render($resourceModel); $expectedPayload = [ - 'items' => [ + 'data' => [ [ 'id' => 2, 'username' => 'bakura', @@ -344,7 +344,7 @@ public function testCanRenderCollectionResourceWithAssociationAsEmbed() $payload = $this->resourceRenderer->render($resourceModel); $expectedPayload = [ - 'items' => [ + 'data' => [ [ 'id' => 2, 'username' => 'bakura', @@ -403,7 +403,12 @@ public function testCanRenderCollectionResourceAsPaginator() $payload = $this->resourceRenderer->render($resourceModel); $expectedPayload = [ - 'items' => [ + 'meta' => [ + 'limit' => 10, + 'offset' => 0, + 'total' => 2 + ], + 'data' => [ [ 'id' => 2, 'username' => 'bakura' @@ -412,11 +417,6 @@ public function testCanRenderCollectionResourceAsPaginator() 'id' => 3, 'username' => 'ocramius' ] - ], - 'meta' => [ - 'limit' => 10, - 'offset' => 0, - 'total' => 2 ] ];