Practical Guide — How to upgrade from Symfony 3.x to 5.0 ( or 4.x ) — Part 1

Diego Favero
11 min readMay 16, 2020

--

In a hardest way

Before Read this guide, be aware

_ This is my first blog post, I mean, from my whole life … I have cut my Starcraft2 time to do so ! Hopping this will be useful to someone …

_ I probably misunderstood a few basic concepts from symfony 5 ( please, let me know if ) … Let’s say I am not much of reader, more like a doer ….

_ What I present here is a practical way to get the job done, with lots of personal opinions and actions needed specifically on the scope of my project.

_If you are totally new to symfony or programming worlds, this guide may seems a little confusing and incomplete. But, it can ring few bells.

_I am not here to teach you how symfony works (google it !), I am here to tell you what I could not find on internet.

Symfony Docs ( many links presented in this post ) may seem a bit confusing if you are start to learn it, but, most of the times, it does explain well how to get the job done.

_ Just to make clear : This guide is for who already has a full Symfony 3 app running and wishes to upgrade it so Symfony 5.

_Last, but not least. I am not native english speaker. I learned by myself, so, forgive any misspelling or ugly sentence here !

Why am I doing this instead to have nice time playing SC2 ?

So, you just read the Symfony Official Guide to upgrade and, there is no references in how to upgrade from symfony 3 to 4 or 5

Although, there is a few links around, such this or this … But those guides fits for newbies, hooding silly apps, most, example from a basic symfony skeleton.

Myself, and only me, have been work in a project using symfony 3 since 2014, and today, my birthday in may 16th 2020, the symfony 3 has been pretty functional and solid. In other hand, this project has became kind of ( as we call in Brazil ) a white elephant, meaning, it is too big, full of dependencies and services written exclusively for serve the system. It is not anymore just a bunch of bundles with an symfony default structure… So, most of those guides cited above won’t work that easy as they claim.

After a few unsuccessful attempts, I decided to do it anyway ( faced it as a personal challenge … even because the symfony official courses are really away from my economic situation).

To get started, instead try to migrate my application, I decided to install a clean symfony 5 project and play with it a bit, so, I could get to understand the newly features. I strongly advice to do the same -> Installing Symfony
I stick to — full-mode just to avoid have to add any missing extension later. Remember I am up to : less work the better, but, pay a little reading before to choose the right initial setup for your needs .

I created an simple controller, an entity, a connection ( and its mapped bundles), a service and configured the basics .

With everything working fine, I tried to copy one of my controllers, respectives entities and a service ( all working on my sfy3 app ) into sfy5 project. Took me a while to figure about namespace-ing, routes, services declarations and initial config… but once I got those working, I am sure now how I can migrate from symfony 3.4 to 5.0

Let’s get to the facts

When Symfony introduced its version 4, they changed (among other changes not listed here):

  • Directories structure ( particularly, I did not approve that change, but, we will talk about it later )
  • The way to declare services ( this one I approve )
  • Config and Routes (also approved by me)
  • Env Variables ( better to change variables than use app_dev.php )
  • The Namespace
  • Basic of Entity Repository
  • How to Update database schemas from CLI ( More info about Migrations )

There’s a bunch of other changes, but I am not here to tell ya that ! Go Google it !!!

And if, like me, you are an stubborn programmer, this is the part you will have to put into your head:

The most twisted to my know how:

_ they changed the concept of bundles !

In Symfony 4/5 there is no such bundles to hood a bunch of codes related to a purpose. More info here

“In Symfony versions prior to 4.0, 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.”

In a way that I understand now, based on sfy3 concepts, there is no practical differences between services and controllers. ( remember that I am not that aware of sfy basics concepts ) .
On Action, both would fit for the same purposes. It is just a matter of best practices

The good news

You will not have to rewrite your controllers, services, dependency injections or any actually programming code !!!! Only a few adaptions on namespace, extends and uses. Maybe some __construct as well.
Most the job will be to adapt syf5 config to work as sfy3 !

I have no bad news for ya !

… Lots of work instead

I took a whole afternoon digging on my backyard looking for earthworms (those are the controllers to process my organic waste and response humus !) && (so, pandemic times, I need fertilizer to grow my own food) … While doing so, I gave serious thoughts questioning myself why should I or not upgrade symfony.

I have came to a sense that, YES, I should upgrade it. Symfony has been so solid, and so far, seems like the 5 version has brought up only improvements ( prove me wrong ??!! )

I also considered how long more the 3.4 version will be under support …

Last consideration was : If I do not do that now, It will be worse, since my system is always growing

Now, dear reader, you are aware of my personal believes, it is the time to decide if you really want go thru this journey…. After all, even announced to be out of support soon, sfy 3.4 is still a pretty damn solid framework. (don't mind my southern accent )

Lets get our fingers warmed up

The default way symfony 5 uses to autowire services More Info Here

Basically, every Class inside the src/Controller folder will be considered as service, meaning, all of sfy3 bundle controllers, will work like services.

PS : I have in my project many bundles and services. So, the idea is to migrate each bundle controller at time, and get it working, which demands to include all routes, entities, services, templates and dependency injections.

Assuming you have now a fully working symfony 5 application running, with its correct configuration and parameters for servers ( http and sql ) and enviroments, lets follow these steps .
The first two may be need only once, all the others, to each controller.

  • Set the .env file
  • Adjust all need configs
  • Copy the controller into src/Controller
  • Copy the entities to src/Entity folder
  • Copy the repositories to src/Repository folder
  • Copy the services to src/MyServices folder
  • Adjust the namespace and headers for each file
  • Set the services configs ( if necessary )
  • Set the routes
  • Update Entities ( if necessary )
  • Copy the templates to template folder
  • Fix assets URL

Do not expect to get your controller working right away after those steps. Mines did not. I got tons of errors before to figure the right way to do so.
But basically, the steps above

Boiling Plate Advice : Google each error, and solve it, one by one. Once there is no more errors, you are done.

Lets Start !

Set the .env file

Now you got to a point a really like on sfy5. No more need do app_dev.php or rewrite rules for each enviroment, and etc…

.env file is where to place global paremeters … I am not going further into it… check the Docs

Adjust all need configs

Sfy5 also brought up a different way to organize the config files… pay attention on config folder, check all the files and try to spot the differences and figure out the best configuration to your app. Symfony Configuration

After complete those steps, lets loop over the next ones !

Copy the controller into src/Controller

Remember When I told that I did not like the new directory structure…. Symfony Docs says to put all your controllers into src/Controller … Same applies to entities, service, template, dependence injections and repositories.

To try to keep a similar sfy3 bundle structure, I have created sub folders:

So, my sfy3

src/DATA/PersonBundle/Controller/CRUDController.php

will be copied to sfy5 as

src/Controller/DATA/PersonBundle/Controller/CRUDController.php

Set the namespace to :

App\Controller\DATA\PersonBundle\Controller

PS: sfy5 auto-loader is the one responsible to interpret the src folder as App in namespace's. Check your composer.json to change that.

The Headers, of the class and it extended parent will also be different.

Change the

CRUDController extends Controller

to

CRUDController extends AbstractController

As so, the file will be something like this

<?php
namespace App\Controller\DATA\PersonBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class CRUDController extends AbstractController {}

Copy the entities to src/Entity folder

The sfy3

src/DATA/PersonBundle/Entity/Person.php

will be placed into sfy5

src/Entity/DATA/PersonBundle/Person.php

Set the namespace to :

App\Entity\DATA\PersonBundle

also, there is a bit of difference on headers :

<?php
namespace App\Entity\DATA\PersonBundle;
use App\Repository\PersonBundle\PersonRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=PersonRepository::class)
*/
class Person {}

Copy the repositories to src/Repository folder

My sfy3

src/DATA/PersonBundle/Repository/PersonRepository.php

will be placed into sfy5

src/Repository/DATA/PersonBundle/PersonRepository.php

Set the namespace to :

App\Repository\DATA\PersonBundle

The Repository headers has changed a bit more :

<?php
namespace App\Repository\DATA\PersonBundle;
use App\Entity\DATA\PersonBundle\Person;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method Person|null find($id, $lockMode = null, $lockVersion = null)
* @method Person|null findOneBy(array $criteria, array $orderBy = null)
* @method Person[] findAll()
* @method Person[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class PersonRepository extends ServiceEntityRepository
{

public function __construct(ManagerRegistry $registry) {
parent::__construct($registry, Person::class);
}

Copy the services to src/MyServices folder

I have created a new folder called MyServices and going to move my services into it. Don’t forget to adjust the namespace.

Set the service config

For Controllers

Into sfy5, controllers extends AbstractController instead Controller … and the main difference to know now is :

The difference is that the AbstractController only uses a limited container that only contains some services that the controller has been subscribed to. There is just a small set of subscribed services by default: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php#L74-L92

source : Here

As said before, now, services and controllers are kind of same same but different …

By default, Symfony has an autowire for services … check on

config/services.xml

By the citation above ( and experience ) I found out that container is not loaded into AbstractController as in Controller, so, into service configuration , I added the arguments line:

# controllers are imported separately to make sure services can be injected
# as action arguments even if you don’t extend any base controller class
App\Controller\:
resource: ‘../src/Controller’
tags: [‘controller.service_arguments’]
arguments: [“@service_container”]

ps: I could not use the right indentation because I don't know yet how to do so in this medium editor … be aware if you straight paste the yml codes.

Basically I have injected the service container straight into all my controllers.
Into each controller I have to add ( optional )

use Symfony\Component\DependencyInjection\Container;
private $_container;
public function __construct(Container $container) {
$this->_container = $container;
}

Now, I can get any of my services into the controllers .

PS : Many say it is a bad practice to inject the whole container into a service/controller, and, somehow, I agree ! Because

It is hard sometimes to debug and test, plus, the container as parameter may imply in some performance leaks … Not to worry if you have a powerful server though.

But … My system has so many need of services, sometimes, I use a service only once into a class.

In my specific case, would be a bit painful to pass as parameter each needed service. So, I do use this bad practice.

If you wanna go deeper on that, symfony allows you to build custom containers.

Extra Tip :
Use $_container ( or whatever ) instead $container.
Because, even if on construct you do assign
$this->container = $container
AbstractController will not override its default smaller $this->container !

For Services

If your service need other params , you will have to declare it. In my case, on config/services.xml I added :

MySession:
class: App\MyService\MySession\MySession
arguments: [“@session”, “@service_container”]
lazy: true

Don’t forget to set services as public !

services:
_defaults:
public: true

Now, inside of my CRUDController, I can use : $this->_container->get(‘MySession’);

Another PS :

I had to make the services work like above because that is the way I do in sfy3 . Remember, the idea is not to re-write the souce-code. Only configuration

Set the routes

Symfony 5 allows to set routes straight into controllers as annotation. It is sure an improvement that makes life easier, but, I will stick to yaml routing files, like my sfy3.

In any case, you will have to rewrite the routes, so, fell free to do the way better fits you. Check Documentation

First, I added to config/routes.yaml

PersonBundle:
resource: “../src/Controller/DATA/PersonBundle/Resources/config/Person.yml”
prefix: /Person

Then created the file :

src/Controller/DATA/PersonBundle/Resources/config/Person.yml

with the follow content

person_dashboard:
path: /Dashboard
controller: App\Controller\DATA\PersonBundle\Controller\CRUDController::Dashboard
# methods: GET|POST|PUT

And into

src/Controller/DATA/PersonBundle/Controller/CRUDController.php

I have :

public function Dashboard(PersonRepository $person){
if ( !$this->MySession->has(‘firstProcessed’)){
$this->MySession->set(‘firstProcessed’, new \DateTime());
}

return $this->render(‘DATA/PersonBundle/CRUD/Dashboard.html.twig’, [
‘persons’ => $person->findAll(),
‘firstProcessed’ => $this->MySession->get(‘firstProcessed’), ]);
}

Update Entities ( if necessary )

In my case, no need because I am using the same dev database as in my sfy3 ( just had to assert the right parameters o .env file ) , but, if you need so, check out about Migrations

Copy the templates to template folder

Again, Symfony has changed the directory structure, all templates go into templates folder. As so, I did create a folder called DATA, within a folder PersonBundle, which contains a folder called CRUD

Doing so, I keep my previous idea of directories layout , in this case : Hood, Bundle, Controller and Method

So, I have the

templates/DATA/PersonBundle/CRUD/Dashboard.html.twig

with the following code into it

{{ dump(firstProcessed) }}
{{ dump(persons) }}

Fix assets URL

If you are using assets, and have already added Asset Component, you must place the assets into public folder then replace into your templates :

href=”{{ absolute_url( asset(“Resources/public/css/menu.css”) ) }}”

by

href="{{ absolute_url( asset("css/menu.css") ) }}"

If you deal with assets in your controller, or have some kind of pre-processing, check the Asset Component for more info.

The Truth Time

Assuming you followed this guide adapting it to your needs, you should be able to access

http://yourlink/public/index.php/Person/Dashboard

If there is any error, pursuit it until the deepest posts on stackoverflow or github and fix it.

If everything is all right, you will see a page with a datetime object and a list of App\Entity\DATA\PersonBundle\Person entity.

Notice if you refresh the page, the datetime object won’t change !

What’s next ?

already migrate all controllers, services, entities, routes, repositories, templates and parameters ?

So, test it ! Do the tests again !!!!

Enjoy Symfony 5

…..

Ok, it didn't work, right ?!!?!

So, check the Practical Guide — How to upgrade from Symfony 3.x to 5.0 ( or 4.x ) — Part 2

--

--

Diego Favero

Sr. Full Stack WebDeveloper — Enthusiast Rock Climber and Mountain Biker — Earthworm humus producer — DJ — Carpenter