Automatiser le front-end dans un projet Symfony2
Dans tout projet Web, nous sommes confrontés à des tâches répétitives :
- Télécharger les différentes librairies ;
- Compiler les assets (Sass, Less, Stylus, CoffeeScript, TypeScript, …) ;
- Optimiser les images ;
- Gérer la concaténation des fichiers javascript, puis la minification ;
- Et beaucoup d’autres tâches…
Je vous propose donc de travailler simplement avec deux outils automatisant ces étapes: bower et grunt.
Pour une présentation plus complète de ces outils, je vous invite à visionner la conférence (slide ou vidéo) faite par ma collègue Claire Coloma lors du PHPTour Lyon 2014.
Section intitulée prerequisPrérequis
Avoir Node.js, bower et grunt installés.
Section intitulée bower-gestionnaire-de-packagesBower – Gestionnaire de packages
Bower est un gestionnaire de paquets pour les librairies et frameworks frontend. Autrement dit, c’est un outil qui s’occupe de récupérer les bonnes versions de chacune de vos dépendances, à l’instar de Composer dans le monde PHP.
Section intitulée grunt-gestionnaire-de-tachesGrunt – Gestionnaire de tâches
Grunt est un outil d’automatisation de tâches. Très complet, il est très souvent utilisé pour les tâches répétitives que les développeurs front doivent faire, afin de nous simplifier grandement la vie !
L’automatisation des tâches s’écrit en Javascript, et permet de faire plein de choses :
- Création de lien symbolique ;
- Concaténation de fichiers ;
- Compilation de fichiers CoffeeScript ;
- Compilation de fichiers TypeScript ;
- Compilation de fichiers sass ;
- Minification de fichiers JavaScript ;
- Minification de fichiers CSS ;
- Lancement d’un serveur PHP ;
- Lancement d’un proxy livereload ;
- Optimisation des images ;
- Gestion de fichier (copie, suppression, création …) ;
- …
Section intitulée installation-de-symfony2Installation de Symfony2
$ composer create-project symfony/framework-standard-edition /path/Site
Section intitulée supprimer-asseticSupprimer Assetic
Avec un tel outil, nul besoin d’Assetic. Vous pouvez le supprimer dans le fichier app/AppKernel.php :
new Symfony\Bundle\AsseticBundle\AsseticBundle(),
Enfin ne pas oublier de le supprimer dans la configuration app/config.yml :
assetic:
debug: "%kernel.debug%"
use_controller: false
bundles: [ ]
#java: /usr/bin/java
filters:
cssrewrite: ~
#closure:
# jar: "%kernel.root_dir%/Resources/java/compiler.jar"
#yui_css:
# jar: "%kernel.root_dir%/Resources/java/__-2.4.7.jar"
Dans le fichier app/config/parameters.yml :
use_assetic_controller: true
Suivi du fichier app/config_dev.php :
assetic:
use_controller: %use_assetic_controller%
Et enfin avec la commande de composer pour supprimer du fichier composer.json :
$ composer remove "symfony/assetic-bundle"
À titre d’exemple, nous allons travailler sur le bundle de démo fourni par Symfony2.
Section intitulée gestion-des-dependancesGestion des dépendances
Grunt s’appuyant sur différents modules, nous allons utiliser un fichier package.json
permettant de définir nos dépendances Node.js.
Ouvrez votre terminal et exécutez la commande suivante :
$ npm init
Exemple d’un fichier package.json
{
"name": "Symfony2-demo",
"version": "0.0.1",
"description": "Automatiser le front-end dans un projet Symfony2",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "lbrunet@jolicode.com",
"license": "MIT",
"dependencies": {
"grunt": "^0.4.5",
"load-grunt-tasks": "^0.6.0",
"grunt-contrib-compass": "^1.0.1",
"grunt-contrib-uglify": "^0.6.0",
"grunt-contrib-cssmin": "^0.10.0",
"grunt-contrib-watch": "^0.6.1",
"grunt-contrib-copy": "^0.6.0",
"grunt-typescript": "^0.3.8"
}
}
Section intitulée bowerBower
Nous allons créer également un fichier bower.json pour toutes nos dépendances front-end.
Ouvrez votre terminal et exécutez la commande suivante :
$ bower init
Exemple d’un fichier bower.json
{
"name": "Symfony2-demo",
"version": "0.0.1",
"authors": [
"Laurent Brunet <lbrunet à jolicode.com>"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"app/Resources/lib",
"test",
"tests"
],
"dependencies": {
"bootstrap-sass-official": "~3.2.0",
"jquery": "2.0.1",
"Fontello": "https://github.com/BrandyMint/brandymint-fontello/archive/master.zip"
}
}
Par défaut, Bower va créer un dossier bower_components
pour y stocker nos dépendances. Nous pouvons décider de définir une autre structure que celle par défaut, il vous suffit de créer un fichier .bowerrc
Exemple d’un fichier .bowerrc
{
"directory" : "app/Resources/lib",
"json" : "bower.json"
}
- directory : Indique dans quel répertoire de votre projet les composants Bower doivent être copiés.
- json : Indique le nom du fichier contenant la liste des composants qui doivent être téléchargés par Bower.
Section intitulée fichier-gitignoreFichier .gitignore
Afin d’éviter de commiter des fichiers inutiles, nous allons mettre à jour notre fichier .gitignore
en ajoutant les dossiers ajoutés par node.js et Bower.
-
/node_modules/
-
/app/Resources/lib
Section intitulée installations-des-dependancesInstallations des dépendances
Maintenant que notre projet est en place et correctement paramétré, nous allons installer nos dépendances. Pour l’exemple, nous allons utiliser :
- Bootstrap (Sass) ;
- jQuery ;
- Fontello (Pack d’icônes).
Vous pouvez récupérer le fichier bower.json plus haut et lancer cette commande :
$ bower install
Section intitulée gruntGrunt
Nous allons utiliser les modules suivants :
-
grunt
-
load-grunt-tasks
-
grunt-contrib-compass
-
grunt-contrib-watch
-
grunt-contrib-cssmin
-
grunt-contrib-copy
-
grunt-typescript
-
grunt-contrib-uglify
Vous pouvez récupérer le fichier package.json plus haut et lancer cette commande :
$ npm install
Pour pouvoir configurer et utiliser nos modules Grunt, nous devons créer un fichier Gruntfile.js
.
Exemple d’un fichier Gruntfile.js
module.exports = function(grunt) {
// Importation des différents modules grunt
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-compass');
...
// Configuration des plugins
grunt.initConfig({});
// Déclaration des différentes tâches
grunt.registerTask('default', ['tache1', 'tache2']);
};
Nous allons nous intéresser au module load-grunt-tasks que nous avons installé. Ce module sert au chargement des différents modules que allons utiliser dans notre projet. Auparavant, nous aurions dû charger chaque plugin individuellement dans notre fichier Gruntfile.js.
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-compass');
...
Mais grâce à ce module, le chargement sera automatique en ajoutant seulement cette ligne dans notre fichier Gruntfile.js.
require('load-grunt-tasks')(grunt);
Exemple d’un fichier Gruntfile.js avec le module load-grunt-tasks
module.exports = function(grunt) {
// Chargement automatique de tous nos modules
require('load-grunt-tasks')(grunt);
// Configuration des plugins
grunt.initConfig({});
// Déclaration des différentes tâches
grunt.registerTask('default', ['tache1', 'tache2']);
};
Nous allons maintenant mettre en application nos modules dans notre fichier Gruntfile.js.
Configuration du module compass
module.exports = function (grunt) {
grunt.initConfig({
compass: {
sass: {
options: {
sassDir: 'src/Acme/DemoBundle/Resources/scss',
cssDir: '.tmp/css',
importPath: 'app/Resources/lib',
outputStyle: 'expanded',
noLineComments: true
}
}
}, //end compass
});
grunt.registerTask('default', ['compass']);
}
Cette tâche va permettre la compilation des fichiers Sass en fichiers CSS en utilisant Compass.
- sassDir : Le dossier source où se trouve vos fichiers Sass.
- cssDir : Le dossier où se trouveront vos fichiers CSS après la compilation.
- outputStyle : Type de compression CSS.
- importPath : Rend possible @import de tous les fichiers du chemin spécifié.
- noLineComments : Désactive les commentaires.
Configuration du module typescript
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
grunt.initConfig({
typescript: {
base: {
src: ['src/Acme/DemoBundle/Resources/ts/*ts'],
dest: '.tmp/js',
options: {
target: 'es5', //or es3
module: 'amd', //or commonjs
sourceMap: true,
declaration: true
}
}
}
});
grunt.registerTask('javascript', ['typescript']);
}
Cette tâche va permettre la compilation des fichiers TypeScript en fichiers JavaScript.
- src : Le dossier source où se trouvent vos fichiers TypeScript.
- target : Définir le type de version de ECMAScript 'ES3' (par default) ou 'ES5'.
- dest : Le dossier où se trouveront vos fichiers javascript après la compilation. Si le nom est suivi de '.js’, alors les fichiers seront concaténés.
Configuration du module cssmin
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
grunt.initConfig({
cssmin: {
combine: {
options:{
report: 'gzip',
keepSpecialComments: 0
},
files: {
'web/built/min.css': [
'.tmp/css/**/*.css'
]
}
}
} //end cssmin
});
grunt.registerTask('css', ['compass','cssmin']);
}
Le module CSSMIN sert à concaténer et compresser tous les fichiers CSS.
- report : Le dossier source où se trouve vos fichiers TypeScript.
- keepSpecialComments : Garder ou supprimer les commentaires spéciaux, pour tout garder 'all’ (defaut), 1 pour garder le premier commentaire, et 0 pour tout supprimer.
Configuration du module watch
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
grunt.initConfig({
watch: {
css: {
files: ['src/Acme/*/Resources/scss/**/*.scss'],
tasks: ['css']
},
javascript: {
files: ['src/Acme/*/Resources/ts/*.ts'],
tasks: ['javascript']
}
}
});
grunt.registerTask('default', ['css','javascript']);
grunt.registerTask('javascript', ['typescript']);
grunt.registerTask('css', ['compass','cssmin']);
}
Vous pouvez maintenant lancer la commande :
$ grunt watch
Configuration du module uglify
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
grunt.initConfig({
uglify: {
options: {
mangle: false,
sourceMap: true,
sourceMapName: 'web/built/app.map'
},
dist: {
files: {
'web/built/app.min.js':[
'app/Resources/lib/jquery/jquery.js',
'app/Resources/lib/bootstrap-sass-official/asset/javascripts/bootstrap.js'
'.tmp/js/**/*.js'
]
}
}
}
});
grunt.registerTask('javascript', ['typescript', 'uglify']);
}
Cette tâche va permettre de minifier nos fichiers javascript. On peut aussi concaténer nos fichiers dans un ordre bien précis.
- mangle : Empêcher que tous les noms de nos variables et fonctions soient renommées.
- sourceMap : Activer la génération des fichiers source map.
- sourceMapName : Définir le chemin ainsi que le nom du fichier source map.
Nous allons maintenant voir la mise en place de la librairie Fontello récupérée avec Bower. Pour cela, nous allons créer un dossier fonts dans notre projet, où nous pourrons stocker toutes les polices de notre application.
Configuration du module copy
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
grunt.initConfig({
copy: {
dist: {
files: [{
expand: true,
cwd: 'app/Resources/lib/Fontello/fonts',
dest: 'web/fonts',
src: ['**']
}]
}
}
});
grunt.registerTask('cp', ['copy']);
}
Avec le module copy
, nous allons pouvoir copier notre font dans le dossier web/fonts
. Vous pouvez lancer la commande, si le dossier n’existe pas, il sera créé automatiquement :
$ grunt cp
Pour utiliser notre lib Fontello
, on va utiliser la tâche cssmin
afin de combiner cette librairie avec notre propre fichier .scss
. Ajouter dans un de vos fichiers .scss
ce code :
@font-face {
font-family: 'fontello';
src: url('../fonts/fontello.eot?65928365');
src: url('../fonts/fontello.eot?65928365#iefix') format('embedded-opentype'),
url('../fonts/fontello.woff?65928365') format('woff'),
url('../fonts/fontello.ttf?65928365') format('truetype'),
url('../fonts/fontello.svg?65928365#fontello') format('svg');
font-weight: normal;
font-style: normal;
}
[class^="fontello-icon-"]:before, [class*=" fontello-icon-"]:before {
font-family: "fontello";
font-style: normal;
font-weight: normal;
speak: none;
display: inline-block;
text-decoration: inherit;
width: 1em;
margin-right: .2em;
text-align: center;
font-variant: normal;
text-transform: none;
line-height: 1em;
margin-left: .2em;
}
Nous pouvons donc utiliser la tâche cssmin
afin ajouter la librairie Fontello :
/app/Resources/lib/Fontello/fontello.css
Configuration du module cssmin
cssmin: {
combine: {
options:{
report: 'gzip',
keepSpecialComments: 0
},
files: {
'web/built/min.css': [
'.tmp/css/**/*.css',
// Nouvelle ligne à ajouter
'app/Resources/lib/Fontello/fontello-codes.css'
]
}
}
},
Dans votre template Twig, vous pouvez utiliser :
<i class="fontello-icon-ambulance"></i>
Remettons à jour le fichier .gitignore
:
-
/.tmp
-
/.sass-cache
-
/node_modules/
-
/app/Resources/lib
-
/web/built
-
/web/fonts
Section intitulée vers-l-infini-et-au-delaVers l’infini et au-delà !
À l’avenir, nous allons continuer d’utiliser Grunt et Bower dans tous nos projets. Ces outils nous apportent un gain de temps réel dans notre processus de développement.
Grunt et Bower ont eu un impact énorme sur le développement d’applications côté front-end. Nous pouvons nous concentrer sur le développement d’applications Web sans avoir à télécharger manuellement toutes librairies, et pouvons effectuer toutes les tâches répétitives simplement. Cela n’empêche pas pour autant la veille constante sur d’autres outils dans le but d’optimiser et d’élargir notre workflow.
Nous avons donc pu voir Gulp, Brunch mais surtout notre choix s’est porté sur Yeoman, un outil très intéressant qui s’appuie sur d’autres librairies, et dont nous parlerons prochainement.
Commentaires et discussions
Un Brunch avec Symfony2 !
Sur un de mes projets client, l’une de mes premières tâches était la mise en place des différents outils pour le frontend, seul pré-requis technique donné par le client concernant cette tâche : ne pas utiliser Gulp. J’ai donc mis en place Grunt, mais lors de nos essais de build automatisé…
Yo, crée tes projets Symfony2 sur mesure
Si vous suivez un peu l’actualité du blog, vous avez dû apercevoir mon précédent article sur automatiser le front-end dans un projet Symfony2. On y voit que la mise en place d’un workflow front-end moderne avec Symfony2 peut prendre du temps : mise en place des dépendances ou librairies…
Lire la suite de l’article Yo, crée tes projets Symfony2 sur mesure
Nos articles sur le même sujet
Ces clients ont profité de notre expertise
Nous avons développé un outil statistique complet développé pour ORPI. Basé sur PHP, Symfony et Elasticsearch, cet outil offre à toutes les agences du réseau une visibilité accrue sur leurs annonces. Il garantit également une transparence totale envers les clients, en fournissant des statistiques détaillées sur les visualisations et les contacts de…
Nous avons construit un extranet afin de de simplifier les tâches quotidiennes de gestion, que ce soit pour les utilisateurs (départements, associations, mandataires, accueillants et accueillis) et l’équipe de Cettefamille. Le socle technique utilisé est Symfony, PostgreSQL, Webpack, VanillaJS. L’emploi de ces technologies modernes permet aujourd’hui…
Ouibus a pour ambition de devenir la référence du transport en bus longue distance. Dans cette optique, les enjeux à venir de la compagnie sont nombreux (vente multi-produit, agrandissement du réseau, diminution du time-to-market, amélioration de l’expérience et de la satisfaction client) et ont des conséquences sur la structuration de la nouvelle…