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

November 14th, 2017 • 9 min read
When you have an idea for a webapp it's easy to get lost into neat little features. But do you need all of them straight away? How do you define your Minimal Viable Product
September 18th, 2017 • 2 min read
We usually focus on getting the homepage Pagespeed to 90+, but what about the rest of the pages? I made a tool to help with this.
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