Doctrine 2, ZendAmf, Flex and Acceleo - Part 1: generating entities from UML Doctrine 2, ZendAmf, Flex and Acceleo - Part 3: generating actionscript value objects from UML
Nov 04

 Here is the second part of my serie about the code generation in the stack Doctrine2, ZendAmf and Flex using Acceleo and an UML diagramming tool, Modelio.

In the 1st part, I built a prototype template to generate Doctrine2 entities. This is only a prototype and an example, as you must define your own rules and policies for your own model design and template definition.

In this second part, we will continue to replace the code written by Mihai Corlan, with our generated code. We will focus now on the PHP services and as you will see, this is really simple as there is no specific rules like for Doctrine2 annotations. There are only your design rules (mine actually in this post :p ).

You can get the source of the templates here: Doctrine2_UML_Code_Generation (391). And you can get the Mihai’s code: here and refer to the 1st part to install the environment.

The approach

I envisaged different approaches to model the service layer:

  • No model.

It is possible to generate all services from the Entity model only. You can generate standard CRUD methods using the entity name for example and add a [protected] section in which you can define all specific services. You keep only one diagram to maintain but you lose the big picture of your service layer. You also encounter naming problems: with the Student class, you can’t just add an ’s’ to build the getStudents() method as it won’t work for the Country class…

  • Model complete services.

This leads for example to the following diagram which corresponds to the service layer of Mihai’s application.

In this diagram, all services are present and all their operations are listed. This is probably the best way as it really give you the most accurate view of the service layer.

  • Model just the specificities

If you look again at the previous diagram, you will notice that only the StudentsService class has specific operations. The 3 others have only a "get all entities" methods. You will also notice that most of the operations listed in the diagram are also related to CRUD applications… after all, isn’t it what we expect from such services ? they must implement all the CRUD operations to manage our entities… In this example, they are not all required but we could imagine to extend the application with a form to create, update, view and delete the courses available in the Mihai’s university ;) … So we could consider that for all services, we have standard operations and specific operations  to implement . On this basis, I could simplify the previous diagram to show only the method that I don’t consider as standard in its class container and I get this ultra simple diagram.

 

Now, to understand the diagram, you must remember that the standard CRUD methods are not shown in the diagram, but they could be generated by the Acceleo template. We could add the skeletons of saveStudent, deleteStudent methods but also some getStudent($val), searchStudentById($val), searchStudentsByName($val)…

To keep things simple and to tie to my primary objective (reuse most of the Mihai’s code), I will use the second approach, which is actually, the best practice.

Defining the service template

I hope you got time to get familiar with Acceleo.. so you must know how to create a new Module file. Name it ‘generateServices’.

You will have also to reference it in the main Module file:

  1. add an import reference
  2. below the command [c.generateEntities()/], add a line with [c.generateService()/]. The code completion of Acceleo will help you.

 For our services, after the hardcoded contructor, we parse the operations only to get their name and their parameters.

[for (op : Operation | c.getAllOperations())]
   public function [op.name/]( [op.member.name->sep(', ')/] )
   {
      //[protected (' for the ' + op.name + ' function')]
      //[/protected]
   }
 
[/for]

That’s all !

As body of the function, I only use a [protected/] section into which we can place the code. As explained in the previous chapter, it would be possible to generate other complete or partial methods according to your design choices…

To be more accurate, it is possible to use the visibility of the operation. By this way, we will get public, private or protected methods according to what is defined in the UML model. The template for services is then:

template public generateServices(c : Class) ? (c.getAppliedStereotype('LocalProfile::Service')<>null)]
[comment @main /]
[file ('/' + c._package.name + '/' + c.name.concat('.php'), false, 'UTF-8')]
?php
require_once(__DIR__.'/../bootstrap.php');
 
/**
 * Description
 * [c.ownedComment._body/]
 */
class [c.name/]
{
 
   public function __construct()
   {
      //[protected (' for the constructor')]
      //[/protected]
 
      $this->entityManager = $GLOBALS['[\'em\']'/];
   }
 
   [for (op : Operation | c.getAllOperations())]
   [op.visibility/] function [op.name/]([op.member.name->sep(', ')/])
   {
      //[protected (' for the ' + op.name + ' function')]
      //[/protected]
   }
 
   [/for]
}
[/file]
[/template]

Generating the services

You can now launch the main Module to generate services. The files will be stored in a folder called ‘services‘ (check the [file] definition at the beginning of the service template). They contains the skeleton of each method defined in the UML model. The empty protected parts have now to be filled with Mihai’s implementation… take the original code and just copy/paste it between the protection tags.

There is a point about the addNewMark method of the StudentsService. The signature generated by our template is different from the one used by Mihai. So you will need to switch the parameters either in the methods definition, either in its call in the saveStudent method.

Save the changes. Build the flex application and check it is still working (update at least one student to check the saveStudent method is ok).

Conclusion

In this part, we have generated the service layer of Mihai’s application using an UML diagram and a very simple Acceleo template. All the Mihai’s code has be reused (with very minor manual changes)  and we have also seen that with Acceleo, it was possible to generate and standardize more sophisticated services classes. It is up to you to decide how things are modeled and what the generation template must contain.

With these 2 posts, I have prototyped 2 Acceleo templates that can generate Doctrine2 entities and their associated services from a UML diagram. The whole server side is now generated and can be maintained using these templates. "Prepared" should be used instead of "generated" actually as all the code implementation has still to be manually written. But a good template should help you to focus on the main code and less on simple or repetitve parts.

The server side is done and I need now to generate the client side code to replace the FB4 generated service wrappers and values objects… so next part in a few days…

 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 (No Ratings Yet)
Loading ... Loading ...


Comments are closed.