PageRenderTime 29ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/mautic/vendor/friendsofsymfony/rest-bundle/FOS/RestBundle/Resources/doc/2-the-view-layer.md

https://gitlab.com/randydanniswara/website
Markdown | 322 lines | 255 code | 67 blank | 0 comment | 0 complexity | b26d6b5fadab52bb860a7dbb8123d530 MD5 | raw file
  1. Step 2: The view layer
  2. ======================
  3. ### Introduction
  4. The view layer makes it possible to write `format` (html, json, xml, etc) agnostic
  5. controllers, by placing a layer between the Controller and the generation of the
  6. final output via the templating or a serializer.
  7. The Bundle works with both the Symfony2 core serializer:
  8. http://symfony.com/doc/2.0/components/serializer.html
  9. But also with the more sophisticated serializer by Johannes Schmitt:
  10. https://github.com/schmittjoh/serializer
  11. https://github.com/schmittjoh/JMSSerializerBundle
  12. In your controller action you will then need to create a ``View`` instance that
  13. is then passed to the ``fos_rest.view_handler`` service for processing. The
  14. ``View`` is somewhat modeled after the ``Response`` class, but as just stated
  15. it simply works as a container for all the data/configuration for the
  16. ``ViewHandler`` class for this particular action. So the ``View`` instance
  17. must always be processed by a ``ViewHandler`` (see the below section on the
  18. "view response listener" for how to get this processing applied automatically)
  19. FOSRestBundle ships with a controller extending the default Symfony controller,
  20. which adds several convenience methods:
  21. ```php
  22. <?php
  23. use FOS\RestBundle\Controller\FOSRestController;
  24. class UsersController extends FOSRestController
  25. {
  26. public function getUsersAction()
  27. {
  28. $data = // get data, in this case list of users.
  29. $view = $this->view($data, 200)
  30. ->setTemplate("MyBundle:Users:getUsers.html.twig")
  31. ->setTemplateVar('users')
  32. ;
  33. return $this->handleView($view);
  34. }
  35. public function redirectAction()
  36. {
  37. $view = $this->redirectView($this->generateUrl('some_route'), 301);
  38. // or
  39. $view = $this->routeRedirectView('some_route', array(), 301);
  40. return $this->handleView($view);
  41. }
  42. }
  43. ```
  44. To simplify this even more: If you rely on the ``ViewResponseListener`` in combination with
  45. SensioFrameworkExtraBundle you can even omit the calls to ``$this->handleView($view)``
  46. and directly return the view objects. See chapter 3 on listeners for more details
  47. on the View Response Listener.
  48. As the purpose is to create a format-agnostic controller, data assigned to the
  49. ``View`` instance should ideally be an object graph, though any data type is
  50. acceptable. Note that when rendering templating formats, the ``ViewHandler``
  51. will wrap data types other than associative arrays in an associative array with
  52. a single key (default ``'data'``), which will become the variable name of the
  53. object in the respective template. You can change this variable by calling
  54. the ``setTemplateVar()`` method on the view object.
  55. There are also two specialized ``View`` classes for handling redirects, one for
  56. redirecting to an URL called ``RedirectView`` and one to redirect to a route
  57. called ``RouteRedirectView``. Note that whether these classes actually cause a
  58. redirect or not is determined by the ``force_redirects`` configuration option,
  59. which is only enabled for ``html`` by default (see below).
  60. There are several more methods on the ``View`` class, here is a list of all
  61. the important ones for configuring the view:
  62. * ``setData($data)`` - Set the object graph or list of objects to serialize.
  63. * ``setHeader($name, $value)`` - Set a header to put on the HTTP response.
  64. * ``setHeaders(array $headers)`` - Set multiple headers to put on the HTTP response.
  65. * ``setSerializationContext($context)`` - Set the serialization context to use.
  66. * ``setTemplate($name)`` - Name of the template to use in case of HTML rendering.
  67. * ``setTemplateVar($name)`` - Name of the variable the data is in, when passed to HTML template. Defaults to ``'data'``.
  68. * ``setEngine($name)`` - Name of the engine to render HTML template. Can be autodetected.
  69. * ``setFormat($format)`` - The format the response is supposed to be rendered in. Can be autodetected using HTTP semantics.
  70. * ``setLocation($location)`` - The location to redirect to with a response.
  71. * ``setRoute($route)`` - The route to redirect to with a response.
  72. * ``setRouteParameters($parameters)`` - Set the parameters for the route.
  73. * ``setResponse(Response $response)`` - The response instance that is populated by the ``ViewHandler``.
  74. See the following example code for more details:
  75. https://github.com/liip/LiipHelloBundle/blob/master/Controller/HelloController.php
  76. ### Forms and Views
  77. Symfony Forms have special handling inside the view layer. Whenever you
  78. - return a Form from the controller
  79. - Set the form as only data of the view
  80. - return an array with a 'form' key, containing a form
  81. - return a form with validation errors
  82. Then:
  83. - If the form is bound and no status code is set explicitly, an invalid form leads to a "validation failed" response.
  84. - In a rendered template, the form is passed as 'form' and ``createView()`` is called automatically.
  85. - ``$form->getData()`` is passed into the view as template as ``'data'`` if the form is the only view data.
  86. - An invalid form will be wrapped into an exception
  87. A response example of an invalid form:
  88. ```javascript
  89. {
  90. "code": 400,
  91. "message": "Validation Failed";
  92. "errors": {
  93. "children": {
  94. "username": {
  95. "errors": [
  96. "This value should not be blank."
  97. ]
  98. }
  99. }
  100. }
  101. }
  102. ```
  103. If you don't like the default exception structure, you can provide your own implementation.
  104. _Implement the ExceptionWrapperHandlerInterface_:
  105. ``` php
  106. namespace My\Bundle\Handler;
  107. class MyExceptionWrapperHandler implements ExceptionWrapperHandlerInterface
  108. {
  109. /**
  110. * {@inheritdoc}
  111. */
  112. public function wrap($data)
  113. {
  114. return new MyExceptionWrapper($data);
  115. }
  116. }
  117. ```
  118. In the `wrap` method return any object or array
  119. _Update the config.yml_:
  120. ``` yaml
  121. fos_rest:
  122. view:
  123. ...
  124. exception_wrapper_handler: My\Bundle\Handler\MyExceptionWrapperHandler
  125. ...
  126. ```
  127. ### Configuration
  128. The ``formats`` and ``templating_formats`` settings determine which formats are
  129. respectively supported by the serializer and by the template layer. In other
  130. words any format listed in ``templating_formats`` will require a template for
  131. rendering using the ``templating`` service, while any format listed in
  132. ``formats`` will use the serializer for rendering. For both settings a
  133. value of ``false`` means that the given format is disabled.
  134. When using ``RouteRedirectView::create()`` the default behavior of forcing a
  135. redirect to the route for html is enabled, but needs to be enabled for other
  136. formats if needed.
  137. Finally the HTTP response status code for failed validation defaults to
  138. ``400``. Note when changing the default you can use name constants of
  139. ``FOS\RestBundle\Util\Codes`` class or an integer status code.
  140. You can also set the default templating engine to something different than the
  141. default of ``twig``:
  142. ```yaml
  143. # app/config/config.yml
  144. fos_rest:
  145. view:
  146. formats:
  147. rss: true
  148. xml: false
  149. templating_formats:
  150. html: true
  151. force_redirects:
  152. html: true
  153. failed_validation: HTTP_BAD_REQUEST
  154. default_engine: twig
  155. ```
  156. See the following example configuration for more details:
  157. https://github.com/liip-forks/symfony-standard/blob/techtalk/app/config/config.yml
  158. ### Custom handler
  159. While many things should be possible via the serializer in some cases
  160. it might not be enough. For example you might need some custom logic to be
  161. executed in the ``ViewHandler``. For these cases one might want to register a
  162. custom handler for a specific format. The custom handler can either be
  163. registered by defining a custom service, via a compiler pass or it can even be
  164. registered from inside the controller action.
  165. The callable will receive 3 parameters:
  166. * the instance of the ``ViewHandler``
  167. * the instance of the ``View``
  168. * the instance of the ``Request``
  169. Note there are several public methods on the ``ViewHandler`` which can be helpful:
  170. * ``isFormatTemplating()``
  171. * ``createResponse()``
  172. * ``createRedirectResponse()``
  173. * ``renderTemplate()``
  174. There is an example inside LiipHelloBundle to show how to register a custom handler:
  175. https://github.com/liip/LiipHelloBundle/blob/master/View/RSSViewHandler.php
  176. https://github.com/liip/LiipHelloBundle/blob/master/Resources/config/config.yml
  177. There is another example in ``Resources\doc\examples``:
  178. https://github.com/FriendsOfSymfony/FOSRestBundle/blob/master/Resources/doc/examples/RssHandler.php
  179. Here is an example using a closure registered inside a Controller action:
  180. ```php
  181. <?php
  182. use Symfony\Bundle\FrameworkBundle\Controller\Controller;
  183. use FOS\RestBundle\View\View;
  184. class UsersController extends Controller
  185. {
  186. public function getUsersAction()
  187. {
  188. $view = View::create();
  189. ...
  190. $handler = $this->get('fos_rest.view_handler');
  191. if (!$handler->isFormatTemplating($view->getFormat())) {
  192. $templatingHandler = function($handler, $view, $request) {
  193. // if a template is set, render it using the 'params' and place the content into the data
  194. if ($view->getTemplate()) {
  195. $data = $view->getData();
  196. if (empty($data['params'])) {
  197. $params = array();
  198. } else {
  199. $params = $data['params'];
  200. unset($data['params']);
  201. }
  202. $view->setData($params);
  203. $data['html'] = $handler->renderTemplate($view, 'html');
  204. $view->setData($data);
  205. }
  206. return $handler->createResponse($view, $request, $format);
  207. };
  208. $handler->registerHandler($view->getFormat(), $templatingHandler);
  209. }
  210. return $handler->handle($view);
  211. }
  212. }
  213. ```
  214. #### Jsonp custom handler
  215. To enable the common use case of creating Jsonp responses this Bundle provides an
  216. easy solution to handle a custom handler for this use case. Enabling this setting
  217. also automatically uses the mime type listener (see the next chapter) to register
  218. a mime type for Jsonp.
  219. Simply add the following to your configuration
  220. ```yaml
  221. # app/config/config.yml
  222. fos_rest:
  223. view:
  224. jsonp_handler: ~
  225. ```
  226. It is also possible to customize both the name of the GET parameter with the callback,
  227. as well as the filter pattern that validates if the provided callback is valid or not.
  228. ```yaml
  229. # app/config/config.yml
  230. fos_rest:
  231. view:
  232. jsonp_handler:
  233. callback_param: mycallback
  234. ```
  235. Finally the filter can also be disabled by setting it to false.
  236. ```yaml
  237. # app/config/config.yml
  238. fos_rest:
  239. view:
  240. jsonp_handler:
  241. callback_param: false
  242. ```
  243. #### CSRF validation
  244. When building a single application that should handle forms both via HTML forms as well
  245. as via a REST API, one runs into a problem with CSRF token validation. In most cases it
  246. is necessary to enable them for HTML forms, but it makes no sense to use them for a REST
  247. API. For this reason there is a form extension to disable CSRF validation for users
  248. with a specific role. This of course requires that REST API users authenticate themselves
  249. and get a special role assigned.
  250. ```yaml
  251. fos_rest:
  252. disable_csrf_role: ROLE_API
  253. ```
  254. ## That was it!
  255. [Return to the index](index.md) or continue reading about [Listener support](3-listener-support.md).