Doctrine 2, ZendAmf, Flex and Acceleo - Part 2: generating php services from UML Doctrine 2, ZendAmf, Flex and Acceleo - Part 4: generating actionscript services from UML
Nov 17

Here comes the 3rd part of this serie about PHP/Flex code generation using Acceleo, coupled with a UML diagramming tool, Modelio.

In the 1st part, I built a prototype of Acceleo template to generate the Doctrine2 entities from a UML model. In the 2nd part, I built an Acceleo template to generate the skeletons of some php services that manage our doctrine2 entities. All based on the great work of Mihai Corlan about the implementation of a Doctrine2-ZendAmf-Flex stack. With these 2 first parts, the server side of the application has been automatically generated and supplemented with the implementation of the code.

It is now time to focus on the client side to replace the actionscript code generated by FB4 for the service wrappers and the value objects (code generated through the use of the Data/Service features of FB4 - very handy feature anyway ). Let’s start with the AS3 value objects.

If you have already worked on the previous parts, you can jump to the second chapter as the following one explain you how to setup materials.

Preparing materials and the environnement

Installing the example

  • Get the project code from Mihai’s post,
  • Install it (import it in FB4 if you use it),
  • Import his database.sql file into your MySQL server,
  • Update links and the db configuration in ZendAmf config and the Doctrine 2 bootstrap files to make the application work in your environnement.
  • Once the application works correctly, rename the following items:
    • the table marks2 to marks,
    • in the students table, the fields last_name and first_name to respectively lastName and firstName.
      • this is required by the code generation way, that implies to have rules.

Creating the Acceleo project

In your eclipse client (I use the Helios Eclipse PDT):

  1. Select File menu, New…
  2. Select Acceleo project
  • Define the parent folder,
  • Give a name to the 1st template: generateEntities for example,
  • Browse the metamodel URI and selected http://www.eclipse.org/uml2/3.0.0/UML.
    • If this metamodel is not present, install the UML2 plug-in for Eclipse. (I tried some other metamodels but they were not satisfying, maybe I missed something…).
  • Select the type: Class which should be the default selection,
  • Finish
  1. Switch to the Acceleo perspective

You should now have your first empty Acceleo template open. You can find more information in the Help documentation coming with Acceleo (from the Help menu of Eclipse).

Acceleo also comes with very good autocompletion capabilities and documentation (user guide and the language reference). You will find everything to start using this great tool. I was able to create a template very quickly, and just had difficulties in the tricky context of Doctrine2 mapping rules.

We will also need a folder to store the xmi model. So, in the source folder of the Acceleo project, create a ‘model’ folder.

And you can finally download the source of the template here: Doctrine2_UML_Code_Generation (300).

The UML diagram of the entities

Here is the diagram I proposed in the 1st part for Doctrine 2 entities. 

 

No change is required to get actionscript code from it. So let’s go.

Defining the value object template in Acceleo

This could be as simple as adding public properties to an Object, as you can see in all tutorials about Remoting with Flex. But actually, it wasn’t as simple as the objects passed by the php services have additional properties making them incompatible with such simple solutions. Moreover, some entities have array properties (the marks of Students) that need to be parsed  to be correctly typed. Then, the encapsulation of the properties was required and I had to define a constructor for each VO. By this way, it was possible to use the VOs directly, calling their constructor with a new operator, without changing (almost) anything in the Main.mxml file.

Actually, I defined a ValueObject class that is extended by the generated VOs. Here is the code of this ValueObject class:

package org.corlan.entities
{
   import flash.events.EventDispatcher;
 
 
   public class ValueObject extends EventDispatcher
   {
	/*
	 * PROPERTIES
	 */
 
	/**
	 * Represents the structure of the value object
	 */
	public var propertyList:Array;
 
 
	/*
	 * METHODS
	 */
 
	public function ValueObject( obj:Object = null )
	{
	   if (obj)
	   {
		var l:int = propertyList.length;
		for ( var i:int = 0 ; i < l ; i++ )
		{
		   // get the Class corresponding to the current property
		   var c:Class;
		   // if the property is an array and has an elementType defined
		   if ( propertyList[i].hasOwnProperty('elementType') )
			c = propertyList[i].elementType;
		   // in all other cases
		   else
		 	c = propertyList[i].type;
 
		   // an array is passed by the remote service for array or single entities
		   if ( obj[propertyList[i].name] is Array )
		   {	
		      // get the length of the array property
		      var proplength:int = obj[propertyList[i].name].length;
		      // if the property expects an array of specific type
		      // NOTE : this could be replaced by the usage of a vector collection
		      if ( propertyList[i].hasOwnProperty('elementType') )
		      {
			 // then parse the array property to get each item correctly typed
			 for (var k:int = 0 ; k < proplength ; k++)
			    this[propertyList[i].name][k] = new c( obj[propertyList[i].name][k] as Object );
		      }
		      // in the last case, a ValueObject is expected
		      else if ( propertyList[i].type is Object )
		      {
			 this[propertyList[i].name] = new c( obj[propertyList[i].name] );
		      }
		   }
		   // and then, for all native types
		   else
		   {
		      this[propertyList[i].name] = obj[propertyList[i].name];
		   }
	    }
      }
   }
}
}

This class is stored in the ‘entities’ folder with other AS3 VOs. It has a single property which aims to contain the structure of the VO: its properties, their types. This avoids the usage of an ObjectProxy.

The constructor makes all the job by analyzing the passed object (received from the php services). It has certainly to be modified to cover more cases (imagine that the php services could send object-hydrated results instead of arrays…).

From this point, the actionscript value objects just need to extend the ValueObject class and define their own properties, with encapsulation for arrays and their own propertyList.

To define properties, you must get all properties from the Entity class and check their type. Here is how it is done.

[for (p:Property | c.attribute->sortedBy(name))]
[if (p.visibility<>'private')]
 
[comment Arrays only are initialized/]
	private var _[p.name/]:[asVarType(p)/][if (asVarType(p)='Array')] = ['[]'/][/if];
[comment Generate getter and setter for each properties/]
	['['/]Bindable(event='propertyChange')]
	public function get [p.name.toLowerFirst()/]():[asVarType(p)/]
	{
	   return _[p.name/];
	}
[comment Define the setter even for a readOnly property./]
	public function set [p.name.toLowerFirst()/]( val:[asVarType(p)/] ):void
	{
	   if ( val && val != _[p.name/])
	   [if (asVarType(p)='Array')]
	   {
	   	var arr:Array = ['[]'/];
		var l:int = val.length;
		for (var i:int = 0 ; i < l ; i++)
		   arr['[i]'/] = new [p.type.name/]( val['[i]'/] as Object );
 
		_[p.name/] = arr;
	   }
	   [else]
		_[p.name/] = val;
	   [/if]
	}
 
[/if]
[/for]

You can see that arrays are specifically addressed. You can also notice that a specific function is used to manage the property types. This is required to map types used in our model and AS3 types. For example, the marks are defined as decimal in our model but AS3 will use Number. Here is the function:

[template public umlVarType (p:Property)]
[type.name.toLower()/]
[/template]
 
[comment We need to convert some data types /]
[template public asVarType (p:Property)]
[if (umlVarType(p) = 'integer')]
int[elseif (umlVarType(p) = 'float' or umlVarType(p) = 'decimal')]
Number[elseif (umlVarType(p) = 'edate')]
Date[elseif (p.upperBound() = -1)]
[comment If the upper bound is *, we set an Array /]
Array[else]
[umlVarType(p).toString().toUpperFirst()/][/if]
[/template]

To define the propertyList in the VO constructor, you parse again the Entity properties:

public function [c.name/]( obj:Object = null )
{
   [comment Define the propertyList from each public class properties /]
   propertyList = [for (p : Property | c.attribute->sortedBy(name)) before ('[') separator (',nttttttt') after('];')][getPropertyDefinition(p)/][/for]
 
   super(obj);
}

The getPropertyDefinition function just builds the correct object with its properties:

[comment Set the appropriate property object definition in the propertyList /]
[template public getPropertyDefinition(p : Property)]
{name:'[p.name/]', type:[asVarType(p)/][if (asVarType(p)='Array' and p.upperBound() = -1)], elementType:[p.type.name/][/if]}
[/template]

Note that there is a trick for arrays of entities (the marks of the students). An elementType is added to be used by the super constructor to set the correct type to each array item.

Generating the code of value objects

Like in previous template, I have partially hardcoded the path were file will be generated (the use of a better package name would more elegant, replacing . by / for example).

[file ('/org/corlan/' + c._package.name + '/' + c.name + '.as', false, 'UTF-8')]
package org.corlan.[c._package.name/]
{
...
}

You must define a new Acceleo main module:

  1. In the source folder, create a new Acceleo Main Module file.
  2. Give it a name (generateAS3VO should be fine)
  3. In the second step, check the template we have defined ‘generateAS3VO‘: this will automatically add it to the body of the main file.
  4. Finish

You are now ready to generate the actionscript value objects of Mihai’s example. But if you generate the code now, the application won’t work anymore. The actionscript services will generate compilation errors. So just test the template to check the result but wait for the 4th and last part of the serie to generate the actionscript service layer ;).

The last point: the marks property of Students is now an array instead of an ArrayCollection. So the saveStudent() methods of the main application has to be modified as follows, this is actually the only change required in the Main.mxml file: (the replaced lines are commented)

private function saveStudent():void {
   var m:Mark;
   //student.marks = new ArrayCollection();
   student.marks = [];
   for (var i:int = 0, l:int = marks.length; i

Conclusion

In this part, I show you how to generate actionscript value objects from an UML entity diagram, exactly the same used to generate Doctrine2 entities. A specific ValueObject class has been defined and can be extended by the value objects generated from the model. The data types have been also converted to some AS3 compatible types. The 4 actionscript value objects are generated completely by Acceleo, no additional code is required (in the context of the sample application).

As said ealier, I think this template is a good starting point for further works and refinements. If the model changes, it will be very easy to update the value objects with a new generation run. You can also see that the code is more simple than the FB4 generated code (from my own perspective), even if you still can’t see it work. So, to be continued…

Flattr this

Related posts

Written by Arnaud
Creative Commons License
Tags: , , , , , ,

Share/Save/Bookmark

Help me improve my blog by rating this post or sending a comment.

not goodquite goodgoodvery goodexcellent (1 votes, average: 1 out of 5)
Loading ... Loading ...


Comments