Upgrade to Pro — share decks privately, control downloads, hide ads and more …

MVVM and Silex - It's the future

MVVM and Silex - It's the future

There's two things that every cool new start-up wants to provide. A rich application like website, and an API. I'll be talking about how to use Silex and an AngularJS to create one. With lots of demos to go wrong, this talk is aimed at PHP developers that work closely with the frontend, some some knowledge of JavaScript is assumed. We'll touch on the cool things happening in the frontend space like Bower, Grunt, MVVM Frameworks, and how that might change how you write PHP.

Billie Thompson

February 20, 2015
Tweet

More Decks by Billie Thompson

Other Decks in Technology

Transcript

  1. Code/Demo Server
    book-jacket-api
    book-info-website
    Book Jacket
    API
    Rating API
    Book Info
    Website
    Browser
    ratings-api
    {server}.herokuapp.com
    https://github.com/PurpleBooth/{server}

    View Slide

  2. MVVM and
    Silex
    Welcome to the world of
    tomorrow!

    View Slide

  3. Who’re you?
    Billie Thompson
    @BillieAThompson
    [email protected]
    PHP Contractor

    View Slide

  4. What’re we building?
    • Domain: Book Shop
    • Sub-domain: Bookshop Website
    • Bounded-Context: Book Information Service

    View Slide

  5. Book Information Service
    Book Jacket
    API
    Rating API
    Book Info
    Website
    Browser

    View Slide

  6. Book Information Service
    DB
    Queues
    Logic
    Responsive
    Extendible
    Clean
    Parallel
    Serve JS
    Frontend-y
    Book Jacket
    API
    Rating API
    Book Info
    Website
    Browser

    View Slide

  7. Book Information Service
    Silex
    PHP
    AngularJS
    JavaScript
    Express
    JavaScript
    Book Jacket
    API
    Rating API
    Book Info
    Website
    Browser

    View Slide

  8. Code/Demo Server
    book-jacket-api
    book-info-website
    Book Jacket
    API
    Rating API
    Book Info
    Website
    Browser
    ratings-api
    {server}.herokuapp.com
    https://github.com/PurpleBooth/{server}

    View Slide

  9. Cross Functional Teams
    Team
    Alpha
    Team
    One
    Team
    Primary
    Product?
    Ops
    Backend
    Test
    Frontend

    View Slide

  10. Why Microservices?
    • Small focused teams
    • Small services
    • Combining to become awesome

    View Slide

  11. Why JavaScript?
    • Mature Platform
    • Mature Frameworks
    • Mature Support Tools

    View Slide

  12. Goals
    • Working knowledge
    • JavaScript serverside
    • Client side JavaScript frameworks
    • Know how to integrate them with PHP
    • Become more of a Cross Stack developer

    View Slide

  13. Silex
    Solid & simple

    View Slide

  14. Recap
    • Micro Framework
    • Symfony Components
    • Lots of you have (probably?) used it

    View Slide

  15. Getting started with Silex
    • Composer
    • PHP on whatever
    • Single PHP File

    View Slide

  16. Book Information Service
    Book Jacket
    API
    Rating API
    Book Info
    Website
    Browser

    View Slide

  17. –Silex Demo
    “Please don’t break”

    View Slide

  18. // book-jacket-api/public/index.php
    require_once __DIR__.’/../vendor/autoload.php';
    $app = new Silex\Application();

    View Slide

  19. // book-jacket-api/public/index.php
    $app->get('/hello/{name}', function($name) use($app) {

    return 'Hello '.$app->escape($name);

    });


    View Slide

  20. // book-jacket-api/public/index.php
    use PurpleBooth\Controller\BooksController;
    use Silex\Provider\ServiceControllerServiceProvider;

    $app->register(new ServiceControllerServiceProvider());



    $app['books.controller'] = $app->share(function() use ($app) {

    return new BooksController();

    });


    $app->get('/', "books.controller:indexAction");

    $app->get('/{isbn}', "books.controller:getAction");

    View Slide

  21. // book-jacket-api/src/Controller/BooksController.php


    namespace PurpleBooth\Controller;


    use Symfony\Component\HttpFoundation\JsonResponse;


    class BooksController

    {


    …

    public function getAction($isbn)

    {

    if (!array_key_exists($isbn, $this->bookData)) {

    return new JsonResponse(null, 404);

    }


    return new JsonResponse($this->bookData[$isbn]);

    }

    }

    View Slide

  22. Silex for the business logic
    • Rapid API Development
    • Familiar libraries (Doctrine, etc)
    • Perfect for small services

    View Slide

  23. Alternatives?
    • Apigility
    • Building an API with Apigility - Rob Allen
    • ZF2
    • Symfony
    • Build RESTful APIs easily with Symfony - Sarah Khalil

    View Slide

  24. MVVM
    Model, View and View Model

    View Slide

  25. MVVM Framework
    View
    View
    Model
    Model
    Remote
    API
    Browser Server

    View Slide

  26. View Slide

  27. Getting Started
    • Create basic HTML file -
    include JS
    • Bower.io
    • Similar to composer
    • More focus on UI components

    View Slide

  28. –AngularJS Demo
    “It looked so easy when I saw someone else’s
    talk”

    View Slide

  29. //- book-info-website/views/index.jade
    doctype html

    html(lang="en" ng-app="booksApp")

    head

    title Book Info Website

    link(rel='stylesheet', href='/stylesheets/style.css')

    script(src='/assets/modules/angular/angular.js')

    script(src='/assets/modules/angular-resource/angular-resource.js')

    script(src='/assets/modules/angular-route/angular-route.js')

    script(src='/javascripts/angular/app.js')

    script(src='/javascripts/angular/services.js')

    script(src='/javascripts/angular/controllers.js')

    body

    div(ng-view)

    View Slide





  30. Book Info Website















    View Slide

  31. //- book-info-website/views/index.jade

    script(src='/assets/modules/angular/angular.js')

    script(src='/assets/modules/angular-resource/angular-resource.js')

    script(src='/assets/modules/angular-route/angular-route.js')

    View Slide

  32. //- book-info-website/views/index.jade
    script(src='/javascripts/angular/app.js')

    script(src='/javascripts/angular/services.js')

    script(src='/javascripts/angular/controllers.js')

    View Slide

  33. //- book-info-website/views/index.jade
    html(lang="en" ng-app=“booksApp”)

    // book-info-website/public/javascripts/angular/app.js

    var booksApp = angular.module('booksApp', [

    'ngResource', 

    'ngRoute',

    'booksServices',

    'booksControllers'

    ]);

    View Slide

  34. // book-info-website/public/javascripts/angular/app.js
    booksApp.config(['$routeProvider',

    function($routeProvider) {

    $routeProvider.

    when('/', {

    templateUrl: 'partials/list.html',

    controller: 'IndexController'

    }).

    when('/:isbn', {

    templateUrl: 'partials/details.html',

    controller: 'DetailController'

    }).

    otherwise({

    redirectTo: '/'

    });

    }]);

    //- book-info-website/views/index.jade
    body

    div(ng-view)

    View Slide

  35. //- book-info-website/views/partials/list.jade

    div(ng-controller="IndexController")

    h1= title

    h2 Book list!

    ul

    li(ng-repeat="book in books")

    a(href="#/{{book.isbn}}") {{book.title}}

    View Slide

  36. // book-info-website/public/javascripts/angular/controllers.js
    var booksControllers = angular.module('booksControllers', []);


    booksControllers.controller(
    'IndexController',
    ['$scope', 'Book',
    function ($scope, Book) {

    $scope.books = Book.query();

    }]
    );

    View Slide

  37. // book-info-website/public/javascripts/angular/services.js
    var booksService = angular.module('booksServices', []);


    booksService.factory('Book', ['$resource', '$timeout',

    function ($resource, $timeout) {

    var api = $resource('api/v2/:isbn', {'isbn': '@id'});

    …

    return api;

    }]);


    View Slide

  38. MVVM Framework
    View
    View
    Model
    Model
    Remote
    API
    Browser Server

    View Slide

  39. // book-info-website/public/javascripts/angular/controllers.js
    booksControllers.controller(

    'DetailController',

    ['$scope', '$routeParams', 'Book',

    function ($scope, $routeParams, Book) {

    Book.pushGet({isbn: $routeParams.isbn}, function (book) {

    $scope.book = book;

    });


    $scope.$on('$destroy', function () {

    Book.stopPushGet();

    });

    }

    ]

    );

    View Slide

  40. // book-info-website/public/javascripts/angular/services.js
    var promise;

    var pushing = false;


    api.pushGet = function (params, callback) {

    if (!pushing) {

    pushing = true;


    var refreshData = function () {

    api.get(params, function (book) {

    callback(book);


    promise = $timeout(

    function () {

    refreshData(params, callback);

    }, 4000);

    });

    };


    refreshData(params, callback);

    }

    };

    View Slide

  41. // book-info-website/public/javascripts/angular/services.js
    api.stopPushGet = function () {

    pushing = false;


    if (angular.isDefined(promise)) {

    $timeout.cancel(promise);

    promise = undefined;

    }

    };

    View Slide

  42. Alternatives?
    • React
    • Bring your PHP application to the next level with React.JS -
    Bastian Hofmann
    • Backbone
    • Ember

    View Slide

  43. Future of frontend
    development
    • Simpler than it looks
    • Has a lot of similarities with server side frameworks
    • More and more popular - you will encounter it
    • Data via APIs from client side

    View Slide

  44. Express
    Contender for the worlds
    most boring logo

    View Slide

  45. Book Information Service
    Book Jacket
    API
    Rating API
    Book Info
    Website
    Browser

    View Slide

  46. Blocking
    Book Jacket
    API
    Rating API
    Book Info
    Website
    Book Info
    Website

    View Slide

  47. Non-blocking
    Book Jacket
    API
    Rating API
    Book Info
    Website
    Book Info
    Website

    View Slide

  48. Who is going to use it most?
    • Less context switching
    • JavaScript developers can understand it

    View Slide

  49. Getting Started
    • Template generator
    npm install -g express-generator
    express /tmp/foo
    • Exactly like composer/bower
    • Except it’s NPM

    View Slide

  50. Friends of Express: Grunt
    • Achieved Phing’s dream
    • Loads of prewritten tasks
    • Pre-process CSS/JS/Images
    • Automatically restart Express
    on change

    View Slide

  51. // book-info-website/Gruntfile.js
    module.exports = function (grunt) {

    grunt.initConfig({

    pkg: grunt.file.readJSON('package.json'),

    watch: {

    express: {

    files: ['**/*.js'],

    tasks: ['express:dev'],

    options: {

    spawn: false

    }

    }

    },

    express: {

    dev: {

    options: {

    script: 'bin/www'

    }

    }

    }

    });


    grunt.loadNpmTasks('grunt-contrib-watch');

    grunt.loadNpmTasks('grunt-express-server');


    grunt.registerTask('server', ['express:dev', 'watch'])

    grunt.registerTask('default', ['server']);


    }

    View Slide

  52. // book-info-website/Gruntfile.js

    grunt.loadNpmTasks('grunt-contrib-watch');

    grunt.loadNpmTasks('grunt-express-server');


    View Slide

  53. // book-info-website/Gruntfile.js

    grunt.registerTask('server', ['express:dev', 'watch'])

    grunt.registerTask('default', ['server']);


    View Slide

  54. –Grunt & Express Demo
    “How likely is that it will break twice?”

    View Slide

  55. // book-info-website/app.js


    var routes = require('./routes/index');

    var bookApi1 = require('./routes/book-api-v1');

    var bookApi2 = require('./routes/book-api-v2');

    app.use("/assets/modules", express.static(__dirname + '/bower_components'));


    app.use('/', routes);

    app.use('/api/v1', bookApi1);

    app.use('/api/v2', bookApi2);

    View Slide

  56. // book-info-website/routes/index.js
    /* GET home page. */

    router.get('/', function(req, res, next) {

    res.render('index');

    });

    //- book-info-website/views/index.jade

    doctype html

    html(lang="en" ng-app="booksApp")

    head

    title Book Info Website

    link(rel='stylesheet', href='/stylesheets/style.css')

    script(src='/assets/modules/angular/angular.js')

    script(src='/assets/modules/angular-resource/angular-resource.js')

    script(src='/assets/modules/angular-route/angular-route.js')

    script(src='/javascripts/angular/app.js')

    script(src='/javascripts/angular/services.js')

    script(src='/javascripts/angular/controllers.js')

    body

    div(ng-view)

    View Slide

  57. // book-info-website/routes/book-api-v1.js
    router.get('/', function (req, res, next) {

    booksApiClient.findAll(function (err, books) {

    res.json(books);

    });

    });

    View Slide

  58. // book-info-website/api-client/books.js
    var endpoint = "http://book-jacket-api.herokuapp.com/";


    this.getBook = function (isbn, callback) {

    makeHttpRequest(endpoint.concat(isbn), callback)

    };


    View Slide

  59. // book-info-website/api-client/client.js
    var http = require('http');


    module.exports = function (url, callback) {

    http.get(url, function (res) {

    var str = '';


    res.on('data', function (chunk) {

    str += chunk;

    });


    res.on('end', function () {

    var apiResponse = JSON.parse(str);


    callback(null, apiResponse);

    });

    }).on('error', function (e) {

    callback(e);

    });

    };

    View Slide

  60. Book Information Service
    Book Jacket
    API
    Rating API
    Book Info
    Website
    Browser

    View Slide

  61. –Async Demo
    “What’s the worst that can happen?”

    View Slide

  62. // book-info-website/routes/book-api-v2.js
    router.get('/:isbn', function (req, res, next) {

    async.parallel({

    'book': function (callback) {

    booksApiClient.getBook(req.params.isbn, function (err, bookDetail) {

    callback(err, bookDetail);

    });

    },

    'rating': function (callback) {

    ratingsApiClient.getRating(req.params.isbn, function (err, ratings) {

    callback(err, ratings);

    });

    }

    }, function (err, results) {

    _.extend(results.book, {'rating': results.rating});

    res.json(results.book);

    });

    });


    View Slide

  63. Alternatives?
    • PHP + Using Guzzle Futures
    • PHP + Queues
    • Adding 1.21 Gigawatts to Applications with RabbitMQ -
    James Titcumb
    • Go/Other language with parallelisation

    View Slide

  64. Express
    • Non-blocking
    • Front-end Developer Friendly
    • Lightweight

    View Slide

  65. In conclusion
    • Cross functional teams require knowledge of other
    disciplines
    • MVVM frameworks are going to get more popular
    • MVVM frameworks require APIs
    • Use middleware to hide none public services

    View Slide

  66. Any Questions?
    @BillieAThompson
    [email protected]
    https://joind.in/talk/view/13383

    View Slide