0

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

1
  • anyone can help me out?? Commented May 9, 2013 at 23:58

1 Answer 1

2

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
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.