REST

Many newer application programmers are realizing the need to open their corefunctionality to a greater audience. Providing easy, unfettered access to yourcore API can help get your platform accepted, and allows for mashups and easyintegration with other systems.

While other solutions exist, REST is a great way to provide easy access to thelogic you’ve created in your application. It’s simple, usually XML-based (we’retalking simple XML, nothing like a SOAP envelope), and depends on HTTP headersfor direction. Exposing an API via REST in CakePHP is simple.

The Simple Setup

The fastest way to get up and running with REST is to add a few lines to setupresource routes in your config/routes.php file.

Once the router has been set up to map REST requests to certain controlleractions, we can move on to creating the logic in our controller actions. A basiccontroller might look something like this:

  1. // src/Controller/RecipesController.php
  2. class RecipesController extends AppController
  3. {
  4. public function initialize(): void
  5. {
  6. parent::initialize();
  7. $this->loadComponent('RequestHandler');
  8. }
  9.  
  10. public function index()
  11. {
  12. $recipes = $this->Recipes->find('all');
  13. $this->set('recipes', $recipes);
  14. $this->viewBuilder()->setOption('serialize', ['recipes']);
  15. }
  16.  
  17. public function view($id)
  18. {
  19. $recipe = $this->Recipes->get($id);
  20. $this->set('recipe', $recipe);
  21. $this->viewBuilder()->setOption('serialize', ['recipe']);
  22. }
  23.  
  24. public function add()
  25. {
  26. $this->request->allowMethod(['post', 'put']);
  27. $recipe = $this->Recipes->newEntity($this->request->getData());
  28. if ($this->Recipes->save($recipe)) {
  29. $message = 'Saved';
  30. } else {
  31. $message = 'Error';
  32. }
  33. $this->set([
  34. 'message' => $message,
  35. 'recipe' => $recipe,
  36. ]);
  37. $this->viewBuilder()->setOption('serialize', ['recipe', 'message']);
  38. }
  39.  
  40. public function edit($id)
  41. {
  42. $this->request->allowMethod(['patch', 'post', 'put']);
  43. $recipe = $this->Recipes->get($id);
  44. $recipe = $this->Recipes->patchEntity($recipe, $this->request->getData());
  45. if ($this->Recipes->save($recipe)) {
  46. $message = 'Saved';
  47. } else {
  48. $message = 'Error';
  49. }
  50. $this->set([
  51. 'message' => $message,
  52. 'recipe' => $recipe,
  53. ]);
  54. $this->viewBuilder()->setOption('serialize', ['recipe', 'message']);
  55. }
  56.  
  57. public function delete($id)
  58. {
  59. $this->request->allowMethod(['delete']);
  60. $recipe = $this->Recipes->get($id);
  61. $message = 'Deleted';
  62. if (!$this->Recipes->delete($recipe)) {
  63. $message = 'Error';
  64. }
  65. $this->set('message', $message);
  66. $this->viewBuilder()->setOption('serialize', ['message']);
  67. }
  68. }

RESTful controllers often use parsed extensions to serve up different viewsbased on different kinds of requests. Since we’re dealing with REST requests,we’ll be making XML views. You can make JSON views using CakePHP’s built-inJSON and XML views. By using the built in XmlView wecan define a serialize option. This option is used to define which viewvariables XmlView should serialize into XML.

If we wanted to modify the data before it is converted into XML we should notdefine the serialize option, and instead use template files. We placethe REST views for our RecipesController inside templates/Recipes/xml. We can also usethe Xml for quick-and-easy XML output in those views. Here’s whatour index view might look like:

  1. // templates/Recipes/xml/index.php
  2. // Do some formatting and manipulation on
  3. // the $recipes array.
  4. $xml = Xml::fromArray(['response' => $recipes]);
  5. echo $xml->asXML();

When serving up a specific content type using Cake\Routing\Router::extensions(),CakePHP automatically looks for a view helper that matches the type.Since we’re using XML as the content type, there is no built-in helper,however if you were to create one it would automatically be loadedfor our use in those views.

The rendered XML will end up looking something like this:

  1. <recipes>
  2. <recipe>
  3. <id>234</id>
  4. <created>2008-06-13</created>
  5. <modified>2008-06-14</modified>
  6. <author>
  7. <id>23423</id>
  8. <first_name>Billy</first_name>
  9. <last_name>Bob</last_name>
  10. </author>
  11. <comment>
  12. <id>245</id>
  13. <body>Yummy yummmy</body>
  14. </comment>
  15. </recipe>
  16. ...
  17. </recipes>

Creating the logic for the edit action is a bit trickier, but not by much. Sinceyou’re providing an API that outputs XML, it’s a natural choice to receive XMLas input. Not to worry, theCake\Controller\Component\RequestHandler andCake\Routing\Router classes make things much easier. If a POST orPUT request has an XML content-type, then the input is run through CakePHP’sXml class, and the array representation of the data is assigned to$this->request->getData(). Because of this feature, handling XML and POST data inparallel is seamless: no changes are required to the controller or model code.Everything you need should end up in $this->request->getData().

Accepting Input in Other Formats

Typically REST applications not only output content in alternate data formats,but also accept data in different formats. In CakePHP, theRequestHandlerComponent helps facilitate this. By default,it will decode any incoming JSON/XML input data for POST/PUT requestsand supply the array version of that data in $this->request->getData().You can also wire in additional deserializers for alternate formats if youneed them, using RequestHandler::addInputType().

RESTful Routing

CakePHP’s Router makes connecting RESTful resource routes easy. See the sectionon RESTful Routing for more information.