The new era of micro frameworks, welcome Symfony 4

Whoop Whoop! Symfony 3.4 and Symfony 4 were released a couple days ago with lots of hype around it!

But what is really different this time from other Symfony versions? Let’s have a look…

Decoupling components

First off, conceptually, Symfony framework is moving towards a more decoupled structure. You probably noticed that instead of creating a new project through the symfony installer, you now use the following command.

composer create-project symfony/skeleton your-project-name

 
The reason behind this is that in earlier versions, when creating a symfony project, you were installing lots of dependencies and components that you may or may not be using in your application.

The idea now is creating a skeleton type project, and then installing all the components we need for that project seperately. This allows the programer complete freedom to be using whatever component he or she needs.

This in turn means that each Symfony application you code might use different components, even if they were created by Symfony, and therefore you do not need to install all of them every time, which makes each project more light weight!

For example, an API project might not use Twig, so there’s no need to install it in the vendor directory right?

Or I might just install the command component because I need to write some php scripts and I need a quick wrapper to organise my code.

Neither of the projects above would need twig, or form, or entities, or orm… you get the point.

I still remember the days when you had one big bloated Symfony framework installed, and many apps inside it. Inside each app, there were bundles.

After that, it evolved into one app per symfony installation, and each app had it’s different bundles.

It’s now time for the bundle less symfony. Best practices now say that you should have one skeleton per project, and following the decoupling of components, each project will have just the component it needs. This is just great, because every project will now have the underlying code that is really necessary, and not a bloated version of all the Symfony components together.

Having said this, there’s a few components I use 99% of the time when creating websites, so I created this small wrapper script so it’s easy for me to get them all, feel free to grab it and add/remove at your taste :). I call it ‘symfony-create-project.sh’

#!/bin/bash
# This script is a wrapper for creating a skeleton and installing the basic components I use most of the time
# To use it: bash symfony-create-project name-of-your-project

if [ "$1" != "" ]; then
	composer create-project symfony/skeleton "$1"
	cd "$1"
	composer require annotations
	composer require --dev profiler
	composer require twig
	composer require orm
	composer require form
	composer require form validator
else
	echo "parameter expected: name-of-your-project"
fi

Directory Structure

First thing you notice is the disappearance of the web folder. You now have the public folder instead. Inside it we can also see that app.php and app_dev.php have disappeared. Instead, we now have a more default index.php. The environment will be controlled by a .env file in the root directory of the project.

Second of all, the whole Resources folder inside src/ has disapeard as well, and all of it’s contents are in different places. The config files (routing.yml, services.yml…) are in the config/ folder in the root directory. This makes sense now since we have bundle-less applications. All of the twig files have also moved to a general templates/ folder in the root directory.

Now the src/ folder is just for php code, which makes much more sense if you work on a team with frontend and backend dev’s, each of them can now focus on their own folder.

Autoconfiguring and Autowiring

This is my absolute favourite.

Let me just copy this with the comments, as it explains everything really well.

services:
    # default configuration for services in *this* file
    _defaults:
        autowire: true      # Automatically injects dependencies in your services.
        autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
        public: false       # Allows optimizing the container by removing unused services; this also means
                            # fetching services directly from the container via $container->get() won't work.
                            # The best practice is to be explicit about your dependencies anyway.

Let’s dive into the code.

So I created a new service class,

<?php

namespace App\Utils;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;

class NumberGenerator
{
    public function __construct()
    {
    }

    public function getRandomNumber()
    {
        return  mt_rand(0, 100);
    }
}

Injected into the action,

<?php

namespace App\Controller;

use App\Utils\NumberGenerator;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;

class DefaultController extends Controller
{
    /**
     * @Route("/default/home")
     */
    public function home(NumberGenerator $numberGenerator)
    {
        return $this->render('default/home.html.twig', array(
            'number' => $numberGenerator->getRandomNumber(),
        ));
    }
}

and BANG, it worked like expected.

Let’s recapitulate here… before, we had to create the service, add it in the dependency injection config file, and then get it from the container.

What we just did in Symfony4 is create the service and inject it in the action we needed it. No configuration, no getting… just injection. This REALLY speeds up the process, enough configuration and more coding!!

However, if you don’t want to inject your service in the action, you can still use the container with the get function as we did in the past, although best practices advise not to.

Firstly,


composer require dependency-injection

Secondly, we need to set the service to public.

services:

    App\Utils\NumberGenerator:
        public: true

You can now get the service from the container

...
return $this->render('default/home.html.twig', array(
            'number' => $this->get(NumberGenerator::class)->getRandomNumber(),
        ));
...

There’s another component/bundle, which although is not super necessary, makes your life much much easier: Yup, you guessed it, it’s the Maker Bundle

composer require maker-bundle

This components creates the skeleton files for you (controllers, entities, forms,… etc), for example

bin/console make:controller CarController

More info on the maker bundle here.
More info on all the different component recipes here.

Conclusion

I just love how Symfony keeps giving more and more control to the developer.

Lots uf us (me included) know that changing and evolving things that we’re used to doing is sometimes painful… but after playing with it a little bit, I see this upgrade as a great step forward for better code, better standards and better readability.

So thank you to the Symfony team and everyone who contributed for making this happen!

Happy coding!!