18

I'm trying to get an understanding of how Backbone.js, Slim PHP and Paris/Idiorm might work together and I'm having trouble completing the flow, starting with model attribute data, all the way to the database. PROBLEM: What exactly gets sent to my server when I do model.save() ?

Client-side: Backbone.js

var Donut = Backbone.Model.extend({
    defaults: {
        name: null,
        sparkles: false,
        creamFilled: false
    },
    url: function() {
        return '/donut';
    }
});

var bostonCream = new Donut({
    name: 'Bawston Cream',
    sparkles: true,
    creamFilled: true
});

bostonCreme.save();  // <-- Problem: Not sure what & format this is sending

I think the above is my main problem. My understanding is that backbone will by default, know to send POST data since it's new. It sends it to /donut which is routed, but the question I have is WHAT does it send? And in what format? The outcome I want is to save those donut attributes to my DB. I can pass this server-side code a json like this using jQuery $.post()...

var myDonut = {"name":"Jelly Filled", "sparkles":false, "creamFilled":true};
$.post('http://localhost/donut', myDonut);

...and it happily takes it, saves it to my database. But with the current setup trying to send my backbone donut data, I get POST 500 Internal Server Error. Below I have some server-side code.

Server-side: Slim PHP w/ Paris

class Donut extends Model {}

$app->post('/donut', function() use ($app) {  // Slim framework routes my POST...

    $donuts = Model::factory('Donut')->create();  // Paris stuff...

    $donuts->name = $app->request()->post('name');  // Slim request parameters...
    $donuts->sparkles = $app->request()->post('sparkles');
    $donuts->creamFilled = $app->request()->post('creamFilled');

    $donuts->save();   // Paris... Save name, sparkles, and creamFilled to my DB
});

I have a feeling the answer is out there, but every example I've looked at seems to be missing one piece of the puzzle or another and I can't get that "A-hA!" moment. I thank you in advance and apologize if this is a really ignorant question. :-P

FOLLOWUP/EDIT: 1

Can you paste the error messages?

I get a POST http://localhost:8888/donut 500 (Internal Server Error) in the current state. I can get more information with the following code.

bostonCream.save({}, {  // REPLACE bostonCream.save();
    success: function(model, response) {
        console.log('SUCCESS:');
        console.log(response);
    },
    error: function(model, response) {
        console.log('FAIL:');
        console.log(response);
    }
});

Now when I run backbone's save(), I still get the 500 Error but also XMLHttpRequest as my FAIL response. The only remarkable clue from the XMLHttpRequest is responseText = SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'name' cannot be null.

So my guess is that either 1) I'm messing something up with the save() in that it isn't capturing my attributes correctly, 2) It is currently sending my attributes in a format that my server isn't recognizing with the standard $app->request()->post() Slim methods (Doesn't seem to do much when I try accessing directly with $_POST either), 3) My server isn't setup correctly to take the kind of data that is being sent.

Another thing I noticed although I don't know what to make of it is that when I add

echo $_POST;

It returns to me an empty array. Still gives me the FAIL. If I do THIS however...

echo json_encode($_POST);

It gives me a SUCCESS and the response is a [ ]. Nothing in there. Clearly my POST data is still wonky.

jmk2142
  • 8,581
  • 3
  • 31
  • 47

3 Answers3

30

I came up with a solution to completing the problem: how to get data from client to server using the default backbone save() and .sync - passed over to the Slim php framework and going through Paris/Idiorm to my DB.

I am including my working updated code below:

Client-side: Backbone.js

var Donut = Backbone.Model.extend({
    defaults: {
        name: null,
        sparkles: false,
        creamFilled: false
    },
    url: function() {
        return '/donut';
    }
});

var bostonCream = new Donut({
    name: 'Bawston Cream',
    sparkles: true,
    creamFilled: true
});

bostonCream.save();

/***** If you want to check out the response to save() ? ***
bostonCream.save({}, {
    success: function(model, response) {
        console.log('SUCCESS:');
        console.log(response);
    },
    error: function(model, response) {
        console.log('FAIL:');
        console.log(response);
    }
});
************************************************************/

Sever-side: Slim PHP w/ Paris/Idorm

class Donut extends Model {}

$app->post('/donut', function() use ($app) {

    $donuts = Model::factory('Donut')->create();

    /* EDIT: Works... but not the Slim way
    $parameters = json_decode(file_get_contents('php://input'), true);
    $donuts->name = $parameters['name'];
    $donuts->sparkles = $parameters['sparkles'];
    $donuts->creamFilled = $parameters['creamFilled']; */

    /* SLIM: Using Slim Request Object */
    $requestBody = $app->request()->getBody();  // <- getBody() of http request
    $json_a = json_decode($requestBody, true);
    $donuts->name = $json_a['name'];
    $donuts->sparkles = $json_a['sparkles'];
    $donuts->creamFilled = $json_a['creamFilled'];

    $donuts->save();

    // echo json_encode($parameters); // Prove you've captured POST data, send it back
}

Now my code is happily using the default settings of Backbone.js (no changes to sync) and sending proper model attribute information to my server which seems to be successfully accepting the data and saving it to my DB.

The key here seems to be this line...

/* $parameters = json_decode(file_get_contents('php://input'), true); */
// EDITED: getBody() method not documented in Develop Doc, only Stable @ time of post

$requestBody = $app->request()->getBody();
jmk2142
  • 8,581
  • 3
  • 31
  • 47
  • You are a genious! for that a plus for the question + answer. Thanks! – P.M Aug 19 '12 at 04:20
  • Hi orangewarp, I have been struggling with the same issue here using Backbone and Slim. I find it very strange though that you have to use getBody() while it is more appropriate to use post() or put() depending on your request. – Maarten Nov 23 '12 at 12:29
  • You can have Slim parse the JSON for you. You'll still need to call getBody(). The request's post() method doesn't honor the parsing that the ContentType middleware did, and appears to only deal with form data. After instantiating $app, do this: $app->add(new \Slim\Middleware\ContentTypes()); // Get incoming JSON parsed. After that, getBody() returns a nice array of parsed data. – Graham Perks Dec 12 '12 at 20:07
1

If you want to know "what exactly is sent to the server", you should have a look at the Backbone.sync function in Backbone's code. It is very well documented, step-by-step. Then, the cleanest way to achieve what you need is to write you own sync function, inspired by Backbone's sync.

Also, a quick way to see what is sent to the server is to use your browser debug console (Network tab). You can compare here what is sent by Backbone vs. what is sent when you use $.post directly. Please post this information if you need more help !

Blacksad
  • 14,906
  • 15
  • 70
  • 81
  • I'll reread the Backbone.sync documentation. My understanding thus far is that it already has a default behavior, that I want to understand! :-) From what I understand, it takes my model attributes and serializes them into JSON, sends it, and it expects a JSON in return from the server if I make changes to the object. Using my posted example, when I do... bostonCream.save(); I expected something like... {"name":"Bawston Creme", "sparkles":true, "creamFilled":true} to be sent. If this were true, it should work with my above code but it doesn't. Suspect! – jmk2142 Jan 29 '12 at 23:18
  • Also, with regard to using console. With jQuery's $.post there is a success or failure callback that I can use to console.log(data). I can echo $_POST and see what is being captured or not. But with Backbone, I'm not sure how to see my echo $_POST. bostonCream.save() will send the request and return... something? Would something like this be the correct way to check out what is being returned? bostonCream.save({ }, {success: function(model, response) {console.log('SUCCESS'); console.log(response); }, error{ console.log('FAIL'); console.log(response); } }); – jmk2142 Jan 29 '12 at 23:35
  • Sorry I meant "read the Backbone Source code" (I posted a link to the documentation, my fault). In backbone.sync you'll see what happens. – Blacksad Jan 30 '12 at 16:39
  • With regard to console, what browser are you using ? Try Chrome, there is a "Network" tab. You'll see the call and the response. – Blacksad Jan 30 '12 at 16:40
  • Cmd+Alt+J! I love Chrome. :-) By the way, your hint to check out those other tools really helped. Thank you. I only used the console up until now but the network tool is quite handy. As a result, I realized I need to learn more about HTTP. Right now I'm struggling with how to capture the PUT data on my Slim backend. The techniques that worked for POST don't seem to be doing it. Backbone correctly PUTs the model to the backend. Request Payload = {"id":"5","name":"Aardvark"}. But my backend is just grabbing air: json_decode(file_get_contents('php://input'), true) No worky. Hints? – jmk2142 Jan 30 '12 at 17:04
0

backbone sends json data to your php backend server, which you should expose your RESTful api to respond to http verb like get, post, put, delete and etc.

your backend api is responsible for communicating with database.

I am not sure about SLIM PHP. it seems to handle the request. Can you paste the error messages?

dvliman
  • 606
  • 2
  • 7
  • 13
  • I've updated the above code with some more error messages after testing. I'll do some more reading like @Blacksad suggests. – jmk2142 Jan 30 '12 at 00:01