Qcubed and Json

Login or register to post comments
19 replies [Last post]
cliff's picture
Offline
Joined: 04/11/2008

Hi all,

I have written a story on my blog about how to customize the code generation templates to get some quick JSON functionality.

The story is here:

http://cliffordmeece.com/content/qcubed-and-json

Essentially, the new templates let you call $someObject->getJson() and get back a JSON serialized string representaiton of your Qcubed object.

Hope you find it useful. I have more along the lines of this in the pipes.

MikeHostetler's picture
Offline
Joined: 01/09/2008

This is great Cliff! I'm personally going to push this into the core templates, I believe that JSON export would be extremely helpful to have in CORE.

Keep up the good work!

cliff's picture
Offline
Joined: 04/11/2008

I know you've done some good work with a RESTful version of Qcubed, but I think I'll take another stab at it.

The idea is to automatically code-generate a RESTful API modeled after the Flickr API. Essentially the FlickR API doesn't necessarily map URLs to objects like some REST services, but it does expose an endpoint and then has a ton of methods to call at http://someurl/service/rest/?method=some.method%parameter=someValue

So the idea is that I can create a QRestService class that when instantiated exposes all the public methods of the classes of QCubed and returns JSON ( and XML in the future) objects.

So you'd be able to call http://yourdomain.com/services/rest/?method=people.LoadAll

and get back a JSON array of People. It will work similarly for every other public method. Additionally, I would like the service to codegen the API documentation for the functions at the same time, so you could have a page like this auto generated:

http://www.flickr.com/services/api/

alex94040's picture
Offline
Joined: 11/06/2008

cliff, this is extremely interesting work, I too would love to see your codegen templates in the core. Would you be willing to create a patch against the SVN version of QCubed 1.1 with your modifications?

cliff's picture
Offline
Joined: 04/11/2008

Sure...Actually, I think I might have core contributor status ( I wrote most of the qdrupal module ). If I don't, I'll make a patch.

cliff's picture
Offline
Joined: 04/11/2008

Hi, I committed them to the 1.1 branch. I created a ticket first, but I didn't go through the whole patch-code-review cycle. Hope that's alright for such a simple change. I'll read up on the correct process for the next contribution.

alex94040's picture
Offline
Joined: 11/06/2008

Cliff, can you talk about why the getIterator() function is there?

cliff's picture
Offline
Joined: 04/11/2008

getIterator is required for any class that implements IteratorAggregate.

I chose to use the IteratorAggregate function because I saw that used in several places as the recommended solution to get a JSON encoded string from an object that has private and protected properties. I suppose it is possible to build the Json string in a more piecemeal way, but this seemed easiest. An additional bonus is being able to easily get a PHP array, if you don't want JSON.

I tested the code against the examples site DB and it seems to work fine. At the moment, it only does properties and not associated objects (but it does get their ID's). So for example, running getJson on the 'Project' object from the examples site DB yields something like this:

{"Id":"1","ProjectStatusTypeId":"3","ManagerPersonId":"7","Name":"ACME Website Redesign","Description":"The redesign of the main website for ACME Incorporated","StartDate":{},"EndDate":{},"Budget":"9560","Spent":"10251"}

alex94040's picture
Offline
Joined: 11/06/2008

Thanks a bunch, Cliff, it makes perfect sense.

Do you think you can write a simple example demonstrating the JSON functionality? That would make a huge difference in the adoption of the feature. As you remember back from the QCodo days, folks were constantly surprised: "what, this feature is there??" because it wasn't documented in examples. We're trying to change that in the QCubed world. Can you help with a JSON example?

cliff's picture
Offline
Joined: 04/11/2008

sure. Anything in particular you'd like to see? It's easy enough to write an example that returns JSON, but do you want something 'done' with it on the client side using QCubed as well? For my projects, I'm returning JSON so that I can use it with other client side languages like Objective-J (cappuccino) and Flex.

MikeHostetler's picture
Offline
Joined: 01/09/2008

This is really great Cliff. I don't think there's a need to make your example implement a use-case, just some basic docs on how to access the JSON stuff so we can put it on the examples site, http://examples.qcu.be.

Thanks!

Steven Warren's picture
Offline
Joined: 11/06/2008

Cliff, if you don't mind I would like to see how you have used your JSON mod with Flex. I have tried in the past to generate ActionScript VO's with Qcubed's codegen to use with Flex, but I think your method of passing objects using JSON would work better.

cliff's picture
Offline
Joined: 04/11/2008

no problem, I'll write an example. I have it working pretty well.

cliff's picture
Offline
Joined: 04/11/2008

I'll write something a bit more detailed later, but I simply do this:

I have a php file that looks like this:

<?php
require('../../qcubed/includes/prepend.inc.php');

$method = $_GET["method"];

  switch ($method) {
      case "dendrite.maps.getList":
          $maps=Map::LoadAll();
        foreach ($maps as $map) {
        $array1[]=$map->getIterator();
        }
        echo json_encode($array1);
          break;

Snip. Case statement goes on to serve other methods>

In flex, I do this:

private function init():void {
mapsGetListService = new HTTPService();
mapsGetListService.url="http://dendrite.dev/services/rest/?method=dendrite.maps.getList";
mapsGetListService.method = "GET";
mapsGetListService.addEventListener("result", mapsGetListServiceResult);
mapsGetListService.addEventListener("fault", mapsGetListServiceFault);
var params:Object = new Object();
params.method = "dendrite.maps.getList";
mapsGetListService.send(params);
}

and then this:

private function mapsGetListServiceResult(event:ResultEvent):void {
var rawData:String = String(event.result);
var arr:Array = (JSON.decode(rawData) as Array);
maps = new ArrayCollection(arr);
mapTileList.dataProvider = maps;
}

public function mapsGetListServiceFault(event:FaultEvent):void {
var faultstring:String = event.fault.faultString;
Alert.show(faultstring);
}

'mapTileList' is a TileList:

<mx:TileList id="mapTileList"
width="100%"
height="100%"
rowHeight="200"
columnCount="3"
allowMultipleSelection="false"
backgroundColor="#545454"
borderStyle="inset"
verticalScrollPolicy="on"
    itemClick="launchMap(event);"
itemRenderer="components.MapItem" borderColor="#747576">
</mx:TileList>

and here you see I have a custom item renderer. It is pretty simple:

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
horizontalAlign="center"
verticalAlign="middle"
verticalGap="0"
width="80"
height="150"
paddingRight="5"
paddingLeft="5"
paddingTop="5"
paddingBottom="5"
>
<mx:Canvas width="150" height="150" backgroundColor="#545454">
<mx:Image
    height="150" width="150"
    source="/thumbs/{data.Thumb}.png" 
    mask="{maskCanvas}"
    scaleContent="true">
    <mx:filters>
        <mx:DropShadowFilter />
</mx:filters>
    </mx:Image>

<mx:Canvas x="0" y="0" width="150" height="150" backgroundColor="#ff0000" id="maskCanvas" cornerRadius="15" borderStyle="solid"/>  

</mx:Canvas>
<mx:Label height="20" width="75" text="{data.Title}" textAlign="center" color="0xFFFFFF" fontWeight="normal" />
</mx:VBox>

so essentially, you create an HTTPService that calls a php script, Qcubed gets the objects in question and converts them to JSON, JSON is returned to a named listener, you decode the raw json string and cast it to an array, and you set your dataprovider to the array.

Then you can call {data.someProperty} in your control.

Steven Warren's picture
Offline
Joined: 11/06/2008

Thanks Cliff, I very much appreciate the example.

Steven Warren's picture
Offline
Joined: 11/06/2008

Cliff,

Thanks again. I had a chance to work with the code and it worked perfectly.

A hint for anyone else who may follow this thread, make sure you have the AS3 Core Library in Flex. (you can obtain it from http://code.google.com/p/as3corelib/wiki/FlexBuilderLibraryProject) You will need to import "com.adobe.serialization.json.JSON" for the JSON decoding in Flex.

For a simple test you can use the following MXML code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="data.send()">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.collections.ArrayCollection;
import com.adobe.serialization.json.JSON;

[Bindable]
private var loadedData:ArrayCollection;

private function resultHandler(event:ResultEvent):void
{
var rawData:String = String(event.result);
var arr:Array = (JSON.decode(rawData) as Array);
this.loadedData = new ArrayCollection(arr);

}

private function faultHandler(event:FaultEvent):void
{
Alert.show("Error: " + event.fault.faultString, "Application Error");
}
]]>
</mx:Script>
<mx:HTTPService id="data"
    url="http://localhost/services.php?method=service.myTest.getAll"
    result="resultHandler(event);"
    fault="faultHandler(event);"
/>
<mx:DataGrid width="100%" height="100%" id="dtgTest" dataProvider="{loadedData}">
</mx:DataGrid>
</mx:Application>

cliff's picture
Offline
Joined: 04/11/2008

yep, sorry, forgot to mention corelib.

alex94040's picture
Offline
Joined: 11/06/2008

I would be absolutely thrilled if one of you could put together an extensive example on how to integrate Flex and QCubed - that would be totally awesome (as a separate plugin, probably - the JSON one is useful in and of itself).

Steven Warren's picture
Offline
Joined: 11/06/2008

I will document what I have together once we finish this current project. Basically we made a new Qcontrol that embeds flash media using swfobject. Then we passed data to Flex/Flash using remote objects. We created new codegen templates to create ActionScript(AS) value objects that basically mirror the properties of the generated Qcubed classes. Then we imported those AS files into our flex builder project and used AMFPHP to pass data between our AS value objects and our Qcubed PHP classes.

We are currently looking at using purely JSON to pass the objects, which is why I was excited to see Cliff's JSON solution. It seems to make the process much simpler. Our only concern is in the security of the data. We have our network guys checking that out with the proof of concept we built, but as we are using SSL I do not foresee any issues using this method.

alex94040's picture
Offline
Joined: 11/06/2008

Pretty cool stuff. I admit to being as far from Flex as it gets, but I'm sure others can make sense from what you described :-) Looking forward to your writeup, I'd be thrilled to give this a shot myself sometime!