Symfony Best Practices

Application Bundles

A bundle is meant to be something that can be reused as a stand-alone piece of software. I.e. if a UserBundle cannot be used ‘as is’ in other Symfony apps, then it shouldn’t be its own bundle. Moreover, if an InvoiceBundle depends on ProductBundle, then there’s no advantage to having two separate bundles.

Implementing a single AppBundle bundle in your projects will make your code more concise. There is no need to prefix the AppBundle with your own vendor (e.g. AcmeAppBundle), because this application bundle is never going to be shared. Your Symfony installation doesn’t come with a pre-generated AppBundle, you can generate it by hand.

One of the best practice when developing a Symfony application is to make it configurable via a parameters.yml file. It contains information such as the database name, the mailer hostname, and custom configuration parameters.

As those parameters can be different on your local machine and other environments, it is not recommended to store it in the project repository. Instead, the repository should contain a paramaters.yml.dist file with sensible defaults that can be used as a good strting point for everyone. Symfony recommends that you split the application configuration into three parts.

Define the infrastructure-related configuration options, related to the database and mail server infrastructure, in the app/config/parameters.yml file.

Configuration

Define the infrastructure-related configuration options in the app/config/parameters.yml file. It contains information such as the database name, the mailer hostname, and custom configuration parameters.

As those parameters can be different various environments, it is not recommended to store it in the project repository. Instead, the repository should contain a paramaters.yml.dist file with sensible defaults that can be used as a good starting point for everyone. Whenever a developer starts working on the project, the parameters.yml file must be created by using the parameters.yml.dist as a template. That works quite well, but whenever a new value is added to the template, you must remember to update the main parameter file accordingly. Whenever you run composer install, a script creates the parameters.yml file if it does not exist and allows you to customize all the values interactively. Moreover, if you use the <code>–no-interaction</code> flag, it will silently fallback to the default values.

Define the application behavior related configuration options in the app/config/config.yml file. The config.yml file contains the options used by the application to modify its behavior, such as the sender of email notifications, or the enabled feature toggles (set a feature on / off on various environments).

The configuration options defined in the config.yml file usually vary from one environment to another. That’s why Symfony already includes app/config/config_dev.yml and app/config/config_prod.yml files so that you can override specific values for each environment.

Symfony bundles have two choices on how to handle configuration: normal service configuration through the services.yml file and semantic configuration through a special *Extension class. When you want to decouple the bundle for use in other projects, you want to include the service configuration in the bundle itself. But the amount of work needed to define that configuration isn’t worth it for bundles that aren’t meant to be shared as third-party bundles.

Organizing Your Business Logic

In Symfony applications, business logic is all the custom code you write for your app that’s not specific to the framework (e.g. routing and controllers). Domain classes, Doctrine entities and regular PHP classes that are used as services are good examples of business logic.

For most projects, you should store everything inside the AppBundle. Inside here, you can create whatever directories you want to organize things.

Our application needs a utility that can transform a post title (e.g. ‘Hello World’) into a slug (e.g. ‘hello-world’). Let’s create a new Slugger class inside src/AppBundle/Utils/:

// src/AppBundle/Utils/Slugger.php
namespace AppBundleUtils;
 
class Slugger
{
    public function slugify($string)
    {
        return preg_replace(
            '/[^a-z0-9]/', '-', strtolower(trim(strip_tags($string)))
        );
    }
}
 
# app/config/services.yml
services:
    # keep your service names short
    app.slugger:
        class: AppBundleUtilsSlugger
 
# You can use the custom slugger in any controller class
public function createAction(Request $request)
{
    // ...
 
    if ($form->isSubmitted() && $form->isValid()) {
        $slug = $this->get('app.slugger')->slugify($post->getTitle());
        $post->setSlug($slug);
 
        // ...
    }
}

Traditionally, the naming convention for a service involved following the class name and location to avoid name collisions. Thus, the service would have been called app.utils.slugger. But by using short service names, your code will be easier to read and use. So the name of your application’s services should be as short as possible, but unique enough that you can search your project for the service if you ever need to.


Leave a Reply