Create RESTful Applications Using The Zend Framework

The Zend Framework 1.9 release added a new feature - Zend_Rest_Controller. Zend_Rest_Controller and Zend_Rest_Route classes go hand in hand. In the previous versions of the Zend Framework, we have had the Zend_Rest_Server component. We still have. Since Zend_Rest_Server provides an RPC like component violating the REST architectural constraint, it is likely to be deprecated in the future versions of the Zend Framework.

In this article let us explore how to make use of Zend_Rest_Route and Zend_Rest_Controller to build a RESTful server application. Zend_Rest_Route routes the request to the appropriate module, controller and action depending on the HTTP request method and URI.

Let's start coding. We build the RESTful application based on the QuickStart project.

Adding the Zend_Rest_Route in the bootstrap

You can choose to enable Zend_Rest_Route for the entire application or for specific set of modules. In this example we enable the rest route for the entire application.

Bootstrap the front controller resource and add the rest route.

<?php
protected function _initRestRoute()
{
        
$this->bootstrap('frontController');
        
$frontController Zend_Controller_Front::getInstance();
        
$restRoute = new Zend_Rest_Route($frontController);
        
$frontController->getRouter()->addRoute('default'$restRoute);

}

?>

Creating the Zend_Rest_Controller

Create the file application/controllers/ArticleController.php. We extend Zend_Rest_Controller instead of Zend_Controller_Action. In our controller we are going to have five actions.

  1. indexAction - return all articles
  2. getAction - return a particular article
  3. postAction - create a new article
  4. putAction - update a particular article
  5. deleteAction - delete a particular article

These actions are defined as abstract methods in the Zend_Rest_Controller class.

The skeletal controller looks like:

<?php
class ArticleController extends Zend_Rest_Controller
{

    public function 

init()
    {
        
$this->_helper->viewRenderer->setNoRender(true);
    }

    public function 

indexAction()
    {
    }

    public function 

getAction()
    {
    }
    
    public function 
postAction()
    {
    }
    
    public function 
putAction()
    {
    }
    
    public function 
deleteAction()
    {
    }

}

?>

For the purpose of brevity, I have disabled the view for this controller in the init() hook.

To test the routing of the requests, let's append sample messages to the response object in each action.

<?php
 
public function indexAction()
    {
         
$this->getResponse()
            ->
appendBody("From indexAction() returning all articles");
    }

    public function 

getAction()
    {
        
$this->getResponse()
            ->
appendBody("From getAction() returning the requested article");

    }
    
    public function 

postAction()
    {
        
$this->getResponse()
            ->
appendBody("From postAction() creating the requested article");

    }
    
    public function 

putAction()
    {
        
$this->getResponse()
            ->
appendBody("From putAction() updating the requested article");

    }
    
    public function 

deleteAction()
    {
        
$this->getResponse()
            ->
appendBody("From deleteAction() deleting the requested article");

    }

?>

Testing the RESTful server using curl

On my computer I have installed this application for the domain zfrest.example.com. We use the curl command to test our RESTful server.

Testing the indexAction() : The URI http://zfrest.example.com/article represents the article resource. All articles are returned for this request.

$ curl http://zfrest.example.com/article

I get the following output:

From indexAction() returning all articles

Testing the getAction() : The URI http://zfrest.example.com/article/1 represents the resource - article 1.

$ curl http://zfrest.example.com/article/1

I get the following output:

From getAction() returning the requested article

Testing the postAction() : we make an HTTP POST request to http://zfrest.example.com.

$ curl -d "article=myarticle" http://zfrest.example.com/article/

I get the following output:

From postAction() creating the requested article

Testing the putAction() : we request the article 1 to be updated by making HTTP PUT request to http://zfrest.example.com/article/1

$ curl -d "article=updatedarticle" -X PUT http://zfrest.example.com/article/1

I get the following output:

From putAction() updating the requested article

Testing the deleteAction() : we send an HTTP DELETE request to http://zfrest.example.com/article/1

curl -X DELETE http://zfrest.example.com/article/1

I get the following output:

From deleteAction() deleting the requested article

Summarizing the exercise

Zend Framework allows you to build RESTful server applications using the Zend_Rest_Controller component. The curl command is a very useful tool to test RESTful servers. If you don't have the curl command on your computer, you can write a PHP script and make use of the curl extension provided by PHP. In the upcoming posts of this series, I will discuss managing API keys from your RESTful application, returning appropriate HTTP response codes, reading the body from PUT and DELETE requests and more.

Are you going to a build RESTful server using the Zend_Rest_Controller component? Tell me about your experiences.

Create RESTful Applications Using The Zend Framework - Part II : Using HTTP Response Code

Reference:
Wikipedia article on Representational State Transfer
Zend_Rest_Route - Zend Framework manual page

Content Type: 
FOSS Project: 

Comments

One thing often overlooked when creating RESTful services is returning appropriate HTTP response codes. Your controllers and/or view scripts should set codes such as 201 (created), 204 (no content -- useful for delete responses), etc. Make sure you get familiar with both HTTP response codes and headers, as well as the details of the REST specification, when you start developing REST applications -- they'll make your apps interoperate well with a variety of clients out of the box.

Thanks for the comment and the insightful tip, Matthew.

Thanks for your post a good starting point on REST with zf. ZF has evolved quickly still working with version 1.0 in some projects. Any idea how much overhead Zend_Rest_Route brings to the table?

Hi Tom,

You're welcome.

ZF has indeed grown a lot and at a fast pace. I haven't done any performance benchmarks on Zend_Rest_Route yet. I imagine it won't be much of a concern. Please share the statistics if you happen to benchmark.

Awesome! Thank you.
This post gave me a kick start on RESTful API development in ZF.
I'm definitely looking forward to read your upcoming posts of this topic.

Thanks for the comment.

I will publish more about ZF and REST in the near future.

Might also be interested in http://framework.zend.com/wiki/display/ZFPROP/Zend_Rest_Controller_DbTable

I'd like to get some feedback on the proposal before starting to code it into the framework core.

Luke,

First of all, I would like to thank you for contributing Zend_Rest_Route and Zend_Rest_Controller.

I have posted my comments to the Zend_Rest_Controller_DbTable component proposal on the Wiki.

Thank you for introducing the REST .
Looking forward to see the rest of the articles . Also Thanks to Mathew for pointing out http response code .

And what about traditional forms?
How do I post/update new articles from Zend_Form?

Use the "post" method on URI http://example.com/article/1

Here's a sample form

Here's the sample output after submitting the form

From putAction() updating the requested article

If there's no article by id 1, the server should create it. If the request fails, you get the relevant HTTP response code. More about it in the coming articles :)

Thanks.
Can't wait what's coming next :)

Excellent article, simple like that!, thanks very much!!!!!!

Not loving the ZF REST solution so I wrote a post called 'RESTful PHP Applications Despite Zend Framework':

http://mike.mykanjo.co.uk/2009/11/22/restful-php-applications-despite-ze...

contains link to my alternative solution called 'Resauce':

http://github.com/mikekelly/Resauce

Coolness. This is exactly what I was looking for. Thanks!

Okay this one got me for a while. Having spent a few hours figuring out what was going on, I finally came across this snippet in the Zend Framework docs;

http://framework.zend.com/manual/en/zend.controller.plugins.html#zend.co...

You will need that to handle PUT request params.

Thank you for this article. It really helped to simplify some things I was confused about. I can't wait for more articles on this topic! Seriously...do you think you could post a more advanced article on creating a restful zend framework application? Or a link to one? It's been a few months since this post and I'm anxious to learn more!

You're welcome, Andrew.

I haven't been able to post blog entries recently due to hectic schedules. I will post more about RESTful applications in the near future.

Hi,

When are you going to release your next post on managing API keys etc?

I would really like to know as I cannot find anything on the net currently.

Cheers.

Ian

Hello Sudheer,

First of all, thanks for the excellent article.

Do you think you can provide the full code of the above example as a zip file?

Subra

I'll put it online in the near future.

Let me know if you have a specific problem with the code.

Thanks Sudheer. Pls do update this thread once you have posted it online. It will very helpful to novices like myself.

Thanks for the article. It helped me a lot.
I noticed I was getting all my layout html returned with the response also, so I disable it in the init() function like this: $this->_helper->layout->disableLayout();
We are only supposed to return response codes anyway right?
Correct me if I'm wrong.

You are supposed to return the entity, representation of the resource, along with the response code. The entity could be represented using any format you wish - XML, JSON, HTML, text or whatever.

I am currently struggling with Zend_Rest_* to achieve the following Rest routes:

http://example.org/users/123/items/456

I need a Rest route for the user resource: /users/:id
This can be done just like in your example

But I also need a Rest route for the item resource of a given user
/user/:id/items/:itemid

And I can't figure it out. Do you know how to set up routes for that?

I don't think the current implementation of Zend_Rest_Route allows you to have that kind of route.

Hello Sudheer,

I followed the quickstart tutorials and also ur tutorial here. This is how my http.conf file looks:


DocumentRoot "C:\Documents and Settings\aswats\My Documents\MyProjects"
.
.
.

#
# Possible values for the Options directive are "None", "All",
# or any combination of:
# Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
#
# Note that "MultiViews" must be named *explicitly* --- "Options All"
# doesn't give it to you.
#
# The Options directive is both complicated and important. Please see
# http://httpd.apache.org/docs/2.2/mod/core.html#options
# for more information.
#
Options Indexes FollowSymLinks

#
# AllowOverride controls what directives may be placed in .htaccess files.
# It can be "All", "None", or any combination of the keywords:
# Options FileInfo AuthConfig Limit
#
AllowOverride None

#
# Controls who can get stuff from this server.
#
Order allow,deny
Allow from all

In my httpd-vhosts.conf file I have added the following:

ServerName example.local
DocumentRoot /askaway/public

SetEnv APPLICATION_ENV "development"

DirectoryIndex index.php
AllowOverride All
Order allow,deny
Allow from all

I also changed the hosts file to include

127.0.0.1 example.local

I followed your instructions step by step. But when I point my browser to http://example.local/article, i get the following error:

Forbidden

You don't have permission to access /article on this server.

Could please tell me what am I doing wrong here?

Set AllowOverride to All

Hi,
Next step will be building custom rest router (perhaps extending the one that exists in ZF) cause current implementation of REST Router is useless.
It doesn't support any arbitrary urls.
Cheers

Zend_Rest_Route can help you get started implementing REST style URIs for one resource. For more flexible URIs you have to chain custom routes. I wish to cover this topic in detail in an upcoming post.

Thanks for the comment.

So if one would like to implement a search on the articles, that has to be a different controller because it is considered a new resource?

Great Article, Thank you!

Articles search should be done in ArticleController. The search parameters must be sent in the form of HTTP GET parameters. The controller should perform the search and return a collection of articles in the response.

instruction to help newbe (as me :) )

generate zend application:

zf.sh create project /path/to/the/project/project_name

zf or zf.bat is in the bin directory od ZendFramework distribution.

change Bootstrap.php as follow:

<?php

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap {

protected function _initRestRoute() {
$this->bootstrap('frontController');
$frontController = Zend_Controller_Front::getInstance();
$restRoute = new Zend_Rest_Route($frontController);
$frontController->getRouter()->addRoute('default', $restRoute);
}
}

thx for brilliant introduction!

Please can anyone mention the step by step procedure for this... i tried the above steps:
First i changed my bootstrap.php file, and added a controller, with some action methods.

When i invoked ..... my application/article/1
i didn't get the expected output, rather i got some error saying:
File not found..
Message: Action "article" does not exist and was not trapped in __call()

Please help....Do i need to create any vieww or models ?

If anyone would like to set it in their ini configuration file. Do the following:

UsersConroller

resources.router.routes.rest.type = "Zend_Rest_Route"
resources.router.routes.rest.defaults.controller = object
resources.router.routes.rest.defaults.mod = users

or more than one controller
UsersController and LeadsController

resources.router.routes.rest.type = "Zend_Rest_Route"
resources.router.routes.rest.defaults.controller = object
resources.router.routes.rest.defaults.mod = users, leads

A Marvel on the web my friend.!! Thanks a ton for such a descriptive guide on zend rest application development. God bless you!!

Nice article, appreciate your help

Looking forward to your post on "Chaining custom routes" for REST URIs

HI I am new to zend,
when i added
protected function _initRestRoute() {
$this->bootstrap('frontController');
$frontController = Zend_Controller_Front::getInstance();
$restRoute = new Zend_Rest_Route($frontController);
$frontController->getRouter()->addRoute('default', $restRoute);

these lines to my bootstrap file, every action in other controllers are not working , all actions are mapped to getAction. so getting an error like "Action "get" does not exist and was not trapped in __call() " , the rest part is working fine.

it happened because this code works for the whole module 'default' ... u can easily create new module f.e. 'restmodule' and change this line in bootstrap
$frontController->getRouter()->addRoute('default', $restRoute);
like this
$frontController->getRouter()->addRoute('restmodule', $restRoute);
about module structure of the zf application u can find in the reference guide zend.com

How i can consume this with zend framework?

It's not clear to me where to put

protected function _initRestRoute

Could you clarify? I'm really new to Zend Framework, so please be very verbose and explain anything you think may be necessary.

Tom

Hello,

I kept getting an Error 500. I followed every step of this tutorial multiple times but ended up with a 500 every time. I checked out the Zend_Rest_Controller class and turns out that there is another abstract function missing in this tutorial. For anyone experiencing the same problem, add
public function headAction(){}
to your controller.

Cheers!

Add new comment