18
Sep
09

Data Transport with JSON-RPC via Ext.Direct and Zend Framework Part 3

Now, with the results from Part 1 and Part 2, let’s take a deeper look into the Ext.Direct Providers to see how they work and what to do.

Prepareing a JSON Service Map Description

To use the Ext.direct framework we need to register a provider. For a remoting provider we need a service mapping description.

Ext.Direct.addProvider(Ext.apply(Ext.app.JSONRPC_API, {

    'type'     : 'zfprovider',
    'url'      : Ext.app.JSONRPC_API.target
}));

Ext.app.JSONRPC_API holds the service mapping description which in the Tine 2.0 case we deliver it with our registry data, but you can also fetch it e.g. by a javascript include as suggested in the “EXT.DIRECT REMOTING SPECIFICATION”.

<script src=”index.php?method=Tinebase.getServiceMap” type=”text/javascript”></script>

The Service Map could be generated like this:

$server = new Zend_Json_Server();

// set all classes you want to expose
$server->setClass('MyModule_Service_Json', 'MyModul');

$server->setTarget('index.php')
    ->setEnvelope(Zend_Json_Server_Smd::ENV_JSONRPC_2);

$smdArray = $server->getServiceMap()->toArray();
// save some bytes
unset($smdArray['methods']);

echo "Ext.app.JSONRPC_API = " . Zend_Json::encode($smdArray);

Ext.ux.direct.ZendFrameworkProvider

To Enable Ext.Direct to understand the JSON-SMD data and also communicate using the JSON-RPC protocol we create an own provider:

Ext.ux.direct.ZendFrameworkProvider = Ext.extend(Ext.direct.RemotingProvider, {

...

At first we need to parse the JSON-SMD and to create the stubs. This is done by overwriting the initAPI method:

// private

    initAPI : function() {
        for (var method in this.services){
            var mparts = method.split('.');
            var cls = this.namespace[mparts[0]] || (this.namespace[mparts[0]] = {});
            cls[mparts[1]] = this.createMethod(mparts[0], Ext.apply(this.services[method], {
                name: mparts[1],
                len: this.services[method].parameters.length
            }));
        }
    },


All we do is to transform the JSON-SMD definition data in to a from the original createMethod method can understand to create the stubs.

It’s important to understand, that the createMethod creates stubs which do only trigger further processing of the provider and do _NOT_ contain all the code to do requests and protocol processing.

Once a stub is called, it applies the doCall function of our provider. In order to support named parameters we inspect the call and transform a named parameter call into a positional parameter call. This is needed, cause the ZendFrameworks Zend_Json_Server is itself not able to understand named parameter calls witch by the way are indeed part of the JSON-RPC specification.

// private

    doCall : function(c, m, args) {
        // support named parameters
        if (args[args.length-1].paramsAsHash) {
            var o = args.shift();
            for (var i = 0; i < m.parameters.length; i++) {
                args.splice(i,0, o[m.parameters[i].name]);
            }
        }

        return Ext.ux.direct.ZendFrameworkProvider.superclass.doCall.call(this, c, m, args);
    },

After the parameters a prepared, the provider assembles the request according to the protocol definitions. This is done in the getCallData method.

// private

    getCallData: function(t){
        return {
            jsonrpc: '2.0',
            method: t.action + '.' + t.method,
            params: t.data,
            id: t.tid
        };
    },


This protocol encapsulation only deals with single transaction calls. The processing for batched calls is done elsewhere. Luckily The both protocols use the same simple outer array for batched calls, so that we don’t need to touch it.

Finally the response needs to be decoded according to the protocol and the callback function needs to get called using the decoded data. This is done in the onData method:

// private

    onData: function(opt, success, xhr) {
        var rpcresponse = Ext.decode(xhr.responseText);
        xhr.responseText = {
            type: rpcresponse.result ? 'rpc' : 'exception',
            result: rpcresponse.result,
            tid: rpcresponse.id
        };

        return Ext.ux.direct.ZendFrameworkProvider.superclass.onData.apply(this, arguments);
    }

And voila, thats it. This is our custom provider to enable Ext.Direct to use and talk pure standard JSON-PRC/JSON-SMD.

Finally let’s register the provider for lazy invocation

Ext.Direct.PROVIDERS['zfprovider'] = Ext.ux.direct.ZendFrameworkProvider;

Server Side Batched Requests

As noted in Part 2, the biggest problem with the Zend_Json_Server is the fact that it’s not capable to handle batched requests yet. To overcome this in a simple way, you can monitor the first character of an JSON request in your dispatcher and dispatch multiple request if it’s a ‘[‘. Of course this is far away from optimum, but it’s a start point till we have the feature in the Zend_Json_Server.


3 Responses to “Data Transport with JSON-RPC via Ext.Direct and Zend Framework Part 3”


  1. 1 Dr. Helga Dec 3rd, 2009 at 1:08 pm

    Very nice article.
    It works good for me, with a little exception:

    When using your provider inside an extended form component,
    the form is loosing scope when loading, and I get the error message:

    this.processResponse is not a function

    and therefor formdata won’t be updated

    To me it seems like the stuff is loosing scope in the override of the getCallData function,
    but i’m not too deep inside extjs for now to really find out (scoping in extjs seems a little tricky to me).

    But overall, very well done. Thank you for this.

  2. 2 Daniel Apr 20th, 2011 at 12:02 am

    The class works well with simple calls but no respond with Ext.data.DirectStore. The call is made and EXTJS receive the response but DirectStore dont get the result and the load/exception event is never dispached. :S

  3. 3 Cornelius Weiss Apr 20th, 2011 at 2:38 pm

    We are using this very stable in Tine 2.0. The adapter has been improved in the meantime, check the latest version out from git.tine20.org/git/tine20