Thursday, December 4, 2008

CakePHP and RESTful Web Services …

I'm on a quest to make my application provide RESTful web services. After much digging, I found a post by Chris Hartjes at http://www.littlehart.net/atthekeyboard/2007/03/13/how-easy-are-web-services-in-cakephp-12-really-easy/ that helped a lot.

Turns out that Cake has some really nifty built in support that can be turned on really easily. For basic XML support, all you need to do is to add a couple of lines to your routes.php file to allow Cake to handle XML. This is pretty well described in the Cookbook at http://book.cakephp.org/view/477/The-Simple-Setup

So for my VolunteerCake project I added the following lines to my routes.php:


/**
* Add in support for web services by enabling generating output based on extension
*/

Router::mapResources(array('events', 'event_josb', 'groups','jobs', 'slots', 'users', 'user_groups','user_slots'));
Router::parseExtensions();

The mapResources() does the magic that maps the REST requests to the actions in the controllers, and the parseExtensions() sets up Cake to do some other routing magic when the request has a ".xml" extension.

So now if I call any of my actions and append ".xml", Cake changes the response type and view to return XML. Next we need to add the view for the XML, which goes in the xml directory under the view we are REST enabling (e.g.- for jobs, we have a views/jobs/xml directory where the view CTP files need to be placed).

First I created the xml directory under the views/jobs folder, and next I created an index.ctp. This is a very simple file, which Cake's XML helper to spit out the data with the following code:
<Jobs>
<?php echo $xml->serialize($jobs); ?>
</Jobs>

Now to get the XML to display, all I have to do is create the appropriate views for my REST actions.

So for example if I go to the app/jobs action, I would normally see the XHTML representation of the page like:

Jobs XHTML screenshot

Then if I append ".xml" to that same URL, I get the XML back as shown in the following screen shot:

Screen shot of the Jobs XML in browser

Next we need to do the view.ctp to provide support for sending back the data for a specific job by ID. This is practically identical to the index.ctp, except we've modified the code to use the variable $job instead of $jobs (since that's what Cake returns):
<Jobs>
<?php echo $xml->serialize($job); ?>
</Jobs>

This allows us to get the behavior of being able to get the XHTML for a specific job by using a url like /jobs/view/1 as shown:
Screen shot of jobs/view/1

Then by appending ".xml" to that same URL, we get the XML for the job with ID of 1:

Screen shot of jobs/view/1.xml

You may notice that the XML for this Job has a lot more data than we saw when we got the list for the specific Job. The XML from /jobs.xml is only one level deep, while the data from /jobs/view/1.xml has a hierarchy of a job having slots, which in turn has a job, user_slot and user.

That happened because the index action was set up to only get the data from the Jobs, while the view action had recursion set in order to gather all the related data. By setting the recursive var to 0 (zero) in the index action, we get no children, while in the view action we set the value to 2 (two) which tells cake to fetch all the HABTM data (see: http://book.cakephp.org/view/439/recursive for more on this). Alternatively we could do a specific find and modify which bits of data we populate in the controller to determine what data gets spit out in the XML (this would alleviate the one potential downside to this approach which is that ALL of the data fields and tables are currently being placed out in the XML stream).

The basic point here is that we now have a working RESTful service (at least as far as fetching data) that doesn't require a great deal of specific view code.

Next: completing the RESTful CRUD ...

No comments:

Post a Comment