The First Symfony 4 Tutorial Is Here

This week saw six new videos added to the site.

These are the final four in the Docker Tutorial for Beginners series, and the first two in the Beginners Symfony 4 Tutorial series.

A tutorial on Symfony 4 has been highly requested since it released on 30th November 2017.

Since then we’ve had three patches, so we’re now on Symfony 4.0.3.

Have you tried it yet?

I Had My Doubts

I will be completely honest, for the longest time Symfony 4 scared me.

We switched from the Symfony 2 / Symfony 3 Standard Edition, to the Symfony 4 skeleton. From a nice “full stack” to a minimal, bare bones starting point.

The first time I used Symfony 4, I just couldn’t wrap my head around why they would remove most of the very useful things:

  • Monolog
  • Twig
  • Routing…

And then the more I read, the more I thought that the symfony/skeleton was taking Symfony to become a replacement Silex. And whilst I have used and quite like Silex, I preferred the Symfony Standard Edition / full-stack approach.

I also went into a panic as I thought pretty much everything on the CodeReviewVideos.com would be obsolete.

Moar Skellingtons

Thankfully, all my fears were eliminated when they released the symfony/website-skeleton.

For me, this is perfect.

All the benefits of why I use Symfony for all my web projects, with all the added new features and bug fixes.

And a bare bones edition for my command line apps.

My Favourite Video

Even though this is a Symfony 4 beginners series, the last two videos get a little geeky. I can’t help myself.

There’s a really interesting change in Symfony 4 with the way we use controllers.

It’s useful to know as a beginner, and hopefully it sparks your curiosity into knowing more about the “how” and “why” of Symfony generally.

This one should be useful even if you’re not a beginner.

Other Stuff

There’s a lot of other change in progress at the moment, particularly on the back end of the site.

I’ve been putting some of the front end tweaks live this morning, and these take on board the suggestions I’ve had from site visitors. Thank you for all your feedback I really appreciate it.

There are still 15 or so videos to come from this batch. I’m currently taking a break from editing to write this.

Ok, on that note, have a great weekend, and happy coding.

Chris – CodeReviewVideos.com

How I Fixed: “Authentication request could not be processed due to a system problem. “

Sometimes the simple stuff seems the hardest.

In my case I am working through the Symfony 4 update to the Symfony Deployment course and somewhat unusually, am not doing anything in dev, but lots of stuff in prod.

The reason for this is that I have forked the official Symfony demo project, and have slightly modified it for my needs. As it’s an official demo project, it’s pretty much guaranteed to work.

I had a production server set up, and had deployed my code.

I’d set up my environment variables – I believed, correctly. But when I hit the site:

“Authentication request could not be processed due to a system problem.”

I do enjoy a good error message.

The fix was simple in my case. I had set the wrong path to the database:

DATABASE_URL=mysql://dbuser:dbpassword@127.0.0.1:3306/db_dev

And what it should have been:

DATABASE_URL=mysql://dbuser:dbpassword@127.0.0.1:3306/dev_db

The db name was back to front.

In prod, the log files give no help around this, at all.

I had to spin up the dev environment where things are a lot more evident:

[2018-01-12 12:50:22] request.INFO: Matched route "security_login". {"route":"security_login","route_parameters":{"_controller":"App\\Controller\\SecurityController::login","_locale":"en","_route":"security_login"},"request_uri":"http://127.0.0.1:8000/en/login","method":"POST"} []
[2018-01-12 12:50:22] security.INFO: Authentication request failed. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AuthenticationServiceException(code: 0): An exception occurred in driver: SQLSTATE[HY000] [1044] Access denied for user 'dbuser'@'%' to database 'dev_db' at /tmp/symfony-4-demo-app/vendor/symfony/security/Core/Authentication/Provider/DaoAuthenticationProvider.php:85, Doctrine\\DBAL\\Exception\\ConnectionException(code: 0): An exception occurred in driver: SQLSTATE[HY000] [1044] Access denied for user 'dbuser'@'%' to database 'dev_db' at /tmp/symfony-4-demo-app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php:108, Doctrine\\DBAL\\Driver\\PDOException(code: 1044): SQLSTATE[HY000] [1044] Access denied for user 'dbuser'@'%' to database 'dev_db' at /tmp/symfony-4-demo-app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:47, PDOException(code: 1044): SQLSTATE[HY000] [1044] Access denied for user 'dbuser'@'%' to database 'dev_db' at /tmp/symfony-4-demo-app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:43)"} []
[2018-01-12 12:50:22] security.DEBUG: Authentication failure, redirect triggered. {"failure_path":"security_login"} []
[2018-01-12 12:50:22] request.INFO: Matched route "security_login". {"route":"security_login","route_parameters":{"_contr

The give away line being:

Access denied for user 'dbuser'@'%' to database 'dev_db'

Typos, such fun.

Anyway, easier to fix than it might otherwise appear. Hopefully it is for you, too.

 

Symfony with Redis

In a bid to make getting up and running with CodeReviewVideos tutorials moving forwards, I have created a new repo called the Docker and Symfony3 starting point.

I will be using this as the basis for all projects going forwards as it dramatically simplifies the process of setting up each tutorial series, and should – in theory – make reliably reproducing my setups much easier for you.

I’ve had a lot of feedback lately to say it’s increasingly hard work to follow some of the older tutorials as the dependencies are out of date.

Taking this feedback on board, I will do my best to update these projects in the next few weeks-to-months. I am, however, going to wait for Symfony 4 to land before I spend any time updating stuff. No point duplicating a bunch of effort. Symfony 4 drops in November, in case you are wondering.

Video Update

This week saw two new videos added to the site. Unfortunately I was ill early in the week so didn’t get to record the usual third video.

#1 – [Part 1/2] – Symfony 3 with Redis Cache

Caching is a (seemingly) easy win.

Imagine we have some expensive operation: maybe a heavy computation, or some API request that needs to go across the slow, and unpredictable internet.

Wouldn’t it be great if we did this expensive operation only once, saved (or cached) the result, and then for every subsequent request, we sent back the locally saved result.

Yes, that sounds awesome.

Symfony has us covered here. The framework comes with a bunch of ways we can cache and store data.

Redis seems to be the one I find most larger organisations like to use, so that’s one of the reasons behind picking Redis out of the bunch.

At this point you may be thinking:

But Chris, I don’t have a Redis instance just laying around waiting for use, and I’m not rightly sure how I might go about setting one up!

Well, not to worry.

To make life as simple as possible, we are going to use Docker.

Docker… simple… the same sentence?!

Well, the jolly good news is that you don’t need to know anything about Docker to use this setup. At least, I hope you won’t. That’s the theory.

As mentioned above, I am making use of the new Docker / Symfony 3 starting point repo. I’ve tweaked this a touch for our Redis requirements.

By the end of this video you will have got a Symfony website up and running, and cached data into Redis. We will cover the setup required, both from a Docker perspective, and the config needed inside Symfony.

#2 – [Part 2/2] – Symfony 3 with Redis Cache

In the previous video we put all our caching logic into a controller action.

That’s really not going to cut it beyond your first steps. In reality you’re going to want to move this stuff out into a separate service.

To give you an example of how I might do it:

Controller calls CacheApiWrapper which calls Api if needed.

That might not be making much sense, so let’s break it down further.

Let’s imagine an app without caching.

We need to call a remote API to get the days prices of Widgets.

We hit a controller action which delegates the API call off to our Api service, which really manages the API call for us. When the API call completes (or fails), it returns some response to the calling controller action, which in turn returns the response to the end user.

If that API call always returns the same response every time it is called – maybe the prices of Widgets only change every 24 hours – then calling the remote API for every request is both pointless, and wasteful.

We could, if we wanted to, add the Caching functionality directly into the Api service.

I don’t like this approach.

Instead, I prefer to split the responsibility of Caching out into a separate ‘layer’. This layer / caching service has an identical API to the Api service. This caching service takes the Api service as a constructor argument, and wraps the calls, adding caching along the way.

If all this sounds complex, after seeing some code, hopefully it will make a bit more sense:

class WidgetController extends Controller
{
    /**
     * @Route("/widgets", name="widget_prices")
     */
    public function widgetPriceAction(
        WidgetPriceProvider $widgetPriceProvider
    )
    {
        return $this->render(
            ':widget:widget_price.html.twig',
            [
                'widget_prices' => $widgetPriceProvider->fetchWidgetPrices(),
            ]
        );
    }

This should hopefully look familiar.

The unusual part is the WidgetPriceProvider. Well, they do say the two hardest parts of computer science are cache invalidation, and naming things…

Really WidgetPriceProvider is the best name I could think of to explain what’s happening. This service provides Widget Prices.

How it provides them is where the interesting part happens:

<?php

namespace AppBundle\Provider;

use AppBundle\Connectivity\Api\WidgetPrice;
use AppBundle\Widget\WidgetPriceInterface;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\Adapter\AdapterInterface;

class WidgetPriceProvider implements WidgetPriceInterface
{
    /**
     * @var CacheItemPoolInterface
     */
    private $cache;

    /**
     * @var WidgetPrice
     */
    private $widgetPriceApi;

    public function __construct(
        AdapterInterface $cache, 
        WidgetPrice $widgetPriceApi
    )
    {
        $this->cache          = $cache;
        $this->widgetPriceApi = $widgetPriceApi;
    }

    public function fetchWidgetPrices()
    {
        $cacheKey = md5('fetch_widget_prices');

        $cachedWidgetPrices = $this->cache->getItem($cacheKey);

        if (false === $cachedWidgetPrices->isHit()) {

            $widgetPrices = $this->widgetPriceApi->fetchWidgetPrices();

            $cachedWidgetPrices->set($widgetPrices);
            $this->cache->save($cachedWidgetPrices);

        } else {

            $widgetPrices = $cachedWidgetPrices->get();

        }

        return $widgetPrices;
    }
}

As mentioned, WidgetPriceProvider is a wrapper / layer over the Api Service.

It also has the cache injected.

This way the API call is separated from the cache, and can be used directly if needed. Sometimes I don’t want to go via the cache. Maybe in the admin backend, for example.

Note that this service implements WidgetPriceInterface. This isn’t strictly necessary. This interface defines a public function of fetchWidgetPrices.

The reason for this is, as mentioned earlier, I want the caching layer (WidgetPriceProvider) to use the same method name(s) as the underlying API service.

Speaking of which:

<?php

namespace AppBundle\Connectivity\Api;

use AppBundle\Widget\WidgetPriceInterface;
use GuzzleHttp\Client;
use Psr\Log\LoggerInterface;

class WidgetPrice implements WidgetPriceInterface
{
    /**
     * @var LoggerInterface
     */
    private $logger;
    /**
     * @var Client
     */
    private $client;

    /**
     * WidgetPrice constructor.
     *
     * @param LoggerInterface $logger
     * @param Client          $client
     */
    public function __construct(
        LoggerInterface $logger,
        Client $client
    )
    {
        $this->client = $client;
        $this->logger = $logger;
    }

    public function fetchWidgetPrices()
    {
        // better to inject via constructor, but easier to show like this
        $url = "https://api.widgetprovider.com/prices.json";

        try {

            return json_decode(
                $this->client->get($url)->getBody()->getContents(),
                true
            );

        } catch (\Exception $e) {

            $this->logger->alert('It all went Pete Tong', [
                'url'       => $url,
                'exception' => [
                    'message' => $e->getMessage(),
                    'code'    => $e->getCode()
                ],
            ]);

            return [];
        }
    }
}

So really it’s about delegating responsibility down to a more appropriate layer.

Be Careful – don’t blindly copy this approach.

There is a gotcha in this approach which maybe not be immediately obvious.

If your API call fails, the bad result (and empty array in this case) will still be cached.

Adapt accordingly to your own needs. It may be better to move the try  / catch logic out from the WidgetPrice class and into the WidgetPriceProvider instead.

Or something else. It’s your call.

Anyway, that’s an excursion into a potentially more real world setup.

Hopefully you find this weeks videos useful.

As every, have a great weekend and happy coding.

Chris

Symfony Access Decision Manager

Unamaous.

Unamouse.

Unamimous!

Yeah, I can’t spell.

I just hit upon a problem whereby I needed my Symfony security voter setup to always deny access even when one of my voters returned an Affirmative.

In this case I want to always deny.

By default Symfony will allow access if at least one voter returns GRANTED.

If you want to know more, check out this video I created about this very problem, and some potential solutions.

How I am using Docker with Symfony, nginx, and MySQL

Behind the scenes this week I have been continuing my work on the next major iteration of the CodeReviewVideos website.

I’ve split the site into two parts:

  • A Symfony 3 powered JSON API for the back-end.
  • React / Redux / Redux Saga for the front-end

It’s a stack that I really enjoy working with, and is all stuff I’ve covered here on the site before.

Progress has gone fairly well this week. As a developer I always expect to be further ahead than I actually am. Partly this is impatience, and partly because I am still not so good at estimating 🙂

By next week’s newsletter I am hoping to be able to share access to the “beta” (for want of a better word) with existing site subscribers.

Become An Instructor

I really want to make CodeReviewVideos as useful to you as possible.

There are likely dozens of topics you’d like to see covered that either I have not yet had chance to record videos for, or do not have any experience in.

Topics like Angular 2/4, Vue, WordPress, Magento… all the things that we as developers have to work with in our real-world, day-to-day jobs.

If you have ever wanted to start recording screencasts / tutorials then this may well be the perfect opportunity to do so, and earn some extra recurring income in the process.

If you’d like to know more then please send an email to chris@codereviewvideos.com.

Video Update

This week saw three new videos added to the site.

#1. How to add a Flash Message on Successful, or Failed Login

I got asked this question:

how can I “$this->addFlash” for login? Because now its working only with registration. How can I make it work with login? Check if signing in was successful/not successful?

There is a solution built into Symfony’s security bundle (think: Login form configuration) to make this fairly easy to accomplish.

There is just one small problem: lack of documentation / example.

I’m hoping to correct that with this video. Before going too much further, you may be wondering:

What are Flash Messages?

Flash messages in Symfony are simply one time messages – notifications, by another name – that are stored in the session and are removed as soon as they have been shown once.

If still unsure, I have a video called the Beginners Guide to Symfony Flash Messages. It’s free to watch.

Symfony 3.3 comes with some improvements to the way Flash messages work.

We cover how to add a Flash Message using the success_handler and failure_handler services. The tricky part is that these two services must behave a certain way, and this video covers one approach to this problem.

I’m always open to answering questions like this, so if you have any that you feel would be best answered by video then do please send them in.

#2.  [Demo] – Docker with Symfony, nginx, and MySQL

Docker.

It’s popular.

It’s good for the CV.

It’s also complicated.

In this video we take a look at a Dockerised environment in which the latest version of Symfony 3, an nginx webserver, and a MySQL database can be “spun-up” in next to no time.

The idea here is to take a look at what’s possible, rather than me saying: Hey, this is all you need to know.

I’ve planned out the Docker series and it’s going to be a few shorter series that can be watched independently, or all together.

There are a bunch of topics in Docker that you can initially skip over. Only as and when you have a need for either a more complex setup, or want to satisfy your curiosity do you need to delve any deeper.

This is good, in my opinion, as Docker itself is both broad and deep, and if we covered all the fundamentals upfront then you may be sound asleep by the end of the first few videos.

Because seeing code is fun, here’s the docker-compose.yml file that this video covers:

# docker-compose.yml

version: '2'

services:

    db:
        image: mysql:5.7.17
        env_file:
          - ./.env
        volumes:
          - "./volumes/mysql/dev:/var/lib/mysql"
        networks:
          crv_dev_network:
            aliases:
              - mysql

    nginx:
        image: codereviewvideos/nginx.dev
        ports:
          - "81:80"
        restart: always
        volumes:
          - "./volumes/nginx/logs:/var/log/nginx/"
        volumes_from:
            - php
        networks:
          crv_dev_network:
            aliases:
              - nginx

    php:
        image: codereviewvideos/www.dev
        restart: always
        env_file:
          - ./.env
        volumes:
          - "./volumes/php/app/cache:/var/www/dev/app/cache/:rw"
          - "./volumes/php/app/logs:/var/www/dev/app/logs/:rw"
          - "./volumes/.composer:/var/www/.composer"
          - "./app:/var/www/dev"
        networks:
          crv_dev_network:
            aliases:
              - php

networks:
  crv_dev_network:
    driver: bridge

You can watch the video here, and then hit the Repo over on GitHub.

We will cover everything in this repo piece by piece, building more than just a Symfony stack in the process.

#3. Linux Permissions 101

Oh my, this sounds dull.

Yes, I hear you.

There are parts of Development that sound sexy and fun (e.g. Docker, the latest JS framework, real-time mobile apps, etc), and then there are the dry and boring sounding topics that unfortunately, you really need to know.

I realise permissions aren’t much fun.

That’s why I have compressed the essential knowledge down into a 4 minute long video.

If you have ever seen something like this:

➜  ~ ls -la Development/docker/nginx

total 24
drwxrwxr-x  4 www-data www-data 4096 Apr  3 19:43 .
drwxrwxr-x 74 chris    chris    4096 Aug 17 20:15 ..
drwxrwxr-x  2 www-data www-data 4096 Apr  3 19:37 conf.d
-rwxrwxr-x  1 www-data www-data  137 Apr 18 20:51 Dockerfile
-rwxrwxr-x  1 www-data www-data   13 Apr  3 19:42 .dockerignore
drwxrwxr-x  8 www-data www-data 4096 Aug 19 12:05 .git

And you have wondered just what the heck most of this means then this video will see you right.

To work with Docker it is essential you understand just a small amount of this output. Most every problem I have had with Docker has been related to permissions. Trust me on this, if you want to use Docker then you need to know this stuff.

And the good news is that it’s really not that bad. And it’s useful beyond working with Docker too. Win win!

Until next week, have a great weekend, and happy coding.

Chris