[Part 2/2] Migrating to Symfony 4.0 with Flex

We're over half way through our migration from Symfony 3 to Symfony 4 with Flex.

We're following along with the official Upgrade to Symfony Flex guidance. This video directly follows on from the previous video, so if you haven't already watched that one, then please start there first.

Use the force, Luke Move the src, Luke

The official Symfony Flex upgrade guidance:

Move the original source code from src/{App,...}Bundle/ to src/ and update the namespaces of every PHP file to be App\... (advanced IDEs can do this automatically).

This one sounds quite involved. And it is, depending on your project's size.

Fortunately our project is relatively tiny.

Due to the way we're working, I can open both the old project and the new project inside two separate instances of PhpStorm.

I'm able to copy / paste the folder contents easily in this way. Here's a command line representation of what I am doing:

cp -R \
   /home/chris/Development/crv-symfony-4-updates/githut/src/AppBundle/Controller \

I've split this onto multiple lines for readability.

This is copying the src/AppBundle/Controller directory contents from the old project to the new.

Likewise, for the src/AppBundle/Service directory:

cp -R \
   /home/chris/Development/crv-symfony-4-updates/githut/src/AppBundle/Service \

That's basically it for the copy / paste.

What we now need to do is de-bundle.

In other words, replace references of AppBundle\... with App\....

We can do this using a find / replace in PhpStorm. Watch the video for an example.

Important: Also update your service definitions. We haven't done that here, but will do so below.

Our code changes are now complete.

Of course real world projects are more involved than this. I know I'm making this look easier than it will very likely be for you.

Move the templates

The official guidance:

Move the original templates from app/Resources/views/ to templates/ and app/Resources/translations to translations/. There may be a few other files you need to move into a new location.

We don't have any translations.

We do have templates though.

We don't seem to have a templates/ directory, so let's create one:

mkdir templates

And copy everything over:

cp -R \
   /home/chris/Development/crv-symfony-4-updates/githut/app/Resources/views/* \

Seems easy enough, right?

We'll come back to templates - or more specifically, Twig - shortly.

Make any other change needed by your application

The last piece of official guidance:

Make any other change needed by your application. For example, if your original web/app_*.php front controllers were customized, add those changes to the new public/index.php controller.

Sweet, we don't have any worries here.

Final Checks

We've complete all the official steps to migrate to Symfony 4 with Flex.

Let's give our new code a cache:clear as a quick way to validate everything is behaving:

php bin/console cache:clear

In DefinitionErrorExceptionPass.php line 37:

  Cannot autowire service "App\Service\GitHubApi": argument "$httpClient" of method "__construct()" references interface "App\Service\HttpClientIn  
  terface" but no such service exists. You should maybe alias this interface to one of these existing services: "App\Service\BuzzHttpClient", "App  

Stone the crows.

Ok, so towards the end of the previous video we did copy over all the service config. However, at the time I mentioned that PhpStorm will likely flag up a fault here.

We need to change the definitions from AppBundle to App:

# config/services.yaml


    # other stuff removed for brevity

    App\Service\HttpClientInterface: '@App\Service\GuzzleHttpClient'

    GuzzleHttp\Client: "@eight_points_guzzle.client.8p_guzzle_client"

With this change our quick cache:clear verification should pass:

php bin/console cache:clear

 // Clearing the cache for the dev environment with debug true                                                          

 [OK] Cache for the "dev" environment (debug=true) was successfully cleared.                                            

Good stuff.

Firing Up Symfony 4

Ok, let's get this show on the road.

All our code is migrated. Before we merge these changes back in, let's fire up our new app and test it out:

php bin/console server:start

 [OK] Server listening on                                                                         

We added the web server bundle back in. This is important. If you missed adding this in then this command will not work.

Ok, so browsing to the site:

We see a ... "Welcome" page.

Where the heck is GitHut?

You Need To Add That

The biggest change by far moving from Symfony 2 or Symfony 3 up to Symfony 4 is that very little is included by default.

This can be ... frustrating.

The reason we can't browse to our pages as before is because Symfony currently has no idea about our routes.

Whereas in Symfony 3 annotation routing was the defacto standard, in Symfony 4 we must composer require annotations to make this work.

php bin/console debug:router
 ------ -------- -------- ------ ------ 
  Name   Method   Scheme   Host   Path  
 ------ -------- -------- ------ ------ 

And after adding the dep:

$ composer require annotations

Using version ^5.1 for sensio/framework-extra-bundle
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 7 installs, 0 updates, 0 removals
  - Installing doctrine/lexer (v1.0.1) Loading from cache
  - Installing doctrine/inflector (v1.2.0) Loading from cache
  - Installing doctrine/collections (v1.5.0) Loading from cache
  - Installing doctrine/cache (v1.7.1) Loading from cache
  - Installing doctrine/annotations (v1.6.0) Loading from cache
  - Installing doctrine/common (v2.8.1) Loading from cache
  - Installing sensio/framework-extra-bundle (v5.1.3) Loading from cache
Writing lock file
Generating autoload files
Symfony operations: 2 recipes (94e3dbafd6174569355f91da114039f9)
  - Configuring doctrine/annotations (>=1.0): From github.com/symfony/recipes:master
  - Configuring sensio/framework-extra-bundle (>=4.0): From github.com/symfony/recipes:master
Executing script cache:clear [OK]
Executing script assets:install --symlink --relative public [OK]

$ php bin/console debug:router          
 --------- -------- -------- ------ ------------------------ 
  Name      Method   Scheme   Host   Path                    
 --------- -------- -------- ------ ------------------------ 
  another   ANY      ANY      ANY    /another/{thing}/{and}  
  githut    ANY      ANY      ANY    /{username}             
  profile   ANY      ANY      ANY    /profile/{username}     
  repos     ANY      ANY      ANY    /repos/{username}       
 --------- -------- -------- ------ ------------------------ 

Hoorah, our routes are back.

Now we can browse to our site, right?

Whoops, looks like something went wrong

Refreshing the page should work.

But it doesn't.

 (1/1) LogicException

You can not use the "render" method if the Templating Component or the Twig Bundle are not available.

Oh yeah, Twig isn't included anymore.

We must add it.

composer require symfony/twig-bundle

./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 3 installs, 0 updates, 0 removals
  - Installing twig/twig (v2.4.4) Loading from cache
  - Installing symfony/twig-bridge (v4.0.2) Downloading: 100%         
  - Installing symfony/twig-bundle (v4.0.2) Downloading: 100%         
Writing lock file
Generating autoload files
Symfony operations: 1 recipe (d75b639d694c496445f2e3d2d4301cd0)
  - Configuring symfony/twig-bundle (>=3.3): From github.com/symfony/recipes:master
Executing script cache:clear [OK]
Executing script assets:install --symlink --relative public [OK]

The reasoning behind this is because not every project needs Twig.

If you're making a JSON API then you don't need to render HTML.

Less dependencies = smaller projects = fewer bugs.

But still, the User Experience here is challenging for beginners, in my opinion.

Ok, onwards.

We have Twig, now let's refresh:


Unknown "asset" function.

Again, we hit on another 'thing' we don't have any longer.

The fix is simple, if a little laborious:

composer require symfony/asset

./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Installing symfony/asset (v4.0.2) Downloading: 100%         
Writing lock file
Generating autoload files
Executing script cache:clear [OK]
Executing script assets:install --symlink --relative public [OK]

For what it's worth, it might be worth your time investigating Symfony Encore at this point.

Our Own Problems

We kinda glossed over the earlier template changes because I deliberately wanted to hit upon all these issues.

Now though, we must do a little further template tweaking because:

An exception has been thrown during the rendering of a template ("The "AppBundle" (from the _controller value "AppBundle:GitHut:profile") does not exist or is not enabled in your kernel!").

We still have some left over references to AppBundle in our templates.

These need changing quite significantly:

<!-- templates/githut/index.html.twig -->

{% extends 'base.html.twig' %}

{% block body %}

    <div class="col-sm-3">
        {{ render (controller('App\\Controller\\GitHutController::profileAction', { 'username': username })) }}

    <div class="col-sm-9">
        {{ render (controller('App\\Controller\\GitHutController::reposAction', { 'username': username })) }}
{% endblock body %}

And a further change:

Unable to find template ":githut:repo.html.twig" (looked into: /tmp/githut-flex/templates, /tmp/githut-flex/templates) in githut/repos.html.twig at line 17.

This needs the include syntax updating:

# templates/githut/repos.html.twig 

- <li class="list-group-item">{% include ':githut:repo.html.twig' %}</li>
+ <li class="list-group-item">{% include 'githut/repo.html.twig' %}</li>

Fixing the CSS

Our page now loads.

But it has a CSS issue.

Namely our custom CSS - mystyle.css is 404.

To fix this we need to copy the old contents from web/css/mystyle.css to public/css/mystyle.css.

It's as easy as that.

And finally, the Rock has come back to... no wait, that's not it. Finally, our web page loads and looks right. And it's on Symfony 4.

I truly hope at this point you can smell what the Rock is cookin'.

All that remains here is to move the new code into the old code repo.

Stop It And Tidy Up

Stop the running web server:

php bin/console server:stop

 [OK] Stopped the web server.                                                                                           

Then I'm going to create a new branch in my old repo:

cd /home/chris/Development/crv-symfony-4-updates/githut
git checkout -b flex

Ready to go crazy?

rm -rf ./*

ls -la

total 20
drwxrwxr-x 4 chris chris 4096 Dec 16 11:38 .
drwxrwxr-x 3 chris chris 4096 Dec  9 20:41 ..
drwxrwxr-x 8 chris chris 4096 Dec 16 11:38 .git
-rw-rw-r-- 1 chris chris  288 Dec 16 09:13 .gitignore
drwxrwxr-x 3 chris chris 4096 Dec 16 11:34 .idea

All I have left now is the git root, any hidden files (. / dot files), and the .idea hidden directory (PhpStorm thingy).

Now I'm going to copy all the flex project stuff into my new / old directory (depending on your point of view).

Before doing this, be sure to delete the .git folder from your Flex project directory. This is very likely going to cause you major headaches if you accidentally copy this directory over the top of your real git directory.

# remove the flex git repo first
rm -rf /tmp/githut-flex/.git

cp -R \
   /tmp/githut-flex/* \

# and again for hidden files
# there may be a better way to do this
cp -R \
   /tmp/githut-flex/.* \

PhpStorm will need to re-index everything to be happy.

But at this point you are done. The migration is complete. It's time to reward ourselves with a biscuit and a cup of tea, and then as is always the case with software development, get started on the next major crisis :)

Code For This Video

Get the code for this video.