Format associative JSON to work with Knockout.js

Posted May 9th, 2012 • 4 min read

I recently started creating a RESTful API in CakePHP to work with a Knockout.js frontend.

While Knockout.js is a lot of fun, it does expect your JSON to be in a certain format.

Take the following response from a simple find action:

{
Projects: [
  {
      Project: {
          id: 151,
          title: "Een ander project",
          slug: "een-ander-project",
          description: "dsfdsfs",
          tasks_count: 2,
          tasks_backlog: 2,
          tasks_open: 0,
          tasks_closed: 0,
          duedate: "2012-04-09",
          created: "2012-04-09 13:52:19",
          modified: "2012-04-09 13:52:19"
      },
      "Task": [ ]
  },
  {
      "Project": {
          "id": "152",
          "title": "Een ander project",
          "slug": "een-ander-project-1",
          "description": "dsfdsfs",
          "tasks_count": null,
          "tasks_backlog": null,
          "tasks_open": null,
          "tasks_closed": null,
          "duedate": "2012-04-09",
          "created": "2012-04-09 13:55:30",
          "modified": "2012-04-09 13:55:30"
      },
      "Task": [ ]
  }
}

This is fine to work with in your typical View, but Knockout rather has a nested format, and doesn't like the leading Project nodes. You could write custom parsers in Knockout, but would quickly become a hell to maintain.

Instead, I wrote a little function to reformat the response to get the result Knockout likes:

public function formatResponse($data) {
    $ret = array();
    foreach($data as $key) {
        $keys = array_keys($key);
        $t = $key[$keys[0]];
        for($i=1; $i<count($keys); $i++) {
            $t[Inflector::pluralize(strtolower($keys[$i]))] = $key[$keys[$i]];
        }
        $ret[] = $t;
    }

    return $ret;
}

$projects = $this->Project->find('all');
$projects = $this->formatResponse($projects);
$this->set(compact('projects'));
$this->set('_serialize', array('projects'));

This will reformat the Projects response to:

"projects": [
    {
        "id": "151",
        "title": "Een ander project",
        "slug": "een-ander-project",
        "description": "dsfdsfs",
        "tasks_count": "2",
        "tasks_backlog": "2",
        "tasks_open": "0",
        "tasks_closed": "0",
        "duedate": "2012-04-09",
        "created": "2012-04-09 13:52:19",
        "modified": "2012-04-09 13:52:19",
        "tasks": [ ]
    },
    {
        "id": "152",
        "title": "Een ander project",
        "slug": "een-ander-project-1",
        "description": "dsfdsfs",
        "tasks_count": null,
        "tasks_backlog": null,
        "tasks_open": null,
        "tasks_closed": null,
        "duedate": "2012-04-09",
        "created": "2012-04-09 13:55:30",
        "modified": "2012-04-09 13:55:30",
        "tasks": [ ]
    },

This way I don't have to worry about modifying the core of how Knockout.js works with JSON, and focus on developing the front-end of my app instead.

It will probably be classier to put the function in something like an afterFind, but for now it will do nicely.

Stay up to date

Want to know when a new post comes out and stay in the loop on tips, tricks and gotchas? Consider signing up for the Mindthecode newsletter.

Comments

Keep reading

June 3rd, 2014 • 3 min read
Following up on my previous post I got a few questions on how to create modules for your app. Let me show you.
April 14th, 2011 • 3 min read
I heard a lot about SASS, mostly a lot of people raving about how it was the best thing since chocolate.
December 6th, 2011 • 2 min read
Loading external assets with curl is not only easy, it's also a lot faster than file_get_contents