Titouan Galopin
Responsable produit de SensioLabsInsight
Ancien responsable produit de la plateforme Open-Source en-marche.fr
Fondateur de compibus.fr
De l’avis de très nombreux utilisateurs de Symfony dans le monde, la dernière version du framework (Symfony 4) est de loin la plus avancée. Sortie le 30 novembre 2017, elle introduit des changements profonds de paradigme dans la façon de créer des applications Symfony, en permettant notamment au framework de mieux s’intégrer à son écosystème (Docker, Single-Page applications, Hébergement Cloud, etc.). Ces changements seront l’occasion de nombreux articles, aussi bien sur le blog officiel que dans la communauté en général.
Nous nous concentrerons dans cet article sur deux fonctionnalités introduites récemment dans le framework : Symfony Flex, qui généralise l’utilisation du framework à d’autres contextes que l’affichage de pages Web, et Symfony Webpack Encore qui permet de développer aisément en Javascript côté client. En alliant ceux-ci à Phonegap, nous allons découvrir une façon intéressante de développer des applications mobiles rapidement et aisément maintenables.
À titre personnel, j’ai développé à la fin de l’année 2015 une application mobile d’information à propos des horaires de bus de la ville dans laquelle j’habite (Compiègne, https://compibus.fr). À ses débuts, cette application n’affichait que les horaires des prochains bus à un arrêt recherché par l’utilisateur. Elle s’est depuis enrichie de nouvelles fonctionnalités, mais je souhaiterais aborder ici la manière dont j’ai re-développé la base de l’application avec Symfony Flex, Webpack Encore et Phonegap.
Là où l’édition Standard de Symfony était taillée pour les applications Web (un dossier
web
, des contrôleurs, des vues, etc.), Symfony Flex est beaucoup plus versatile et
s’applique à de nombreux types des projets PHP, du projet Web au daemon en passant par le CRON et
le consumer RabbitMQ.
Une caractéristique particulière des horaires de transport en commun est qu’ils sont tout à fait publics : ils n’ont pas de restriction d’accès, quiconque peut les lire.
Cette caractéristique importante permet d’utiliser Symfony non pas pour développer une API directement, mais pour développer un outil en ligne de commande : au lieu d’exposer directement Symfony au Web, Symfony est utilisé comme framework de daemon PHP tenant à jour des fichiers JSON, eux-mêmes servis par nginx.
Cette structuration a de nombreux avantages :
Symfony Flex s’adapte particulièrement bien à ce genre d’utilisation plus exotique : en ne requérant aucun composant lié au Web, il est possible de créer un outil parfaitement adapté à un usage en ligne de commande, et ce tout en se basant sur des standards et des librairies de qualité.
Notez qu’il n’y a pas de dossier public
ni de contrôleurs dans l’application décrite ci-contre.
Pour créer cette structure de fichiers, il m’a suffit de lancer quelques commandes et d’implémenter la logique métier d’import des données depuis l’API de l’opérateur du réseau de bus de Compiègne.
# Créer le projet
composer create-project symfony/skeleton afsy && cd afsy
# Monolog et son bundle
composer req logger
# Doctrine, Doctrine Migrations et Doctrine Fixtures
composer req orm migrations orm-fixtures
# SensioFrameworkExtraBundle
composer req annotations
# MakerBundle, pour créer plus rapidement la structure de fichiers
composer req maker --dev
# PHPUnit et le bridge Symfony, pour les tests
composer req phpunit --dev
Une fois ma commande de daemon développée, j’ai ajouté un container Docker de production pour la lancer. Une boucle infinie dans cette commande permet alors l’écoute de l’API de l’opérateur.
Une fois le daemon créé, tenant des fichiers JSON à jour, il faut s’attaquer à la partie cliente : l’application mobile.
Différentes approches existent pour développer des applications mobiles :
Dans le cas de Compibus, j’ai utilisé Cordova/Phonegap, afin de partager le plus de code possible entre les plateformes. Un autre effet de l’utilisation de cet outil est la possibilité de créer une version Web en utilisant le même code que la version mobile, ce qui est particulièrement utile dans le cas d’une application de transport en commun.
Utiliser Phonegap implique de développer une Single-Page Application en HTML/CSS/Javascript. Or, depuis maintenant quelques mois, une librairie Symfony répond parfaitement à ce besoin : Symfony Webpack Encore.
Webpack Encore est une librairie Javascript créée et gérée par l’équipe de Symfony, notamment par Ryan Weaver. Cette librairie a pour objectif de simplifier l’utilisation et la configuration de Webpack, connu pour être un outil complexe qui dispose d’une grande configuration.
L’objectif de Webpack est de “préparer” votre code à être exécuté en production, en lui appliquant différents traitements. Il résout les “import” en Javascript afin d’agréger tous vos fichiers sources en un nombre limité de fichiers transférables facilement à travers le réseau. Il est aussi capable de minifier vos fichiers, de les transformer dans une version plus vieille de Javascript pour qu’ils puissent être utilisés par de vieux navigateurs, etc.
En utilisant Webpack Encore, vous allez pouvoir en quelques lignes de code utiliser Typescript, React JSX, SASS et autres outils dont vous avez probablement déjà entendu parler.
L’intérêt de Webpack et Webpack Encore réside notamment dans le fait qu’ils permettent au développeur d’utiliser toutes les nouveautés de Javascript sans avoir peur de manquer de compatibilité avec les anciennes versions du langage. Cela est particulièrement important dans le contexte de Phonegap, car ce dernier utilise le navigateur Web natif du téléphone pour afficher l’application, ce qui peut amener à un support très hétéroclite des nouvelles fonctionnalités de Javascript.
Dans Compibus, j’ai fait le choix d’utiliser Typescript parce que ce langage dispose d’outils avancés de Programmation Orientée Objet, permettant par exemple la mise en place d’injection de dépendances à la manière de Symfony. J’ai aussi choisi d’utiliser React et Redux comme framework Javascript que j’apprécie particulièrement, ainsi que SASS pour éviter la redondance de CSS.
Pour créer cette structure de fichiers, il m’a suffit de lancer quelques commandes, de créer le fichier de configuration pour Webpack et d’implémenter la logique cliente d’interaction avec les fichiers JSON générés par l’application Symfony.
# Créer le projet et installer Webpack Encore
yarn init
yarn add @symfony/webpack-encore --dev
# Typescript
yarn add --dev typescript ts-loader
# SASS
yarn add --dev sass-loader node-sass
# PostCSS
yarn add --dev postcss-loader autoprefixer
# React et les types Typescript pour React
yarn add --dev react react-dom prop-types babel-preset-react @types/react @types/react-dom
Mon fichier webpack.config.js
, permettant de configurer Webpack, ressemble alors à ceci :
const Encore = require('@symfony/webpack-encore');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackHarddiskPlugin = require('html-webpack-harddisk-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const availableTargets = ['browser', 'phonegap'];
let target = 'browser';
if (typeof process.env.TARGET !== 'undefined' && availableTargets.indexOf(process.env.TARGET) > -1) {
target = process.env.TARGET;
}
console.log('Building '+target+' distribution...');
console.log('');
Encore
.setOutputPath('build/'+target)
.setPublicPath('/')
.cleanupOutputBeforeBuild()
.addEntry('app', './src/loader.tsx')
.enableSassLoader()
.enableTypeScriptLoader()
.enablePostCssLoader((options) => {
options.config = {
path: 'postcss.config.js'
};
})
.enableReactPreset()
.enableVersioning(Encore.isProduction())
.enableSourceMaps(!Encore.isProduction())
.addPlugin(new HtmlWebpackPlugin({
template: Encore.isProduction() ? 'src/Template/index.'+target+'.ejs' : 'src/Template/index.dev.ejs',
alwaysWriteToDisk: true
}))
.addPlugin(new HtmlWebpackHarddiskPlugin())
.addPlugin(new CopyWebpackPlugin([
{
from: 'res/'+target,
to: '',
},
]))
;
let config = Encore.getWebpackConfig();
config.output.publicPath = '';
module.exports = config;
Grâce à Webpack Encore, il est très simple d’utiliser des outils avancés de développement front-end. Notez l’utilisation dans la configuration du versionning (le nom des fichiers inclut un hash md5 pour permettre une stratégie de cache HTTP aggressive).
Il est aussi intéressant de remarquer qu’en utilisant une variable d’environnement, il est
possible de changer de cible à fabriquer (browser
ou phonegap
), et
donc de choisir le template adapté : TARGET=phonegap yarn encore prod
.
J’ai souhaité écrire cet article pour une raison importante : j’ai toujours pensé qu’il existait une certaine forme de balance entre les frameworks basés sur le développement rapide (RAD, basé sur les conventions, cherchant à l’efficacité en priorité) et ceux basés sur une approche plus standard (SOLID, basé sur la configuration, cherchant à la maintenabilité en priorité). On pourrait par exemple opposer ainsi Ruby on Rails et Spring, qui ont deux approches différentes dans leur objectif en tant que framework.
Jusqu’à présent, je voyais la même différence entre Laravel et Symfony : Laravel est beaucoup plus basé sur les idées de Ruby on Rails, là où Symfony est beaucoup plus basé sur les idées de Spring.
J’ai cependant le sentiment que Symfony, avec Symfony Flex, est en train de combler cette différence. En gardant son esprit basé sur la configuration, une grande qualité de développement et la solidité de son architecture interne, Symfony 4 propose une nouvelle approche au développement rapide d’applications (Symfony Flex, les nouveautés du composant DI, Webpack Encore, etc.). C’était d’ailleurs un objectif de cet article que de montrer comment il est désormais possible de très rapidement mettre en place les bases de toute application, Web ou non, tout en restant dans un contexte de suivi des bonnes pratiques de la programmation orientée objet.
Cela augure de très belles choses pour la communauté Symfony et pour le monde de PHP en général !