Straightforward Symfony 4 Best Practices

please note that all of these recomandations come from the official best practices, I just exctracted the most interesting sentences and sample codes.


Creating the Project

  • Use Composer and Symfony Flex to create and manage Symfony applications.
  • Use the Symfony Skeleton to create new Symfony-based projects.
cd projects/
# minimum package
composer create-project symfony/skeleton project_name
# OR minimum common symfony components
composer create-project symfony/website-skeleton project_name
  • Don’t create any bundle to organize your application logic.
project/
-------bin/
---console
-------config/
-------public/
---index.php
-------src/
---Kernel.php
-------var/
---cache/
---log/
-------vendor/
  • It’s recommended to keep this structure because it’s easy to navigate and most directory names are selfexplanatory, but you can override the location of any Symfony directory.

Configuration

  • Define the infrastructure-related configuration options as environment variables. During development, use the .env file at the root of your project to set these.
Infrastuctured-Related Configuration => .env
  • Define all your application’s env vars in the .env.dist file.
Canonical Parameters => .env.dist
  • Define the application behavior related configuration options in the config/services.yaml file.
Application-Related Configuration => config/services.yaml
  • Use constants to define configuration options that rarely change.
// src/Entity/Post.php
namespace App\Entity;

class Post
{
const NUMBER_OF_ITEMS = 10;

// ...
}
  • Constants can be used for example in your Twig templates
<p>
Displaying the {{ constant('NUMBER_OF_ITEMS', post) }} most recent results.
</p>
  • Doctrine entities and repositories can now easily access these values, whereas they cannot access the container parameters
namespace App\Repository;

use App\Entity\Post;
use Doctrine\ORM\EntityRepository;

class PostRepository extends EntityRepository
{
public function findLatest($limit = Post::NUMBER_OF_ITEMS)
{
// ...
}
}
  • The name of your configuration parameters should be as short as possible and should include a common prefix for the entire application
  • Using app. as the prefix of your parameters is a common practice to avoid collisions with Symfony and third-party bundles/libraries parameters
# config/services.yaml
parameters:
# don't do this: 'dir' is too generic and it doesn't convey any meaning
app.dir: '...'
# do this: short but easy to understand names
app.contents_dir: '...'
# it's OK to use dots, underscores, dashes or nothing, but always
# be consistent and use the same format for all the parameters
app.dir.contents: '...'
app.contents-dir: '...'

Business Logic

project/
-------src/
---Utils/
---MyClass.php
  • Use autowiring to automate the configuration of application services
  • The id of your application’s services should be equal to their class name, except when you have multiple services configured for the same class (in that case, use a snake case id).
  • Services should be private whenever possible. This will prevent you from accessing that service via $container->get().
  • Use the YAML format to define your own services.
  • Use annotations to define the mapping information of the Doctrine entities.
  • Coding Standards are PSR-1 and PSR-2. You can learn more about the Symfony Coding standards.

Controllers

  • Make your controller extend the AbstractController base controller provided by Symfony and use annotations to configure routing, caching and security whenever possible.
  • Don’t add the Action suffix to the methods of the controller actions.
  • Use Annotation, To load routes in your controllers, add the following configuration to the main routing configuration file:
# config/routes.yaml
controllers:
resource: '../src/Controller/'
type: annotation
  • Don’t use the @Template annotation to configure the template used by the controller.
  • Don’t use $this->get() or $this->container->get() to fetch services from the container. Instead, use dependency injection.
  • Use the ParamConverter trick to automatically query for Doctrine entities when it’s simple and convenient.

Templates

  • Use Twig templating format for your templates.
  • Store the application templates in the templates/ directory at the root of your project.
  • Use lowercased snake_case for directory and template names
  • Use a prefixed underscore for partial templates in template names.
  • Define your Twig extensions in the src/Twig/ directory. Your application will automatically detect them and configure them.

Forms

  • Define your forms as PHP classes.
class PostType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('summary', TextareaType::class)
->add('content', TextareaType::class)
....
  • Put the form type classes in the App\Form namespace, unless you use other custom form classes like data transformers.
  • Add buttons in the templates, not in the form classes or the controllers.

Internationalization

  • Store the translation files in the translations/ directory at the root of your project.
  • Use the XLIFF format for your translation files.
  • Always use keys for translations instead of content strings.

Security

  • 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), Symfony recommend having only one firewall entry with the anonymous key enabled.
  • Use bcrypt encoder for hashing your users' passwords.
  • For protecting broad URL patterns, use access_control
  • Whenever possible, use the @Security annotation
  • Check security directly on the security.authorization_checker service whenever you have a more complex situation.
  • Define a custom security voter to implement fine-grained restrictions.

Web Assets

  • Store your assets in the assets/ directory at the root of your project.
  • Use Webpack Encore to compile, combine and minimize web assets.

Test

  • Define a functional test that at least checks if your application pages are successfully loading
  • Hardcode the URLs used in the functional tests instead of using the URL generator.