QCubed and JSON
As I wrote here, QCubed is a full featured and productive PHP framework that I use often. I'd like to share with you a customization that I did to allow the easy conversion of QCubed objects into serialized JSON representations.
As you may know, recent versions of PHP have a json_encode and json_decode function. This works for many uses, however, a main limitation is that it will not serialize private or protected variables defined in objects. Since QCubed objects have properties defined as protected and private, json_encode($SomeObject) returns an empty JSON string.
We could fix this easily in the subclasses of our QCubed-generated data, however, I am going to show you a way to customize the codegeneration templates so that all code-generated data classes will have associated JSON helper functions. Then I will give an example of how one might use this in a PHP application.
Customizing the Templates
First, you need to know where the QCubed keeps its code generation templates.
System templates are in:
/includes/qcodo/_core/codegen/templates/
and you should put customizations in:
/includes/qcodo/codegen/templates/
If a template exists in the customizations (non _core) directory, it will ovverride the similarly named file in the _core directory.
So do the following, starting from your Qcubed wwwroot:
# cd includes/qcodo/codegen/templates/ # mkdir db_orm # cd db_orm # mkdir class_gen # cd class_gen # cp ../../../../_core/codegen/templates/db_orm/class_gen/_main.tpl .
open _main.tpl and change line 21 to read:
class Gen extends QBaseClass implements IteratorAggregate {
Now, add this around line 86:
<%@ json_methods('objTable'); %>
The complete file is attached below in case you have trouble making the edits.
The second edit instructs QCubed to read in another template file, so we need to create it.
We will make it in the same directory as our new, edited _main.tpl. It will be called 'json_methods.tpl'
Here is the complete code for this file, and the file itself is attached at the end of the post.
////////////////////////////////////////
// METHODS for JSON Object Translation (Clifford T Meece)
////////////////////////////////////////
// this function is required for objects that implement the
// IteratorAggregate interface
public function getIterator() {
///////////////////
// Member Variables
///////////////////
<% foreach ($objTable->ColumnArray as $objColumn) { %>
$iArray['<%= $objColumn->PropertyName %>'] = $this-><%= $objColumn->VariableName %>;
<% } %>
return new ArrayIterator($iArray);
}
// this function returns a Json formatted string using the
// IteratorAggregate interface
public function getJson() {
return json_encode($this->getIterator());
}
Now, after you run another codegen, every Data Class will have two methods:
$object->getIterator()
and
$object->getJson()
getIterator will return an array of object properties and getJson() will return a JSON encoded string of those properties.
Example
Here is an example from a recent project of mine. I had the need to load up an array of Maps and send them to the client serialized as JSON. This code snippet shows how that might be done:
require('../../qcubed/includes/prepend.inc.php');
$maps=Map::LoadAll();
print "";
echo "These are the JSON encode objects:";
echo "\n";
foreach ($maps as $map) {
echo $map->getJson();
echo "\n";
}
// You might need to do the following, depending on how you want to send
// on how you want to send your data. This loops through the objects
// creating arrays first, and then json encodes the lot.
foreach ($maps as $map) {
$array[]=$map->getIterator();
}
echo "\n";
echo "These is the JSON encoded collection:";
echo "\n";
echo json_encode($array);
print "";
The output from the script above looks something like this:
These are the JSON encode objects:
{"Id":"1","Title":"My First Map","Description":"The very first map in the cortex","Created":null,"Modified":"2009-03-29 10:53:47","Thumb":"889fe4f8fe3eaf636c71982049804793","UserId":"1"}
{"Id":"2","Title":"Second Map","Description":"Second Map in the Cortex","Created":null,"Modified":"2009-05-28 21:33:19","Thumb":"2873859513f5967ca66906a0cdf6e77c","UserId":"1"}
{"Id":"3","Title":"test","Description":"test","Created":{},"Modified":"2009-06-08 21:05:50","Thumb":"blank","UserId":"1"}
{"Id":"4","Title":"new%20Map","Description":"Test%20this%20mofo%21","Created":{},"Modified":"2009-06-08 22:33:09","Thumb":"blank","UserId":"1"}
{"Id":"5","Title":"again with","Description":"the map!","Created":{},"Modified":"2009-06-08 22:34:27","Thumb":"blank","UserId":"1"}
{"Id":"6","Title":"bring it bitch","Description":"get some!","Created":{},"Modified":"2009-06-09 00:15:30","Thumb":"blank","UserId":"1"}
{"Id":"7","Title":"test again","Description":"do it!!","Created":{},"Modified":"2009-06-09 00:23:48","Thumb":"blank","UserId":"1"}
{"Id":"8","Title":"this is the shit","Description":"you're the man now Dog!","Created":{},"Modified":"2009-06-09 11:48:37","Thumb":"blank","UserId":"1"}
These is the JSON encoded collection:
[{"Id":"1","Title":"My First Map","Description":"The very first map in the cortex","Created":null,"Modified":"2009-03-29 10:53:47","Thumb":"889fe4f8fe3eaf636c71982049804793","UserId":"1"},{"Id":"2","Title":"Second Map","Description":"Second Map in the Cortex","Created":null,"Modified":"2009-05-28 21:33:19","Thumb":"2873859513f5967ca66906a0cdf6e77c","UserId":"1"},{"Id":"3","Title":"test","Description":"test","Created":{},"Modified":"2009-06-08 21:05:50","Thumb":"blank","UserId":"1"},{"Id":"4","Title":"new%20Map","Description":"Test%20this%20mofo%21","Created":{},"Modified":"2009-06-08 22:33:09","Thumb":"blank","UserId":"1"},{"Id":"5","Title":"again with","Description":"the map!","Created":{},"Modified":"2009-06-08 22:34:27","Thumb":"blank","UserId":"1"},{"Id":"6","Title":"bring it","Description":"get some!","Created":{},"Modified":"2009-06-09 00:15:30","Thumb":"blank","UserId":"1"},{"Id":"7","Title":"test again","Description":"do it!!","Created":{},"Modified":"2009-06-09 00:23:48","Thumb":"blank","UserId":"1"},{"Id":"8","Title":"this is the it","Description":"you're the man now Dog!","Created":{},"Modified":"2009-06-09 11:48:37","Thumb":"blank","UserId":"1"}]
Future Enhancements
While this code works well, I would like to see a few enhancements:
- Make it easier/more intuitive for JSON encoding collections. Many times in QCubed, you will have an array of objects. It would be nice to call a single function on that array and get a JSON encoded array back
- Right now, I am ignoring 'child' objects. I haven't experimented with children objects yet with my technique, but it should work in theory. It would be nice to have options to include/exclude children objects
- Many web applicaitons use the Flickr Rest API as a model for their own web apps. It should be quite easy to expose the Qcubed object methods as RESTful API methods, so that an entire JSON delivering Web API is codegenned along with the Data Classes. Heck, even API docs could be generated at the same time. That would be a good way to bring some of the features of SOAP to RESTful JSON.
| Attachment | Size |
|---|---|
| _main.tpl | 2.3 KB |
| json_methods.tpl | 745 bytes |





This looks great! Thanks for
This looks great! Thanks for the submission.
As an idea for determining which children to send, I suggest checking the child objects themselves and sending the ones that aren't null (ie: have already been expanded / loaded). That way you never cause an additional DB hit, and gives developers the option of expanding to fill in the ones they want.
Good point...
...about the children, but I was actually more concerned from a security perspective. In other words, there are times when you won't want a 'User' object returned even if there is a fk relationship defined.
This problem exists in the QSoapService as well, I believe.
QGoogleMap and JSON
This is going to help out a lot. I was looking for exactly this same sort of functionality to go with the QGoogleMap plugin in order to load markers asynchronously. I am surprised I didn't notice this earlier. This will save some time for sure. Thanks. I will send you any adjustments I may end up making.
Post new comment