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}
  2. What’re we building? • Domain: Book Shop • Sub-domain: Bookshop

    Website • Bounded-Context: Book Information Service
  3. Book Information Service DB Queues Logic Responsive Extendible Clean Parallel

    Serve JS Frontend-y Book Jacket API Rating API Book Info Website Browser
  4. 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}
  5. Goals • Working knowledge • JavaScript serverside • Client side

    JavaScript frameworks • Know how to integrate them with PHP • Become more of a Cross Stack developer
  6. // 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");
  7. <?php
 // 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]);
 }
 }
  8. Silex for the business logic • Rapid API Development •

    Familiar libraries (Doctrine, etc) • Perfect for small services
  9. Alternatives? • Apigility • Building an API with Apigility -

    Rob Allen • ZF2 • Symfony • Build RESTful APIs easily with Symfony - Sarah Khalil
  10. Getting Started • Create basic HTML file - include JS

    • Bower.io • Similar to composer • More focus on UI components
  11. //- 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)
  12. <!DOCTYPE html>
 <html lang="en" ng-app="booksApp">
 
 <head>
 <title>Book Info Website</title>


    <link rel="stylesheet" href="/stylesheets/style.css">
 <script src="/assets/modules/angular/angular.js"></script>
 <script src="/assets/modules/angular-resource/angular-resource.js"></script>
 <script src="/assets/modules/angular-route/angular-route.js"></script>
 <script src="/javascripts/angular/app.js"></script>
 <script src="/javascripts/angular/services.js"></script>
 <script src="/javascripts/angular/controllers.js"></script>
 </head>
 
 <body>
 <div ng-view></div>
 </body>
 
 </html>
  13. //- 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'
 ]); 

  14. // 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)
  15. // 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;
 }]);

  16. // 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();
 });
 }
 ]
 );
  17. // 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);
 }
 };
  18. // book-info-website/public/javascripts/angular/services.js api.stopPushGet = function () {
 pushing = false;


    
 if (angular.isDefined(promise)) {
 $timeout.cancel(promise);
 promise = undefined;
 }
 };
  19. Alternatives? • React • Bring your PHP application to the

    next level with React.JS - Bastian Hofmann • Backbone • Ember
  20. 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
  21. Who is going to use it most? • Less context

    switching • JavaScript developers can understand it
  22. Getting Started • Template generator npm install -g express-generator express

    /tmp/foo • Exactly like composer/bower • Except it’s NPM
  23. Friends of Express: Grunt • Achieved Phing’s dream • Loads

    of prewritten tasks • Pre-process CSS/JS/Images • Automatically restart Express on change
  24. // 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']);
 
 }
  25. // 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); 

  26. // 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)
  27. // 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);
 });
 };
  28. // 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);
 });
 });

  29. Alternatives? • PHP + Using Guzzle Futures • PHP +

    Queues • Adding 1.21 Gigawatts to Applications with RabbitMQ - James Titcumb • Go/Other language with parallelisation
  30. 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