Question

I just started using rest library wrote by Phil Sturgeon. I started using it by writing some simple examples. I short of get 'post' and 'get' work, but not for put and delete. I have some questions based on the code below.

// a simple backbone model
var User = Backbone.Model.extend({
    urlRoot: '/user',
    defaults:{
        'name':'John',
        'age': 17
    }
});

var user1 = new User();
//user1.save(); // request method will be post unless the id attr is specified(put)
//user1.fetch(); // request method will be get unless the id attr is specified
//user1.destroy(); // request method will be Delete with id attr specified

In my CI REST controller

class User extends REST_Controller
{
    public function index_get()
    {
        echo $this->get(null); //I can see the response data
    }

    public function index_post()
    {
        echo $this->post(null); //I can see the response data
    }

    public function index_put()
    {

    }

    public function index_delete()
    {

    }
}

Basically, the get and post in the controller will be called when I save a model or fetch a model. With a id specified in the model, I can make a put or delete request to the server using model.save() and model.destroy(). however, I got a server error. it looks like index_put or index_delete can not be called. does anyone know How I can handle:

  1. put request in the controller
  2. delete request in the controller
  3. get a single record with id specified

From the git, I only saw him to list index_post and index_put. there is no index_put and index_delete demo. should anyone can help me out? thanks

Was it helpful?

Solution

I faced the same exact problem, it looks like that DELETE, PUT, PATCH methods are not fully supported by browsers/html/server yet. You may want to look at this stack overflow question: Are the PUT, DELETE, HEAD, etc methods available in most web browsers?

A simple solution would be to change the methodMap of backbone line 1191 to the following:

 // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
 var methodMap = {
  'create': 'POST',
  'update': 'POST',     //'PUT',
  'patch':  'POST',     //'PATCH',
  'delete': 'POST',     //'DELETE',
  'read':   'GET'
 };

and then include the action type as an attribute of the model

 var Person = Backbone.Model.extend({
      defaults:{
         action_type : null,
         /*
          * rest of the attributes goes here
          */
      },
      url : 'index.php/person'
 });

now when you want to save a model, do the following

 var person = new Person({ action_type: 'create' });
 person.set( attribute , value );  // do this for all attributes
 person.save();

in the application/controllers folder you should have a controller called person.php with class named Person extending REST_Controller, that has the following methods:

class Person extends REST_Controller {

   function index_get()  { /* this method will be invoked by read action */  }

   /*  the reason those methods are prefixed with underscore is to make them
    *  private, not invokable by code ignitor router. Also, because delete is
    *  might be a reserved word
    */

   function _create()  { /* insert new record */  }
   function _update()  { /* update existing record */  }
   function _delete()  { /* delete this record */  }
   function _patch ()  { /* patch this record */  }

   function index_post() {

      $action_type = $this->post('action_type');
      switch($action_type){

           case 'create' : $this->_create();  break;
           case 'update' : $this->_update();  break;
           case 'delete' : $this->_delete();  break;
           case 'patch'  : $this->_patch();   break;
           default:
               $this->response( array( 'Action '. $action_type .' not Found' , 404) );
               break;

      }
   }
}

Having said that, this solution is an ugly one. If you scroll up in the backbone implementation, you will find the following code at line 1160:

// For older servers, emulate HTTP by mimicking the HTTP method with `_method`
// And an `X-HTTP-Method-Override` header.
if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) {
    params.type = 'POST';

which means you need to set the emulate options of backbone configurations. add the following lines to your main.js

 Backbone.emulateHTTP = true;
 Backbone.emulateJSON = true;

To test the effect of that, I created a simple model and here are the results

you need a controller called Api in applications/controllers folder, in a file named api.php

<?php defined('BASEPATH') OR exit('No direct script access allowed');

require_once APPPATH.'/libraries/REST_Controller.php';

class Api extends REST_Controller
{

   function index_get()
   {
    $this->response(array("GET is invoked"));
   }

   function index_put()
   {
    $this->response(array("PUT is invoked"));
   }

   function index_post()
   {
    $this->response(array("POST is invoked"));
   }

   function index_patch()
   {
    $this->response(array("PATCH is invoked"));
   }

   function index_delete()
   {
    $this->response(array("DELETE is invoked"));
   }

}

and in your js/models folder, create a model called api_model.js

var Api = Backbone.Model.extend({
       defaults:{ 
            id:      null, 
            name:    null
       },
       url: "index.php/api/"
});

var api = new Api();

api.fetch({ success: function(r,s) { console.log(s); } });  // GET is invoked

api.save({},{ success: function(r,s) { console.log(s); } }); // POST is invoked

//to make the record old ( api.isNew() = false now )
api.save({id:1},{ success: function(r,s) { console.log(s); } }); // PUT is invoked

api.destroy({ success: function(r,s) { console.log(s); } }); //DELETE is invoked

I don't know how to do patch, but hope this helps.

Edit

I found out how to do patch, which is not included in the REST implementation of code ignitor. In REST_Controller line 39, you will find the following,

protected $allowed_http_methods = array('get', 'delete', 'post', 'put');

you need to add 'patch' at the end, to accept this method, also, after doing that add this code

/**
 * The arguments for the PATCH request method
 *
 * @var array
 */ 
protected $_patch_args = array();

also, you need to add the following code to parse patch arguments:

/**
 * Parse PATCH
 */
protected function _parse_patch()
{
    // It might be a HTTP body
    if ($this->request->format)
    {
        $this->request->body = file_get_contents('php://input');
    }

    // If no file type is provided, this is probably just arguments
    else
    {
        parse_str(file_get_contents('php://input'), $this->_patch_args);
    }
}

Now, according to backbone docs, you need to pass {patch: true} to send a PATCH method, when you call the following line, you execute a patch:

 api.save({age:20},{patch: true, success: function(r,s) { console.log(s); } });

 // PATCH is invoked
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top