Tutorial Part 1: REST with Dojo and PHP

I only started recently to dig into REST, so I’m in no way an expert in this field. There are not that many tutorials on the internet, and the ones I found were only of limited help. They all seemed to miss something or, rather, I missed something. So I decided to write my own tutorial about my findings to help others that might have the same problems. On the client side, I’ll use the Dojo Toolkit, on the server side, PHP.

I will focus mainly on providing and explaining some working code examples including server PHP code. At the end of this article you can find all code as a downloadable zip-file. As an introduction to REST, read some of the tutorials linked at the end of this article, because I’m not going to repeat some of the things you can read there.

1. Url-confusion

What confused me the most at the beginning was the structure of the REST urls, for example in the article Create a REST API with PHP. How on earth can an url be structured like:

PUT request to /api/users/1 – Update user with ID of 1

Where’s the PHP page in this url that receives this request? And second, I always thought variables had to be posted with a querystring. It took me a while to figure out that the path in the url does not necessarily have to end with a filename such as mypage.php and that in this example it is simply omitted. Furthermore, /api/users/1 is not a sort of variable, but just part of the path to the resource. To make this clear, just add a PHP filename in front of the resource…

mypage.php/api/users/1

… and then you can process the request on the page mypage.php with:

$resource = $_SERVER['PATH_INFO'];
$method   = $_SERVER['REQUEST_METHOD'];

Where the variable $resource would contain:

/api/users/1

and $method would contain:

PUT

To extract additional variable data from the request body or request header you can use parse_string() and file_get_contents() or $_GET.

if ($method == 'POST' || $method == 'PUT') {
  parse_str(file_get_contents('php://input'), $_DATA);
}
else {
  $_DATA = $_GET;
}

The above code is exactly what I use in the demo file php-rest-check.php:

<?php
$resource = $_SERVER['PATH_INFO'];
$method = $_SERVER['REQUEST_METHOD'];

 if ($method == 'POST' || $method == 'PUT') {
  parse_str(file_get_contents('php://input'), $_DATA);
}
else {
  $_DATA = $_GET;
}

$arr = array(
  'method' => $method,
  'resource' => $resource,
  'data' => $_DATA
);	

// send (fake) HTTP status responses if forceError is set
switch($method) {
  case 'POST':
   if ($_DATA['forceError'] == 'false') {
    $status = 'HTTP/1.1 201 Created';
  }
  else {
    $status = 'HTTP/1.1 500 Internal Server Error';
  }
  break;
  case 'GET':
    if ($_DATA['forceError'] == 'false') {
       $status = 'HTTP/1.1 200 OK';
    }
    else {
      $status = 'HTTP/1.1 500 Internal Server Error';
    }
    break;
  case 'PUT':
    if ($_DATA['forceError'] == 'false') {
      $status = 'HTTP/1.1 200 OK';
    }
    else {
      $status = 'HTTP/1.1 404 Not Found';
    }
    break;
  case 'DELETE':
    if ($_DATA['forceError'] == 'false') {
      $status = 'HTTP/1.1 200 OK';
      // With 204 there would be no status code available on the client-side
      //$status = 'HTTP/1.1 204 No Content';
    }
    else {
      $status = 'HTTP/1.1 500 Internal Server Error';
    }
    break;
}
header($status);
if ($_DATA['forceError'] == 'false') {
  // only send response data back when successful
  echo json_encode($arr);
}
?>

2. Sending the RESTful requests

The only remaining question now is how you send a PUT or DELETE request. For that I’ll use the Dojo Toolkit, because that makes it very easy and I’m a big fan of it. If you don’t know Dojo, sorry, but it’s worth learning. The demo is meant as a working example to check if your server provides all four methods. The toolkit provides for each its own method, e.g. dojo.xhrPost(), dojo.xhrGet(), dojo.xhrPut() and dojo.xhrDelete().

First we define a general argument object for all requests, because some attributes are all the same. If you want to send some variables along with your request, just add them as the content property. Dojo will either send them as part of the request header (e.g. in the querystring) in case of a GET/DELETE or as part of the request body in case of a PUT/POST. Use firebug to check what I mean. I added a forceError variable to tell PHP to send fake HTTP error messages.

<script type="text/javascript">
var demo = {
  // general arguments object for the different xhr methods.
  args: {
    url: 'php-rest-check.php',
    handleAs: 'json',
    content: {
      forceError: false,    // if set to true, server responds with an HTTP error
      var1: 'someVal1',   // just some variables
      var2: 'someVal2'
    },
    load: function(response, ioArgs) {
      // log response
      dojo.byId('log').innerHTML+= '<p>HTTP status: ' + ioArgs.xhr.statusText + ' ' + ioArgs.xhr.status + '<br/>' +
        'method: <strong>' + response.method + '</strong><br/>' +
        'resource: ' + response.resource + '<br/>' +
        'query: ' + dojo.objectToQuery(response.data) + '</p>';
    },
    error: function(error) {
      // log HTTP error status
      dojo.byId('log').innerHTML+= '<p>HTTP error status: ' + error.status;
    }
  },
...

Then, all that’s left is that you need to slightly adapt the args object for each request. Note the dojo.clone() method. I use it to work with a copy not a reference of the args object.

...
  // create
  post: function() {
    var args = dojo.clone(this.args);    // otherwise we would keep adding '/name/1' with each new post
    args.url+= '/images/1'; // create new item with id = 1 at '/images'
    dojo.xhrPost(args);
  },
  // read/load
  get: function() {
    var args = dojo.clone(this.args);
    args.url+= '/images';    // load all items at '/images'
    dojo.xhrGet(args);
  },
  // update
  put: function() {
    var args = dojo.clone(this.args);
    args.url+= '/images/1'; // update item with id = 1 at '/images
    dojo.xhrPut(args);
  },
  // delete
  del: function() {
    var args = dojo.clone(this.args);
    args.url+= '/images/1';    // delete item with id = 1 at resouce
    dojo.xhrDelete(args);
  }
}
...

Finally, add the calls to the links on page load:

...
dojo.addOnLoad(function() {
  // add each request method to one of the links
  dojo.query('#ulDemo li').forEach(function(node, i) {
    dojo.connect(node, 'onclick', function(e) {
      dojo.stopEvent(e);	// prevent link action
      demo.args.content.forceError = dojo.byId('fldforceError').checked ? true : false;
      switch(i) {
        case 0:
          demo.post();
          break;
        case 1:
          demo.get();
          break;
        case 2:
          demo.put();
          break;
        case 3:
          demo.del();	// note: delete is reserved word
          break;
        }
     });
  });
});
</script>

Tutorials

Downloads

zipped demo 1 of part 1 php-rest-check.zip

zipped demo 2 of part 1 dojo-jsonreststore.zip

That’s the end of the first part. Second part in preparation…

Join the Conversation

7 Comments

Leave a Reply to Simon Cancel reply

Your email address will not be published. Required fields are marked *

  1. Hello again,
    last hour I made a working example to get the lazy tree populated by all of my dropbox files.

    I encountered the following problem. The dropbox api needs a ‘full’ path but the problem is the “/” character.
    So maybe it might be useful to share.
    Currently I replace ‘/’ by another string in the json “$ref” and “rereplace” that string when making another request to dropbox api.
    This is a very bad and ugly solution. So I would be happy if anyone has a better idea.

  2. –> exactly <– what I was looking for…
    Have you ever thought of coming to google+ ? Strong developer community !
    If so, contact me there and get a big dev. circle back 😉

  3. Hi Robert
    I just bought a new computer and I’m currently putting it together, so I don’t have access to my data yet and I can’t look into how I did it exactly. But if I remember right, it is important, that when creating the new item on the server through a post, you have to respond with a 201 Created. This will automatically have the JsonRestStore call a put to update your node. You should also add a location header with the newly created item.

    See http://dojotoolkit.org/reference-guide/dojox/data/JsonRestStore.html#id8

  4. hello,

    Im in the middle of implementing somethin very similar to what we have
    here: http://www.speich.net/articles/dojo-jsonreststore.php

    During the implementation I faced a really huge problem.
    If Im adding an item to the tree and I dont know
    the “id” of the new node.
    So far Im adding it anyway but DOJO than generate new node “id” for me.
    Question is how can I refresh this node information after that
    JsonRESTStore will write info
    to server (POST request).

    I simply have no idea how can I refresh node information including ID.
    I tried to put new node information into POST request reposne and than
    update the item
    “id” but I got error from DOJO that identity of the item cannot be update.


    Regards,
    Robert