Titanium Quickie: A Simple Integration of Alloy and WordPress JSON Plugin

Simple REST integration with Appcelerator Alloy seems to be a recurring theme when looking through the QA Forums and the Alloy User’s Group. For HTML/Javascript developers, Backbone comes with default integration of jQuery to provide the http/ajax client for REST integration. If your endpoint supports the common REST verbs, then things just work.

There is a question in the forums about WordPress integration with Titanium and since WordPress provides a REST API to JSON; I took some time to try and create a simple example of how to string this all together. The question, references WordPress JSON plugin and since wordpress is pretty popular, I figured it was easy to use that instead of creating an alternate REST API service

Appcelerator’s Alloy Documentation on Sync Adapters

BacknoneJS Documentation on Sync Adapters

REST Sync Adapter

First we need a REST based adapter, there is one floating around in github that I have simplified and made more generic. The objective here is to try and stay as true as possible to the BackboneJS implementation so developers have a foundation to build upon. Also please note that this adapter is not complete and I have only implemented the features I needed to retrieve content for the purpose of this example.

So for fetching the content, we need to implement the GET http verb in out sync adapter. All the adapter should do is get the JSON response… that is it! No more, no less. The rest of the response processing should happen elsewhere. With that being said, here is what the code looks like in the adapter file restapi.js

 

    switch(method) {

        case 'delete' :
        case 'create' :
        case 'update' :
            throw "Not Implemented"
            break;

        case 'read':
            apiCall(params, function(_resp) {
                if (_resp.success) {
                    var data = JSON.parse(_resp.responseText);
                    params.success(data, _resp.responseText);
                } else {
                    params.error(JSON.parse(_resp.responseText), _resp.responseText);
                    Ti.API.error('[REST API] ERROR: ' + _resp.responseText)
                }
            })
            break;
    }

 

The apiCall function does the Titanium specific http client call to get the content.

function apiCall(_options, _callback) {
    Ti.API.debug("[REST API] apiCall ", _options);
    var xhr = Ti.Network.createHTTPClient({
        timeout : 5000
    });

    //Prepare the request
    xhr.open(_options.type, _options.url);

    xhr.onload = function() {
        _callback({
            success : true,
            responseText : xhr.responseText || null,
            responseData : xhr.responseData || null
        });
    };

    //Handle error
    xhr.onerror = function() {
        _callback({
            'success' : false,
            'responseText' : xhr.responseText
        });
        Ti.API.error('[REST API] apiCall ERROR: ' + xhr.responseText)
    }
    for (var header in _options.headers) {
        xhr.setRequestHeader(header, _options.headers[header]);
    }

    if (_options.beforeSend) {
        _options.beforeSend(xhr);
    }

    xhr.send(_options.data || null);
}

 

The complete restapi alloy sync adapter used in this post is available here

Alloy Models and Collections

So now that we have a way to talk to the wordpress server, we need to create our application specific representation of the content; we will do that using Alloy Models and Collections. We will create a model file called post.js that will be extended to support the functions needed to retrieve and process content from the wordpress server’s API response.

The Alloy sync adapter needs to know how to get the data from the server and what to do with the data response it gets back. So we need to provide the model with the appropriate information. We do that by extending the model and setting the url and the parse functions.

The Model

We need to set the alloy model configuration settings so the model knows how to connect to the adapter we provided.

    config : {
        "columns" : {},
        "adapter" : {
            "type" : "restapi",
            "collection_name" : "post"
        }
    },

 

The url function for this example will be different for models and collections. When we retrieve the model, we need to set the id and call a specific endpoint

    extendModel : function(Model) {
        _.extend(Model.prototype, {

            /**
             * when getting a model object we need to set the url with the
             * correct path and object id
             */
            url : function() {
                return "[WORDPRESS_URL]/api/get_post/?post_id=" + this.id
            },
        });
        // end extend

        return Model;
    },

 

The Collection

When we retrieve the collection, there is no need to set the id but we need to specify a different url endpoint

    extendCollection : function(Collection) {
        _.extend(Collection.prototype, {

            /**
             * when getting a collection object we need to set the url with the
             * correct path
             */
            url : function() {
                return "[WORDPRESS_URL]/api/get_recent_posts/"
            },
        });

 

So now when you execute your code to fetch a collection…

Alloy.Collections.Posts = Alloy.createCollection("post");
// fetch the data
Alloy.Collections.Posts.fetch();

 

you will get the JSON response that would look something like this in a default WordPress implementation.

{
  "status": "ok",
  "count": 1,
  "count_total": 1,
  "pages": 1,
  "posts": [
    {
      "id": 1,
      "type": "post",
      "slug": "hello-world",
      "url": "http:\/\/localhost\/wordpress\/?p=1",
      "title": "Hello world!",
      "title_plain": "Hello world!",
      "content": "Welcome to WordPress. This is your first post. Edit or delete it, then start blogging!\n",
      "excerpt": "Welcome to WordPress. This is your first post. Edit or delete it, then start blogging!\n",
      "date": "2009-11-11 12:50:19",
      "modified": "2009-11-11 12:50:19",
      "categories": [],
      "tags": [],
      "author": {
        "id": 1,
        "slug": "admin",
        "name": "admin",
        "first_name": "",
        "last_name": "",
        "nickname": "",
        "url": "",
        "description": ""
      },
      "comments": [
        {
          "id": 1,
          "name": "Mr WordPress",
          "url": "http:\/\/wordpress.org\/",
          "date": "2009-11-11 12:50:19",
          "content": "Hi, this is a comment.
To delete a comment, just log in and view the post's comments. There you will have the option to edit or delete them.\n",
          "parent": 0
        }
      ],
      "comment_count": 1,
      "comment_status": "open"
    }
  ]
}

 

The last missing piece here is to convert the response above into something BackboneJS likes to deal with. The parse function on models and collections is the method we will implement to make the JSON response useful in our application. The parse function is usually just a straight pass-thru of the JSON response, but as you can see from the listing above, there is more information there then we really need, this is where parse comes in handy. We will strip off the posts array since that is what we really want for our collection results.

    extendCollection : function(Collection) {
        _.extend(Collection.prototype, {

            /**
             * when getting a collection object we need to set the url with the
             * correct path
             */
            url : function() {
                return "[WORDPRESS_URL]/api/get_recent_posts/"
            },
            /**
             *
             * this is where you make the JSON fit your models, NOT in the adapter
             *
             * @param {Object} _resp
             * @param {Object} xhr
             */
            parse : function(_resp, xhr) { debugger;
                // return the post attributes
                return _resp.posts;
            }
        });
        // end extend

        return Collection;
    }

 

This a high level overview of how to integrate Alloy Models with a WordPress site utilizing the JSON API Plugin. This is also a good example of how to integrate a generic REST adapter into an Alloy application. The key objective of the separation of concerns is to put the functionality where it belongs. Adding the url function and the parse function to the model allows us to reuse the sync adapter for any rest based webservice.

 


WordPress to Mobile App Service

Questions? Need further assistance? Drop us a line below and we will contact you shortly to help you convert your WordPress content into a mobile application.






captcha

16 Comments on “Titanium Quickie: A Simple Integration of Alloy and WordPress JSON Plugin

  1. Why are you writing a JSON sync instead of using the default Backbone one? Can’t you just rebind the Collection’s sync method back to the Backbone.sync method?

    • No, that is not how backbone works in Alloy, there is no default sync implementation supported.

  2. It is possible… Just unhook the Alloy sync code and hook back up the Backbone sync code – you can do this in the extendModel and extendCollection methods of the Alloy Model.

    e.g.
    extendCollection: function(Collection) {
    // extended functions go here
    _.extend(Collection.prototype, {
    sync: function(){
    return Backbone.sync.apply(this, arguments);
    }
    }); // end extend
    return Collection;
    }

  3. or put the return Backbone.sync.apply(this, arguments); sync call in your own sync adapters sync method.

  4. Simon, if you really think that will work, please provide a complete example because everything you have said so far will not work based on my understanding of titanium and alloy… Which I think is pretty good…, do you have experience with either of these frameworks?

  5. Ok, I stand corrected. The problem isn’t that it isn’t possible to reconnect the Backbone.sync method. The problem is that Backbone sync defers to jQuery, Zepto or Ender for the actual Ajax work. None of which are present by default. To get it to work you have to call Backbone.setDomLibrary() passing a module which exports an ajax method. Which turns out to be about as much work as writing the sync adapter, as you have done, except that the sync adapter seems less flimsy. (Cudos to the Alloy devs for coming up with such an elegant solution.) On the flip side, the Backbone eventing seems a bit better using their sync (which I think is just a signifier of how new the sync adapter code is), and in theory the Backbone routing stuff could be leveraged by hooking Backbone’s sync back up, so there may be some merit to knowing about the technique. I spent a couple minutes putting together a demo project which uses the technique to perform a JSON twitter search: http://dl.dropbox.com/u/17869058/backboneTest.zip
    (I’ve been playing with Alloy for a couple days, hence the noob questions ;)

  6. I still think you are making some incorrect assumptions but you do you. I have spent months working with this framework and I am pretty certain that the approach I have recommended above is pretty solid

  7. Pingback: This Week in Titanium Mobile Development: 4 Feb 2013 | Titanium Development

  8. Hi Aaron,

    I’ve recently noticed the “include” content-modifying argument on JSON-API.

    http://wordpress.org/extend/plugins/json-api/other_notes/
    3.2. Content-modifying arguments

    eg. api/get_recent_posts/?include=posts,title,content

    Helps with filtering and parsing on the server side without adding new json-api controllers.

    Originally I was thinking of writing custom json-api controllers to slim down the payload but these content-modifying arguments are great for doing the job most of the time.

    Keen to draw attention to it to save others time who are also working with WP JSON-API.

    As always, love your community contributions. I’m looking forward to learning from your #tialloy book :)

    Cheers,
    Anthony

  9. So I have been trying out your code, but I have yet to get it to run without having an error.

    ‘undefined’ is not a function (evaluating ‘model.url()’) at restapi.js (line 40).

    I even created a blank testing project with only your code from the github gist (with a proper json url) and I still get this error so there seems to be some kind of problem.

  10. Aaron,
    I’ve forked this gist. I want to mod it for a more generic web service implementation. If I run into any blockers, I’d like to be able to contact you about it. But I know how busy you are. So, I want to respect your time.

    Thanks for all your contributions to the Titanium community.

    Regards,
    Mike.

    • no problem I would be curious to see the final result when you are finished. This was something I was hoping to get back to but it never happened.

  11. Hi Aaron,
    Where should the restapi.js file reside? I tried in both /lib and /modules. Either way, I get “[ERROR] Script Error Couldn’t find module: alloy/sync/restapi” which is a compile-time folder found in /Resources not in /app.

  12. Hi Aaron,

    I have gone over to git hum and downloaded your source files. I did exactly what was followed in this tutorial, but I ended up getting an error “unable to parse JSON String at restapi.js”

    I presume that this is because the string is null, either way it is not working.

    Do I have to install a word press module to get it to work? I also do not have a wordpress site to test this with – just used a random one, could provide a link to a wordpress URL which this code works with? Thanks

  13. Hi, you’ve a example project “Titanium studio” with your files inside.Sorry I don’t understand, i’m a noob :(
    Thank you very much

Leave a Reply

Your email address will not be published. Required fields are marked *


three + 2 =

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>