Upgrading from Symfony vPR5 to bleeding edge

Posted on 20th February, 2011 | Tagged:

Some background

We are working on a new Symfony2 project (by new I mean I have been working on it for 2 months now), but it is slowly coming to fruition.

I began in December and cloned the Symfony 2 vPR4 branch from the main Symfony GIT repository at github.com/symfony/symfony. About a month ago I upgraded from vPR4 to vPR5 and spent about 4-5 hours making everything work, and a month later, I thought it was a good time to upgrade to vPR6, but then had a better idea and decided to upgrade to the bleeding edge repository at github.com/fabpot/symfony.

  • This repository always has the latest changes
  • I dont have to worry about it unexpectedly breaking things, as GIT uses a detached head_, and I will not automatically get the latest changes when I git pull.
  • Updating the rapidly evolving framework is easier as I can upgrade frequently instead of waiting for the next release. (and hope that most changes are not reverted :) )

Upgrading the repositories

First I updated the Symfony 2 submodule::

# cd to the Symfony submodule dir, in my case ...
cd src/vendor/Symfony
git checkout origin/master

you should get a message like the following::

HEAD is now at 9b15b69... [AsseticBundle] Sort Twig assets by name before loading for filesystem-independent results

So great, we now have Symfony up-to-date. But Symfony is not the only third-party code that we use, so I had to up date the following submodules also::

cd src/Bundle/FOS/UserBundle
git checkout origin/master

cd src/Bundle/DoctrineExtensionsBundle
git checkout origin/master

Perfect. Now everything is up-to-date except our code base!

Upgrading the code base

OK, the last part was easy. The next chunk of my day was spent blindly adapting the code integration to work with the latest version of Symfony and the other third-party libs. I say blindly as really I had no idea how long this would take and where the changes were made. In hindsight checking the GIT commit history should have given me a good idea. So as is usual for me in infomatics, what I thought would take an evening took over 10 hours in total :)

About 3 hours in I had the idea of writing this blog post and so started keeping note of the changes I was making.

Naming convention change for templates

Following a prolonged debate on the symfony-devs mailing list it was finally decided to change the mytemplate.format.engine (e.g. mytemplate.html.twig, or mytemplate.json.php) back to mytemplate.engine.format (as was the case in vPR4.

Fortuneatly this is actually pretty easy using the Linux BASH shell::

for i in `find . -type f -name "*twig.html*"`; do git mv $i `echo $i | sed 's/twig.html/html.twig/g'`; done;

The above, in one line, iterates over all the filenames that are output by the find command, and performs a git mv operation on each moving (renaming) it using a combination of echo and sed.

Then we just need to rename all the references inside the files, for this I used the rpl tool, because its easier then anything else::

rpl '.twig.html' '.html.twig' ./ -R

All done.

Resources in routing files now prefixed with @

So in our case::

crm:
    resource: CrmBundle/Resources/config/navigation.xml
        type: navigation

Became::

crm:
    resource: @CrmBundle/Resources/config/navigation.xml
        type: navigation

DIC - Configuration Loading

Dependency injection has become somewhat sexier since vPR5, and during the upgrade process I managed to improve our use of the DIC (Dependency Injection Container), which I will talk about later.

The first thing I noticed is that the syntax no longer has the mybundle.config: format, where the previous example would cause the execution of function loadConfig and the following, mybundle.foo would cause the execution of function loadFoo. Now there is just mybundle: and all configuration given thereafter is passed to a single load method.

DIC - Kernel Listener Tag

The kernel.listener tag used to cause the referenced class's register method to be called with the EventDispatcher so that it could register itself to the event dispatcher and so do stuff with events, this event has now changed to be more explict and eliminates the need for the register method::

$container->register('some.service', 'Vendor/SomeBundle/SomeClass')
  ->addTag('kernel.listener', array('event' => 'someevent', 'method' => 'foo'));

So the new syntax explicitly passes the specified event to the specified method, which is cool, unfortunately we used this listener for another purpose, so it no longer worked for us.

We originally used this listener to kick-start our CMS's extension system by allowing extensions to tune-in to the kernel,listener event and register themselves with the CMS's extension_manager. Fortunately I found a better way to do this using a compiler pass.

DIC - Implementing compiler pass

So basically, a compiler pass is a way to modify the DIC after it has been initialized, which enables us to add our CMS extensions directly in the DIC by explicitly adding extensions to the extension_manager Definition. Or another way to put it would be that the DIC creates a template class configuration and we can add the extensions directly to this template::

// src/Vendor/SomeBundle/DependencyInjection/Compiler
class RegisterCoreExtensionsPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        $extensionManager = $this->container->getDefinition('yprox.extension_manager');

        $serviceIds = $this->container->findTaggedServiceIds('yprox.extension');
        foreach ($serviceIds as $serviceId => $attributes) {
          $extensionManager->addMethodCall('addExtension', array(new Reference($serviceId)));
        }
    }
}

So you can see we instruct the DIC builder to add a method addExtension for each service definition tagged with yprox.extension. Now, when we call the extension.manager service from within the application it will automatically add all these extensions.

I like it.

Forms -- Lots of changes

The form framework has undergone some significant API changes, which I like also.

Forms -- Factory Method and Form Context

The way to instantiate forms has changed from::

$form = new MyForm('myform_name', $data, $this->get('validator'), array('option1' => 'foo', etc...));

to::

$form = MyForm::create($this->get('form.context'), 'myform_name', array('option1' => 'foo'));

So, firstly it should be noted that everything is an option now, even the validator. So with this in mind the first argument of the factory method is a reference to a service which provides default options, including the validation service and the csrf stuff. The options in the third argument are merged with these contextual options.

Secondly, you might have noticed that the $data argument is missing, this has been moved (as standard) to the bind method, which is now more funky::

$form->bind($this->get('request'), $data);

By passing the request object directly the age-old Symfony form practice of if ('POST' === $request->getMethodName()) { // bind form, validate, etc ... } is no longer needed and we can simply call isValid inline::

// my controller
$myentity = $this->getMyEntity();
$form = MyForm::create($this->get('form.context', 'myform_name'));
$form->bind($this->get('request'), $myentity);

if ($form->isValid()) {
  $this->get('session')->flash('message', 'Entity is Valid.');
}

Forms - Removal of Doctrine value transformers

The value transformers for doctrine, EntityToIDTransformer, CollectionToChoiceTransformer and another which I forget the name of were removed from the source code because they were buggy, unfortunately they are used quite heavily in our project so I had to reimport them, I could only get them working by modifying the Core library, which is far from ideal, but I hear that they will be reimplemented properly soon ...

Security is now in its own bundle!

Whilst the Security component rests in the core the framework integration has been moved from the FrameworkBundle to SecurityBundle.

At last

It took a while and I have not covered many things, but there are many changes for the better from vPR5 to latest, and it has, I think, been a real community effort. I have been following the symfony-devs mailing list closely and it has been really good to see progress in action.

I think Symfony 2 will arrive!

.. _head: http://sitaramc.github.com/concepts/detached-head.html

Comments

Be the first to leave a comment.

Post new comment


type "i hate spam" in UPPER CASE

Tags

10 Latest Items