This article describes the best practices for developing web applications with
Symfony that fit the philosophy envisioned by the original Symfony creators.

If you don't agree with some of these recommendations, they might be a good
starting point that you can then extend and fit to your specific needs.
You can even ignore them completely and continue using your own best practices
and methodologies. Symfony is flexible enough to adapt to your needs.

This article assumes that you already have experience developing Symfony
applications. If you don't, read first the Getting Started
section of the documentation.

Tip

Symfony provides a sample application called Symfony Demo that follows
all these best practices, so you can experience them in practice.

These are the options used to modify the application behavior, such as the sender
of email notifications, or the enabled feature toggles. Their value doesn't
change per machine, so don't define them as environment variables.

Define these options as parameters in the
config/services.yaml file. You can override these options per
environment in the config/services_dev.yaml
and config/services_prod.yaml files.

Consider using app. as the prefix of your parameters
to avoid collisions with Symfony and third-party bundles/libraries parameters.
Then, use just one or two words to describe the purpose of the parameter:

1
2
3
4
5
6
7
8
9
10

# config/services.yamlparameters:# don't do this: 'dir' is too generic and it doesn't convey any meaningapp.dir:'...'# do this: short but easy to understand namesapp.contents_dir:'...'# it's OK to use dots, underscores, dashes or nothing, but always# be consistent and use the same format for all the parametersapp.dir.contents:'...'app.contents-dir:'...'

Configuration options like the number of items to display in some listing rarely
change. Instead of defining them as service container parameters,
define them as PHP constants in the related classes. Example:

The main advantage of constants is that you can use them everywhere, including
Twig templates and Doctrine entities, whereas parameters are only available
from places with access to the service container.

The only notable disadvantage of using constants for this kind of configuration
values is that it's complicated to redefine their values in your tests.

When Symfony 2.0 was released, applications used bundles to
divide their code into logical features: UserBundle, ProductBundle,
InvoiceBundle, etc. However, a bundle is meant to be something that can be
reused as a stand-alone piece of software.

If you need to reuse some feature in your projects, create a bundle for it (in a
private repository, to not make it publicly available). For the rest of your
application code, use PHP namespaces to organize code instead of bundles.

Service autowiring is a feature that
reads the type-hints on your constructor (or other methods) and automatically
passes the correct services to each method, making unnecessary to configure
services explicitly and simplifying the application maintenance.

Symfony provides a base controller
which includes shortcuts for the most common needs such as rendering templates
or checking security permissions.

Extending your controllers from this base controller couples your application
to Symfony. Coupling is generally wrong, but it may be OK in this case because
controllers shouldn't contain any business logic. Controllers should contain
nothing more than a few lines of glue-code, so you are not coupling the
important parts of your application.

Using annotations for routing, caching and security simplifies configuration.
You don't need to browse several files created with different formats (YAML, XML,
PHP): all the configuration is just where you need it and it only uses one format.

The @Template annotation is useful, but also involves some magic.
Moreover, most of the time @Template is used without any parameters, which
makes it more difficult to know which template is being rendered. It also hides
the fact that a controller should always return a Response object.

If you extend the base AbstractController, you can only access to the most
common services (e.g twig, router, doctrine, etc.), directly from the
container via $this->container->get() or $this->get().
Instead, you must use dependency injection to fetch services by
type-hinting action method arguments or
constructor arguments.

If you're using Doctrine, then you can optionally use the
ParamConverter to automatically query for an entity and pass it as an argument
to your controller. It will also show a 404 page if no entity can be found.

If the logic to get an entity from a route variable is more complex, instead of
configuring the ParamConverter, it's better to make the Doctrine query inside
the controller (e.g. by calling to a Doctrine repository method).

Template fragments, also called "partial templates", allow to
reuse template contents. Prefix their names
with an underscore to better differentiate them from complete templates (e.g.
_user_metadata.html.twig or _caution_message.html.twig).

Form classes should be agnostic to where they will be used. For example, the
button of a form used to both create and edit items should change from "Add new"
to "Save changes" depending on where it's used.

Instead of adding buttons in form classes or the controllers, it's recommended
to add buttons in the templates. This also improves the separation of concerns,
because the button styling (CSS class and other attributes) is defined in the
template instead of in a PHP class.

Rendering forms and processing forms
are two of the main tasks when handling forms. Both are too similar (most of the
times, almost identical), so it's much simpler to let a single controller action
handle everything.

Of all the translation formats supported by Symfony (PHP, Qt, .po, .mo,
JSON, CSV, INI, etc.) XLIFF and gettext have the best support in the tools used
by professional translators. And since it's based on XML, you can validate XLIFF
file contents as you write them.

Symfony also supports notes in XLIFF files, making them more user-friendly for
translators. At the end, good translations are all about context, and these
XLIFF notes allow you to define that context.

Unless you have two legitimately different authentication systems and users
(e.g. form login for the main site and a token system for your API only), it's
recommended to have only one firewall to keep things simple.

Additionally, you should use the anonymous key under your firewall. If you
require users to be logged in for different sections of your site, use the
access_control option.

Web assets are things like CSS, JavaScript and image files that make the
frontend of your site look and work great. Webpack is the leading JavaScript
module bundler that compiles, transforms and packages assets for usage in a browser.

Webpack Encore is a JavaScript library that gets rid of most
of Webpack complexity without hiding any of its features or distorting its usage
and philosophy. It was originally created for Symfony applications, but it works
for any application using any technology.

In Symfony applications it's recommended to generate URLs
using routes to automatically update all links when a URL changes. However, if a
public URL changes, users won't be able to browse it unless you set up a
redirection to the new URL.

That's why it's recommended to use raw URLs in tests instead of generating them
from routes. Whenever a route changes, tests will break and you'll know that
you must set up a redirection.