OPTIMISING YOUR FRONT END WORKFLOW
MATTHEW DAVIS @mdavis1982
PhpSpec phpspec/nyan-formatters
PhpSpec R! WW AW R phpspec/nyan-formatters
Terrible at jokes
Terrible at jokes Sorry!
Advances in back end code
D DD COM POSI TION ENICS STH CALI Advances in back end code Y R D TDD PSR ES FAC TER IN SOLID
COMPOSER
When we look at front end code…
Cool tools for front end code
BOWER S S LE SASS ANGULAR GRU NT I PT R for front end code SC E tools Cool FFE CO IG W GU T LP N MA EO Y REQUI RE JS
* more And a GAZILLION
* more And a GAZILLION *approximately
Lack of practical examples
😢
Let’s change that!
😄
Example Project https://github.com/mdavis1982/workflow
Example Project Simple Article Management https://github.com/mdavis1982/workflow
Example Project Simple Article Management Administration Area https://github.com/mdavis1982/workflow
Twig
Insanely powerful
Insanely powerful Compiled and cached
Insanely powerful Compiled and cached Often overlooked
Translate all the things
Translations are notoriously messy
But it’s easy to keep them organised
config.yml framework:
 translator: { fallback: "%locale%" }
Article.php /**
 * The title of the Article
 *
 * @var string
 *
 * @ORMColumn(type="string", length=255)
 *
 * @AssertNotBlank(message="article.title.not_blank")
 * @AssertLength(
 * max=255,
 * maxMessage="article.title.length.max"
 * )
 */
 protected $title;
validators.en.yml article:
 title:
 not_blank: You must enter a title
 length:
 max: The title cannot be longer than {{ limit }} characters
 
 content:
 not_blank: You must enter some content

ArticleType.php $builder
 ->add(
 'title',
 'text',
 [
 'label' => 'article.label.title'
 ]
 )
ArticleType.php $builder
 ->add(
 'title',
 'text',
 [
 'label' => 'article.label.title'
 ]
 ) For all properties in the form
forms.en.yml article:
 label:
 title: Title
 content: Content
 submit: Save

forms.en.yml?!
ArticleType.php /**
 * {@inheritdoc}
 */
 public function setDefaultOptions(OptionsResolverInterface $resolver)
 {
 $resolver->setDefaults([
 'data_class' => 'MDEntityArticle',
 'translation_domain' => 'forms'
 ]);
 }
When your translations don’t work…
CLEAR THE CACHE!
CLEAR THE CACHE! Even in the dev environment
Translate everything
Translate everything All titles, actions, single words
Translate everything All titles, actions, single words Translations per ‘section’
admin.en.yml article:
 title:
 list: All Articles
 create: Add a New Article
 
 action:
 create: Add New
 cancel: Cancel

list.html.twig {% block body %}
 <h1>{{ 'article.title.list'|trans({}, 'admin') }}</h1>
 {% if articles %}
 <ul class="articles">
 {% for article in articles %}
 <li>{{ article.title }}</li>
 {% endfor %}
 </ul>
 {% endif %}
 
 <a href="{{ path('admin.article.create') }}”> {{ 'article.action.create'|trans({}, 'admin') }} </a>
 {% endblock body %}
Messy
Template Inheritance
Default base template
base.html.twig <!DOCTYPE html>
 <html>
 <head>
 <meta charset="UTF-8" />
 <title>{% block title %}Welcome!{% endblock %}</title>
 {% block stylesheets %}{% endblock %}
 <link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}" />
 </head>
 <body>
 {% block body %}{% endblock %}
 {% block javascripts %}{% endblock %}
 </body>
 </html>
Doesn’t promote great code
base.html.twig <!doctype html>
 
 <!--[if lt IE 7 ]> <html lang="en" class="no-js ie6 lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
 <!--[if IE 7 ]> <html lang="en" class="no-js ie7 lt-ie9 lt-ie8"> <![endif]-->
 <!--[if IE 8 ]> <html lang="en" class="no-js ie8 lt-ie9"> <![endif]-->
 <!--[if IE 9 ]> <html lang="en" class="no-js ie9"> <![endif]-->
 <!--[if (gt IE 9)|!(IE)]><!-->
 <html lang="en" class="no-js"><!--<![endif]-->
 <head>
 <meta charset="utf-8" />
 <title>{% block title %}{% endblock title %}</title>
 
 <!-- Set up mobile viewport for responsive design -->
 <meta name="viewport" content="width=device-width" />
 
 {% block stylesheets %}{% endblock stylesheets %}
 </head>
 <body>
 {% block content %}{% endblock content %}
 {% block javascripts %}{% endblock javascripts %}
 </body>
 </html>

Gives us a better foundation
The same HTML structure and assets per ‘section’?
New ‘Layouts’ Directory
Directory Structure
admin.html.twig {% extends "::base.html.twig" %}
 
 {% block title %} {{ 'defaults.title'|trans({}, 'admin') }} {% endblock title %}
 
 {% block content %}
 {% block body %}{% endblock body %}
 {% endblock content %}

frontend.html.twig {% extends "::base.html.twig" %}
 
 {% block title %}{{ 'defaults.title'|trans }}{% endblock title %}
 
 {% block content %}
 {% block header %}{% endblock header %}
 {% block body %}{% endblock body %}
 {% block footer %}{% endblock footer %}
 {% endblock content %}
What?!
Change the extends
list.html.twig {% extends "MDMainBundle:Layouts:admin.html.twig" %}
 
 {% block body %}
 <h1>{{ 'article.title.list'|trans({}, 'admin') }}</h1>
 {% if articles %}
 <ul class="articles">
 {% for article in articles %}
 <li>{{ article.title }}</li>
 {% endfor %}
 </ul>
 {% endif %}
 
 <a href="{{ path('admin.article.create') }}”> {{ 'article.action.create'|trans({}, 'admin') }} </a>
 {% endblock body %}

Given ourselves flexible base
Given ourselves flexible base All boilerplate code is DRY
Given ourselves flexible base All boilerplate code is DRY Defaults per ‘section’
trans_default_domain
list.html.twig {% extends "MDMainBundle:Layouts:admin.html.twig" %}
 
 {% trans_default_domain "admin" %}
 
 {% block body %}
 <h1>{{ 'article.title.list'|trans }}</h1>
 {% if articles %}
 <ul class="articles">
 {% for article in articles %}
 <li>{{ article.title }}</li>
 {% endfor %}
 </ul>
 {% endif %}
 
 <a href="{{ path('admin.article.create') }}”> {{ 'article.action.create'|trans }} </a>
 {% endblock body %}

But wait!
admin.html.twig {% trans_default_domain "admin" %}
NOPE
And it won’t be fixed
Node.js http://nodejs.org
Node.js Server-side JavaScript http://nodejs.org
Node.js Server-side JavaScript Adds extra functionality http://nodejs.org
npm
npm Composer for node
npm Composer for node Install globally or into project
OMG!!1!
Bower http://bower.io
Demo: Installing Bower With No Sudo - Eek!
👎
Demo: Installing Bower Successfully - Yay!
👍
Getting stuff into your project
Find dependencies
Demo: Using Bower To Search For Packages - Modernizr
Install dependencies
Demo: Using Bower To Install Packages - Modernizr
We can do better
.bowerrc
Demo: Using .bowerrc To Customise Installation Directory
Better
Better Save dependencies
bower.json
Demo: Using Bower To Save Dependencies To bower.json
Don’t forget .gitignore
Easy to reference assets
Easy to reference assets {% block javascripts %} <script src="{{ asset('/vendor/modernizr/modernizr.js') }}"></script> {% endblock javascripts %}
Easy to reference assets {% block javascripts %} <script src="{{ asset('/vendor/modernizr/modernizr.js') }}"></script> {% endblock javascripts %} Globally or per ‘section’
Different locations?
Non-standard install
Non-standard install Make it clear
Non-standard install Make it clear Potentially more flexibility
Gulp http://gulpjs.com
Build tool
“ Every week someone who doesn’t understand Make writes a new build system in JavaScript. #gruntjs #gearjs #gulpjs #broccolijs. Laugh or cry? https://twitter.com/aslak_hellesoy/status/435506106496851968
Streaming
Plugin architecture
Demo: Installing Gulp Globally With npm
One extra step
Demo: Installing Gulp and Gulp-Util Into A Project With npm
Don’t forget .gitignore
Minify JavaScript
Demo: Using Gulp To Minify JavaScript
Use it in our project
frontend.html.twig {% block javascripts %}
 <script src="{{ asset('/web/js/vendor/modernizr.js') }}"></script>
 {% endblock javascripts %}
ITCHY NOSE!!!
We have 2 copies in web/
We have 2 copies in web/ Bower install to /assets/vendor
We have 2 copies in web/ Bower install to /assets/vendor Prevents source being public
(S)CSS
Normal SCSS workflow
frontend.scss @import "assets/vendor/normalize-scss/normalize";
 
 $body-width: 60% !default;
 
 body
 {
 width: $body-width;
 margin: 0 auto;
 }

gulpfile.js var scss = require('gulp-sass'); ! ! gulp.task('scss', function() {
 return gulp.src('assets/scss/*.scss')
 .pipe(scss())
 .pipe(gulp.dest('web/css'));
 });
frontend.html.twig {% block stylesheets %}
 <link rel="stylesheet" href="{{ asset('/css/frontend.css') }}" />
 {% endblock stylesheets %}
matt at Chloe in ~/Sites/workflow.dev on dev * 🍔 $ gulp scss [gulp] Using file /Users/matt/Sites/workflow.dev/gulpfile.js [gulp] Working directory changed to /Users/matt/Sites/workflow.dev [gulp] Running 'scss'... [gulp] Finished 'scss' in 11 ms
Running commands manually gets old real fast
Watching!
gulpfile.js gulp.task('watch', function() {
 gulp.watch('assets/scss/**/*.scss', ['scss']);
 });
Demo: Gulp Watch In Action
Awesomesauce!
Only scratching the surface!
Gulp plugins
TESTS T IN L CONCAT ONS plugins ATIGulp IFIC OT N IG W T SE I TIM BROW P O SERIF Y RENAME COP Y GI T
Custom actions are easy
Setting up a project can be tedious
Can be taken much further!
GAZILLIONS of tools available
GAZILLIONS of tools available Don’t use all of them!
Plan it!
Plan it! **groan**
Questions?
Thanks! @mdavis1982

Optimising Your Front End Workflow With Symfony, Twig, Bower and Gulp