Friday, March 15, 2013

Generate code with BasGeneratorBundle

Not all tasks of the programmer are fun, inspiring or exciting. Some of them just boring. But you still have to take care of those routine tasks. If you develop with Symfony2 you probably are familiar with SensioGeneratorBundle which makes your life easier by allowing you to avoid copying and pasting such things like bundles, form classes and CRUD controllers over and over again.

Though SensioGeneratorBundle copes with its task pretty well it has one drawback: there is no easy mechanism of extending generation commands with custom generation templates (aka skeletons). Since I had to create a lot of rest CRUD controllers of the same type and there were no way to generate them via SensioGeneratorBundle I decided to create my own BasGeneratorBundle.

BasGeneratorBundle is designed in such a way that it is really easy to create your own generation skeletons or extend existing ones. Generation skeletons, just like in the SensioGeneratorBundle, are twig templates.

Table of contents

Create custom skeleton

For the sake of example imagine that you have the following controller in your bundle:
Skeletons are stored inside Resources/bas-skeletons folder of the bundle. If your skeleton is going to be called mycustomskeleton create corresponding folder Resources/bas-skeletons/mycustomskeleton. Now let's create skeleton template for our controller and put it in mycustomskeleton folder:
Assuming that this skeleton will be generated for the entity Acme\DemoBundle\Entity\Example\SomeEntity variables namespace, entity_class, etc will be substituted with:
namespace        = Acme\DemoBundle
entity_namespace = Example
entity           = Example\SomeEntity
entity_class     = SomeEntity
bundle           = AcmeDemoBundle
There is also entity_relpath, fields, path variables available (check out built-in skeleton templates: one, two and three)

We've created generation skeleton for our controller. But how generation Command will know that it should put generated contents into Controller/Example/SomeEntityController.php file. Well, there is special twig template file for this purpose called main.twig that has to be put into skeleton folder:
skeleton.start('someFile.php') says that everything after this macro and up to skeleton.end() macro will be put into someFile.php file. Though there is only one file in my main.twig example you can include there as many templates as you want. Just don't forget to mark scope of the file with skeleton.start and skeleton.end macros.

Running command:
$ app/console bas:generate:using-entity

The Entity shortcut name: AcmeDemoBundle:Example/SomeEntity

Skeleton name: mycustomskeleton
will generate controller from the first code listing.

Interaction with user

bas:generate:using-entity asks user only Entity shortcut and skeleton name by default. Not very flexible, right? What if more information is needed from user. For example you want to add route prefix annotation to your controller skeleton and you want to let user enter this prefix in interaction process. Well, BasGeneratorBundle enables you to do that.

There is special file called options.twig that resides in skeleton folder. So, if you want to ask user some variable (for example route prefix) add the following lines into options.twig:
skeleton.option() registers variable. And now you can use {{ route_prefix }} variable in your skeleton. And if you run bas:generate:using-entity the command will ask you the route_prefix variable:
$ app/console bas:generate:using-entity

The Entity shortcut name: AcmeDemoBundle:Example/SomeEntity

Skeleton name: mycustomskeleton

Please provide route_prefix (Route prefix to use in controller):
The skeleton.option macro has the following syntax skeleton.option(name, description, devault_value).

Easy to extend

It is really easy to extend skeletons with BasGeneratorBundle. You just use twig extension mechanism and extend main.twig and options.twig templates.

Let's say you want to create othercustomskeleton skeleton and it is almost identical to mycustomskeleton. The only thing that is different is the code inside someaction_body block of the controller.php.twig template. In order to extend mycustomskeleton put the following lines into othercustomskeleton's main.twig template:

No comments:

Post a Comment