5 Tips for developing Symfony reusable bundles

I’ve been working with Symfony for around six years and I wanted to go a step further developing a reusable bundle. Now I want to share my experience helping Symfony developers not to get stuck where I got.

This is the bundle: (https://github.com/msalsas/MsalsasVotingBundle)
And this is an installable demo: (https://github.com/msalsas/voting-bundle-demo)

1. Put your bundle in /lib/AcmeFooBundle/src/ and update composer.json

A Symfony bundle usually depends on a Symfony project, so you have to create a project before starting to develop. There is an official demo to start with: (https://github.com/symfony/demo)
My first doubt was: Where should I develop my bundle from?
From Symfony >= 3.4 docs advise:

In Symfony versions prior to 3.4, it was recommended to organize your own application code using bundles. This is no longer recommended and bundles should only be used to share code and features between multiple applications.

This means that previously you could just use SensioGeneratorBundle to create a reusable bundle and place it under /src. But now, this is no longer supported, so you have to create the bundle by yourself.

You could just place it under /vendor, but I prefer to keep it aside so that I couldn’t delete it by accident deleting the vendor directory. You can place it wherever you want but keeping in mind that the final bundle directory will be /vendor/acme/foo-bundle (for AcmeFooBundle).

I followed the KnpBundles tip and put it under /lib/AcmeFooBundle/src/

But remember that you have to change the composer.json of the project:

    ...
    "autoload-dev": {
        "psr-4": {
            "App\\Tests\\": "tests/",
            "Acme\\FooBundle\\": "lib/AcmeFooBundle/src/"
        }
    },
    ...

when enabling your bundle at config/bundles.php

You can see how it looks like at this point of the demo.
Later I decided to delete the bundle from lib/MsalsasVotingBundle/src and add it as a dependency in composer.json, but I keep this structure for developing in localhost.

2. Follow Symfony best practices

This is quite evident, but don’t forget to read and follow Symfony best practices.

3. Configuration, extension and compiler pass

This is probably the hardest part. When you develop a reusable bundle usually need to add some custom configuration. It will depend on the bundle functionality so I’ll reference my bundle as an example.
This bundle has a dependency on user integration: you have to specify your user class and the bundle will relate users with votes. Then, there must be an option to tell the bundle where the user class is. Here is where Configuration comes in defining and validating the bundle configuration.
In this case, I wanted a “user_provider” key for the user class name with '\App\Entity\User' as default value among other things:
(https://github.com/msalsas/MsalsasVotingBundle/blob/master/DependencyInjection/Configuration.php).

Now you need to process and manipulate that configuration and here comes in extensions and/or compiler passes. In my case I just used extension:

By now, take a look at the load method. Here you will process your configuration and add some parameters from there. Here I’m setting three parameters: negative_reasons, anonymous_percent_allowed and anonymous_min_allowed to use them in services.xml.

But what if you need to modify other bundles configuration? In my case I needed to modify some Twig and Doctrine configuration. Then, you need to implement PrependExtensionInterface and add a the prepend method. E.g. to overwrite Twig configurations just call:

$container->prependExtensionConfig('twig', $yourNewTwigConfig);

This method is called before any bundle configurations are loaded.

Again, don’t forget to read Configuration Symfony docs.

4. Recipes and Symfony Flex

I spent a lot of time to realize how other bundles like NelmioApiDocBundle manage to create routing files under config/routing. It’s very simple. There is a repo for defining Symfony Flex configuration for all bundles. Actually, there are two: one for official bundles and other for unofficial ones. You’ll probably have to use recipe-contrib (unofficial).
You have to fork the repo, add your config in a branch and make a PR (https://github.com/symfony/recipes-contrib/pull/416).
With this configuration you define some files to be copied to the project (bundle config file, routing file, …).
Now, when you require the bundle with composer those files will be automatically copied if you use Symfony Flex.

5. Code review tools

Besides of Travis CI, you can use a code review tool to help you to improve your code.
I’m using Scrutinizer but there are a lot of providers: Code Climate, Codacy, SensioLabsInsight, …

Conclusion

Those are just a few tips which I found helpful.
I hope this tips can help you to take your first steps in Symfony reusable bundles development.
Don’t hesitate to leave a comment if you have anything to contribute.

Leave a Reply

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