Creating a COVID-19 Data Visualization with Symfony UX
Quentin Ferrer
Posted on January 14, 2021
- Creating the Project
- Lauching the Local Web Server
- Installing Webpack Encore
- Installing UX Chart.js
- Creating the Covid-19 Http Client
- Creating the Covid Controller
- Rendering the Chart
At the beginning of December, Symfony started the keynote with the presentation of Symfony UX, a new JavaScript ecosystem for Symfony.
Symfony UX is a series of tools to create a bridge between Symfony and the JavaScript ecosystem.
To get a full overview of the initiative, you can also watch the Symfony World replay, especially the Fabien’s keynote and Titouan’s talk.
For now, Symfony provides 5 packages:
- UX Chart.js
- UX Cropper.js
- UX Dropzone
- UX LazyImage
- UX Swup
In this tutorial, I will introduce you to the UX Chart.js package by graphing some COVID-19 data with the Chart.js library. To do this, we will create a line chart that will display the total number of cases and deaths by country from a free Covid-19 API.
Creating the project
First of all, we need to set up and configure a project:
$ symfony new covid --full
$ cd covid/
Launching the Local Web Server
Start a local web server executing the command:
$ symfony server:start
For the tutorial, we will suppose that the webserver is listening to http://localhost:8000.
Installing Webpack Encore
As we will use a JavaScript library, we need to manage JavaScript in Symfony using Webpack:
$ symfony composer req symfony/webpack-encore-bundle
$ yarn install
Symfony now integrates Stimulus to organize JavaScript code inside projects. If you take a look at the assets/
directory, you can see a new JavaScript directory structure:
-
controllers/
: it contains Stimulus controllers of the application. They are automatically registered inapp.js
, -
controllers.json
: it references Stimulus controllers provided by installed Symfony UX packages.
Installing UX Chart.js
Let's install our first UX package:
$ symfony composer req symfony/ux-chartjs
Symfony Flex has just added a reference to the Javascript code of UX-Chart.js in the package.json
:
{
"devDependencies": {
"@symfony/ux-chartjs": "file:vendor/symfony/ux-chartjs/Resources/assets"
},
}
Symfony Flex also added a reference to the Stimulus controller of UX-Chart.js in the assets/controllers.json
:
{
"controllers": {
"@symfony/ux-chartjs": {
"chart": {
"enabled": true,
"webpackMode": "eager"
}
}
},
"entrypoints": []
}
Because of these changes, we now need to install the new JavaScript dependencies and compile the new files:
$ yarn install
$ yarn encore dev
Now, the UX package is ready.
Creating the Covid-19 Http Client
Thanks to a free Covid-19 API (https://api.covid19api.com), we will able to fetch the total number of cases and deaths by country by using the following endpoint:
GET https://api.covid19api.com/total/country/$country
$country must be the slug from https://api.covid19api.com/countries.
Symfony provides a HttpClient component to consume APIs. Add a scoped client to auto-configure the client based on the requested URL:
# config/packages/framework.yaml
framework:
http_client:
scoped_clients:
covid:
base_uri: https://api.covid19api.com
The covid
client will have a unique service named covid
.
Create a CovidHttpClient
service that will be responsible for fetching the total number of cases and deaths by country and group all by date.
<?php
namespace App\HttpClient;
use Symfony\Contracts\HttpClient\HttpClientInterface;
/**
* Class CovidHttpClient
* @package App\Client
*/
class CovidHttpClient
{
/**
* @var HttpClientInterface
*/
private $httpClient;
/**
* CovidHttpClient constructor.
*
* @param HttpClientInterface $covid
*/
public function __construct(HttpClientInterface $covid)
{
$this->httpClient = $covid;
}
/**
* Get total number of cases and deaths by the given country.
*
* @param string $country
*
* @return array
*/
public function getTotalByCountry(string $country): array
{
$response = $this->httpClient->request('GET', "/total/country/$country");
$data = json_decode($response->getContent(), true);
$total = [];
foreach ($data as $dailyData) {
$date = (new \DateTime($dailyData['Date']))->format('Y-m-d');
$total[$date] = $dailyData;
}
return $total;
}
}
As we have an argument $covid
as HttpClientInterface
type, autowiring inject the covid
service into the class.
We are now ready to build the chart.
Creating the Covid Controller
Create the controller using the Maker bundle:
symfony console make:controller CovidController
The command creates a CovidController
class under the src/Controller/
directory and a template file to templates/covid/index.html.twig
.
In the CovidController
, implement the index()
method:
- Fetch the total number of cases and deaths by country using the
CovidHttpClient
service and group all by status; - Create a
Chart
object by using theChartBuilderInterface
builder; - Set the data (labels & datasets) to the
Chart
object; - Finally, pass the
Chart
object to the Twig templatecovid/index.html.twig
.
<?php
namespace App\Controller;
use App\HttpClient\CovidHttpClient;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\UX\Chartjs\Builder\ChartBuilderInterface;
use Symfony\UX\Chartjs\Model\Chart;
class CovidController extends AbstractController
{
/**
* @Route("/{country}", name="covid")
*/
public function index(CovidHttpClient $covidClient, ChartBuilderInterface $chartBuilder, $country = 'france'): Response
{
$total = $covidClient->getTotalByCountry($country);
$totalByStatus = [];
foreach ($total as $dailyTotal) {
$totalByStatus['confirmed'][] = $dailyTotal['Confirmed'];
$totalByStatus['deaths'][] = $dailyTotal['Deaths'];
$totalByStatus['recovered'][] = $dailyTotal['Recovered'];
$totalByStatus['active'][] = $dailyTotal['Active'];
}
$chart = $chartBuilder->createChart(Chart::TYPE_LINE);
$chart
->setData([
'labels' => array_keys($total),
'datasets' => [
[
'label' => 'Confirmed',
'backgroundColor' => 'rgb(120, 161, 187, 0.5)',
'data' => $totalByStatus['confirmed']
],
[
'label' => 'Death',
'backgroundColor' => 'rgb(219, 80, 74, 0.5)',
'data' => $totalByStatus['deaths']
],
[
'label' => 'Recovered',
'backgroundColor' => 'rgb(147, 196, 139, 0.5)',
'data' => $totalByStatus['recovered']
],
[
'label' => 'Active',
'backgroundColor' => 'rgb(252, 191, 73, 0.5)',
'data' => $totalByStatus['active']
]
]
]);
return $this->render('covid/index.html.twig', [
'chart' => $chart,
'country' => $country
]);
}
}
You can read Chart.js documentation to discover all options.
Rendering the Chart
The last step is to update the templates/covid/index.html.twig
file:
{% extends 'base.html.twig' %}
{% block body %}
<h1>Total number of cases and deaths in {{ country|capitalize }}</h1>
{{ render_chart(chart) }}
{% endblock %}
It's done! Go to the homepage by specifying the country parameter. The list of countries is available on https://api.covid19api.com/countries.
Here are some examples:
Posted on January 14, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.