Decoupling in Symfony How to Pull out from Annotations

Annotations bring coupling as shown in Matthias Noback’s book on “A year with Symfony” and elsewhere in many blog posts around. You realize this too late if you are a newcomer because the documentation and many bundles assumed sometime ago that annotations was a good idea for automating stuff. This is the magic that Symfony in its beginnings set out itself to pursue but did not stick to as we are seeing lately in some developments. Easy of use can be sometimes a trap to add too much magic and blur the view of developers to use a library or framework.

Thanks to some efforts however there is a way in which you can pull your project out of annotations. One of them is using the doctrine bundle built-in capability to export mapping to yaml or other formats. The exporting seldom needs adjustments and even though some say it could be broken or lack some features, pretty much saves you a lot of time when you are doing the move with minor or no adjustments at all.

For instance if you have a

Vendor\CoreBundle

you can just issue this command:

php app/console doctrine:mapping:import "VendorCoreBundle" yml

The only thing with the output is that you will have to remove the indexes that it created and lifecyclecallbacks if you like to be clean and did not have anything specified in those fields.

It however generates a complete specification which helps you don’t miss anything:

// ...
            targetEntity: VendorUsers
            cascade: {  }
            fetch: LAZY
            mappedBy: null
            inversedBy: null
            joinColumns:
                locked_by_id:
                    referencedColumnName: id
            orphanRemoval: false
// ...

That is for mapping, but what about annotations. Well simply just install the bundle https://github.com/sed-szeged/SedRouteExporterBundle and run:

mkdir builds-output
php app/console sed:router:export --format yaml --output builds-output

The output is clogged into a single

routing.yml

file but it is easily split and cleaned up. The specification is also complete on the output:

user_lock:
    path: /users/lock
    defaults: { _controller: 'Vendor\Bundle\CoreBundle\Controller\UserController::lock' }
    options: { compiler_class: Symfony\Component\Routing\RouteCompiler }

Now your controllers will look much cleaner, you can actually go Controllers as Services and then have actual tests that cleanly tests part of your controllers at least. Definitely this will increase the quality of the code of your project because you are decoupling not only mapping, but routing, and others as well as you don’t need necessarily tools to convert but they speed up the process in converting something like this:

/**
 * @Cache(expires="tomorrow")
 * @Method(...)
 * @Template()
 * @ParamConverter()
 * @Route("some_route_here")
 * @Secure(roles="ROLE_USER")
 * @RunAs(roles="ROLE_PRIVATE_SERVICE")
 * @InjectParams({
 *     "em" = @Inject("doctrine.entity_manager")
 * })
 */
 */

Which is madness and that is just a quick invented example on how some projects end up. The conversion will heal you and your team.

4 thoughts on “Decoupling in Symfony How to Pull out from Annotations

  1. So to avoid the extreme approach of using annotations anywhere for everything, you’d advise the other extreme of not using any annotations?

    I’d rather go with the reasonable choice of using annotations sparingly for the right things (for e.g. routing configuration in the controller which includes HTTP method).

    • buy the book from Matthias and read it, it has good examples where annotations are right. But the ones you mentioned are not necessarily right. They are sometimes a big mistake and you want to justify them however you want to. But in the long run you will realize yourself.

    • this is a blog post to convert out of annotations. All those metadata gets into a single format ultimately so there is no advantage over the other. The advantage is in decoupling for better code quality. But still thanks for your comment.

Leave a Reply

Your email address will not be published. Required fields are marked *