Using Laravel, Gulp, Bootstrap and LESS

Another similar tutorial that replaces GruntJS to GulpJS into my workflow.
GulpJS

Go to the updated workflow Laravel 5 Deployment.

Install Laravel with Composer

Head to: https://getcomposer.org/download/
or use this

curl -sS https://getcomposer.org/installer | php  

You can shorten the command composer instead of php composer.phar with alias
Type alias composer='php composer.phar'

composer create-project laravel/laravel --prefer-dist  

or

composer create-project laravel/laravel laravel 4.2 --prefer-dist  

This guide is written for Laravel 4

Goto laravel directory and run composer update afterwards to make sure Laravel dependencies are updated

Goto laravel directory and run php artisan key:generate afterwards to make sure you generate a key in ./laravel/app/config/app.php

Install NodeJS and others once

You may skip this if you have done this.

It's really great and small for the tasks you're going to love.
Head to: http://nodejs.org/
Make sure you install with sudo command

Also install GulpJS and Bower.IO

npm install -g gulp  
npm install -g bower  

With the flag -g you installed it globally and now you can access it from anywhere on your system.

Gulp - The streaming build system
Bower - A package manager for the web

What is Gulp

Official definition: Gulp: the streaming build system

Gulp is basically a tool that automates your frontend tasks, but it goes beyond that if you want it. It can upload your changed files to CDN networks, copy files to production environment, optimize images and more. There are inumerous plugins for all these functions and if you can't find one that suits your needs, you can always write your own.

Initialize a Gulp project

To initialize a new Gulp project from your project's directory run

npm init  

and follow the instructions. The command will create your package.json file, necessary for any npm project.

name: gulpRocks
version: 0.0.0
description: Using Gulp with Laravel and Bootstrap
entry point: gulpfile.js
test command: Enter
git repository: https://github.com/HariantoAtWork/gulp-laravel.git
keywords: Laravel, Gulp, Bootstrap, LESS, mdstn.com
author: Harianto van Insulinde
license: BSD
Is this ok? (yes): Enter

{  
  "name": "gulpRocks",
  "version": "0.0.0",
  "description": "Using Grunt with Laravel and Bootstrap",
  "main": "Gulpfile.js",
  "repository": {
    "type": "git",
    "url": "https://github.com/HariantoAtWork/gulp-laravel.git"
  },
  "keywords": [
    "Laravel",
    "Gulp",
    "Bootstrap",
    "mdstn.com"
  ],
  "author": "Harianto van Insulnde",
  "license": "BSD",
  "bugs": {
    "url": "https://github.com/HariantoAtWork/gulp-laravel/issues"
  }
} 

Finally, let's install gulp and some plugins as dependencies. We'll use:

  • "gulp-less": For LESS compilation
  • "gulp-sass": For SASS compilation
  • "gulp-minify-css": For minifying CSS
  • "gulp-concat": For concatenation
  • "gulp-uglify": For javascript minification
  • "gulp-rename": For renaming files
  • "gulp-phpunit": For PHP Unit testing

In your terminal run:

npm install gulp --save-dev  
npm install gulp-less --save-dev  
npm install gulp-sass --save-dev  
npm install gulp-minify-css --save-dev  
npm install gulp-concat --save-dev  
npm install gulp-uglify --save-dev  
npm install gulp-rename --save-dev  
npm install gulp-phpunit --save-dev  

Or use this single command:
npm install gulp gulp-less gulp-sass gulp-minify-css gulp-concat gulp-uglify gulp-rename gulp-phpunit --save-dev

If you already install this dependency before, add this extra flag:
--cache-min 999999.

That will install the dependencies and because we defined the --save-dev flag it will add them to the package.json file.

Bower

Bower - A package manager for the web

Set path Bower dependencies installation

After we’ve done installing Laravel with Composer while ago. We’re going to set up the path for Bower dependencies before we continue or it will install in the default directory bower_components. First we must create a file .bowerrc.
with

{
  "directory": "laravel/public/assets/bower_vendor"
}

Frontend dependencies

For this tutorial we'll use Bootstrap and jQuery so let's install them with Bower:

You could use only bower install bootstrap and that would work, but if you want to save a list of dependencies go ahead and create a bower.json file containing only a project name:

{  
  "name": "gulpRocks"
}

Now run:
bower install bootstrap -S

The -S flag will save the dependency in the bower.json file and later you can just run bower install to replicate the exact front-end dependencies of your project. If you're not one of those guys paranoid about github just dying one day and our poor selves left without our packages, you can freely add bower_components to your .gitignore file and just track bower.json.

I know you noticed already; I'm forgetting about jQuery. But actually we already got it. In its bower.json file Bootstrap defines that it depends on jQuery, so it got automatically pulled in together with Bootstrap.

Read more information about bower on http://bower.io

Here's how our components folder turned out (only the parts that concern us):

/ laravel/public/assets/bower_vendor
  / bootstrap
    / dist
      / js
        - bootstrap.js
        - bootstrap.min.js
      / less
        - alerts.less
        - badges.less
        - bootstrap.less
        ...
        ...
        - variables.less
        - wells.less
  / jquery
    - jquery.js
    - jquery.min.js

Configuring gulpfile.js

Buckle up, here begins the fun part! Let's create the configuration file gulpfile.js in root. This is its basic structure:

// gulpfile.js

// --- INIT
var gulp = require('gulp'),  
    less = require('gulp-less'), // compiles less to CSS
    sass = require('gulp-sass'), // compiles sass to CSS
    minify = require('gulp-minify-css'), // minifies CSS
    concat = require('gulp-concat'),
    uglify = require('gulp-uglify'), // minifies JS
    rename = require('gulp-rename'),
    phpunit = require('gulp-phpunit');

// Paths variables
var paths = {  
    'dev': {
        'less': './laravel/public/dev/less/',
        'scss': './laravel/public/dev/scss/',
        'js': './laravel/public/dev/js/',
        'vendor': './laravel/public/dev/vendor/'
    },
    'assets': {
        'css': './laravel/public/assets/css/',
        'js': './laravel/public/assets/js/',
        'vendor': './laravel/public/assets/bower_vendor/'
    }

};

// --- TASKS

// --- WATCH

// --- DEFAULT

The stylesheets

Let's create the following filestructure:

/ laravel/public
  / dev
    / less
      - backend.less
      - bootstrap.less
      - fonts.less
      - frontend.less
      - variables.less
      / fonts
    / js
      - backend.js
      - frontend.js
    / vendor
  / assets
    / img
      / icons
    / fonts
    / css
    / js
    / bower_vendor

You can copy bootstrap.less from Bootstrap and rename it to /laravel/public/dev/less/bootstrap.less

You can copy variables.less from Bootstrap and put it to /laravel/public/dev/less/variables.less

The file /laravel/public/dev/less/frontend.less:

@import 'fonts.less';
@import 'bootstrap.less';   
//styles specific for the frontend 
// ... 

The file /laravel/public/dev/less/backend.less:

@import 'fonts.less';
@import 'bootstrap.less';   
//styles specific for the backend 
// ... 

The stylesheets that are common to both the backend and front end are in bootstrap.less. That's also the file where we include all the components from Bootstrap we need. It will go something like this:

// Core variables and mixins
@import "variables.less";
@import "../../assets/bower_vendor/bootstrap/less/mixins.less";

// Reset
@import "../../assets/bower_vendor/bootstrap/less/normalize.less";
@import "../../assets/bower_vendor/bootstrap/less/print.less";

// Core CSS
@import "../../assets/bower_vendor/bootstrap/less/scaffolding.less";
@import "../../assets/bower_vendor/bootstrap/less/type.less";
@import "../../assets/bower_vendor/bootstrap/less/code.less";
@import "../../assets/bower_vendor/bootstrap/less/grid.less";
@import "../../assets/bower_vendor/bootstrap/less/tables.less";
@import "../../assets/bower_vendor/bootstrap/less/forms.less";
@import "../../assets/bower_vendor/bootstrap/less/buttons.less";
/*  
...
...
*/

If you already copy the (Bootstrap) bootstrap.less to (Dev) bootstrap.less, make sure you correct the path like the script above

And let's create some tasks:

// --- TASKS
// CSS frontend
gulp.task('frontend.css', function() {  
  // place code for your default task here
  return gulp.src(paths.dev.less+'frontend.less') // get file
    .pipe(less())
    .pipe(gulp.dest(paths.assets.css)) // output: frontend.css
    .pipe(minify({keepSpecialComments:0}))
    .pipe(rename({suffix: '.min'}))
    .pipe(gulp.dest(paths.assets.css)); // output: frontend.min.css
});

// CSS backend
gulp.task('backend.css', function() {  
  // place code for your default task here
  return gulp.src(paths.dev.less+'backend.less') // get file
    .pipe(less())
    .pipe(gulp.dest(paths.assets.css)) // output: backend.css
    .pipe(minify({keepSpecialComments:0}))
    .pipe(rename({suffix: '.min'}))
    .pipe(gulp.dest(paths.assets.css)); // output: backend.min.css
});

Remember we already set path for paths.dev.less and paths.assets.css.

To run only this single task run gulp frontend.css in the command line.

The javascript

This one is pretty clear, first we're concatenating jquery.js, bootstrap.js and frontend.js in a single frontend.js file that will live in the public directory and than we repeat the same thing for the backend javascript file backend.js.

// --- TASKS
// JS frontend
gulp.task('frontend.js', function(){  
  return gulp.src([
      paths.assets.vendor+'jquery/dist/jquery.js',
      paths.assets.vendor+'bootstrap/dist/js/bootstrap.js',
      paths.dev.js+'frontend.js'
    ])
    .pipe(concat('frontend.min.js'))
    .pipe(uglify())
    .pipe(gulp.dest(paths.assets.js));
});

// JS backend
gulp.task('backend.js', function(){  
  return gulp.src([
      paths.assets.vendor+'jquery/dist/jquery.js',
      paths.assets.vendor+'bootstrap/dist/js/bootstrap.js',
      paths.dev.js+'backend.js'
    ])
    .pipe(concat('backend.min.js'))
    .pipe(uglify())
    .pipe(gulp.dest(paths.assets.js));
});

To run only this single task run gulp frontend.js or gulp backend.js in the command line.

The tests

Skip this. gulp-phpunit is not working as expected

Gulp can automatically run your tests in Laravel, but we need to make sure PhpUnit exist. laravel/vendor/bin/phpunit.
Let’s update Laravel if you haven’t already. Head to laravel directory and perform this magic.

composer update  

Open laravel/composer.json and check if "phpunit/phpunit": "3.7.*" are present inside "require": {}. If not, add manually and run
composer update

Let’s get back to gulpfile.js and check this task definition:

// PHP unit
gulp.task('phpunit', function() {  
  var options = {debug: false, notify: false};
  return gulp.src('./laravel/app/tests/*.php')
    .pipe(phpunit('./laravel/vendor/bin/phpunit', options))
    // .pipe(phpunit())

    //both notify and notify.onError will take optional notifier: growlNotifier for windows notifications
    //if options.notify is true be sure to handle the error here or suffer the consequenses!
    .on('error', notify.onError({
      title: 'PHPUnit Failed',
      message: 'One or more tests failed, see the cli for details.'
    }))

        //will fire only if no error is caught
    .pipe(notify({
      title: 'PHPUnit Passed',
      message: 'All tests passed!'
    }));
});

To run only this single task run gulp phpunit in the command line.

SKIP THIS!!! For some reason PHP Unit doesn't work.

Watching the filesystem for changes

Gulp's superpower is running tasks without having to do many things. It watches your filesystem for changes and it only needs your instructions to know what to do and when to do it.

We already defined 5 tasks:
frontend.css, backend.css, frontend.js, backend.js and phpunit. You could run every one of them separately, if you want, but to fully harness the power of Gulp and automate pure project let's put the bits and pieces together and define the big bad watch task that will watch certain files and run determinate tasks.

// --- WATCH
//Rerun the task when a file changes
gulp.task('watch', function() {  
  gulp.watch(paths.dev.less, ['frontend.css', 'backend.css']);
  gulp.watch(paths.dev.js, ['frontend.js', 'backend.js']);
});

So, you're probably already guessing: every time one of the defined frontend javascript files is saved, Gulp will run the tasks frontend.css and frontend.js. Same thing with the tasks backend.css and backend.js and their appropriate files.

-- edit this -- We can use livereload. "gulp-livereload" will automatically refresh your browser when one of the tasks is done.

install the Live reload extension and activate it for the tab your app is running in.

Beer me!

Thank you!

comments powered by Disqus