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

Micro Framework and JavaScript Applications

Micro Framework and JavaScript Applications

Small footprint libraries and so-called micro frameworks are a newer development in the PHP community. In this session we'll look at the MicroPHP Manifesto and go over building an application focused on the ideas presented by it. We'll look at a couple micro frameworks and other tools including Slim as well as concerns with implementing your frontend with Backbone.js. We'll cover simple ways to organize your application and manage application dependencies using Composer. The techniques for developing PHP web applications are plentiful, MicroPHP is but one of them.

0f930e13633535c1c4041e95b8881308?s=128

Jeff Carouth

June 30, 2012
Tweet

Transcript

  1. Micro Framework and JavaScript Applications brought to you via the

    MicroPHP Manifesto Jeff Carouth
  2. http://mojolive.com/profile/jcarouth

  3. http://mojolive.com/profile/jcarouth @jcarouth

  4. http://mojolive.com/profile/jcarouth

  5. http://mojolive.com/profile/jcarouth Occasional Blogger

  6. http://mojolive.com/profile/jcarouth

  7. 1 <?php 2 require_once __DIR__ . "/../vendor/composer/autoload.php"; 3 4 $app

    = new Slim(array( 5 'templates.path' => __DIR__ . "/../templates", 6 )); 7 8 $app->get('/', function() use($app) { 9 $app->render('index'); 10 }); 11 12 $app->run(); Micro Enough? </sarcasm> I thought so. I’ll take your questions now. Thank you for coming.
  8. Agenda The MicroPHP Manifesto

  9. None
  10. Learning a comprehensive framework is hard.

  11. PHP is complicated enough

  12. Have you tried Xdebug yet? Hello…Dev Support…

  13. To grok where a Micro Framework fits in today’s PHP

    ecosystem, it is imperative to examine PHP’s roots.
  14. hypertext preprocessing language which is easily extensible modules and extensions

    leading to community contributed 1998
  15. Exposing MicroPHP microphp.org……good start packagist.org……better choice

  16. Exposing MicroPHP IMHO microphp.org……good start packagist.org……better choice

  17. http://getcomposer.org “Composer is a tool for dependency management in PHP.

    It allows you to declare the dependent libraries your project needs and it will install them in your project for you.” – The FM
  18. None
  19. None
  20. Slim: micro framework

  21. simple yet powerful CREDIT: http://www.flickr.com/photos/malabooboo/4930536883/

  22. REST CREDIT: http://www.flickr.com/photos/tmartin/111423043/

  23. 11 //GET route - retrieve data at resource 12 $app->get('/resource/:id',

    function ($id) { 13 echo 'This is a GET route'; 14 }); 15 16 //POST route - create new resource 17 $app->post('/resource', function () { 18 echo 'This is a POST route'; 19 }); 20 21 //PUT route - update resource 22 $app->put('/resource/:id', function ($id) { 23 echo 'This is a PUT route'; 24 }); 25 26 //DELETE route - delete resource 27 $app->delete('/resource/:id', function ($id) { 28 echo 'This is a DELETE route'; 29 }); RESTful Verbs
  24. Tasty API Implementation CREDIT: http://www.flickr.com/photos/agustinrafaelreyes/6895890406/

  25. PomTrac Eventually will be a pomodoro tracker. But we’ll start

    by just implementing tasks and the activity inventory.
  26. Resource: tasks http://pomtrac.localhost/tasks

  27. Implemention Guide ‣ GET /tasks ‣ POST /tasks ‣ PUT

    /tasks/<id> ‣ GET /tasks/<id> ‣ DELETE /tasks/<id>
  28. Implemention Guide ‣ GET /tasks ‣ POST /tasks ‣ PUT

    /tasks/<id> ‣ GET /tasks/<id> ‣ DELETE /tasks/<id>
  29. 39 $app->get('/tasks', function() use ($app) { 40 $collection = $app->dataStore->tasks;

    41 $tasks = iterator_to_array($collection->find()); 42 $app->render( 43 'tasks', 44 array( 45 'tasks' => $tasks, 46 ) 47 ); 48 });
  30. 39 $app->get('/tasks', function() use ($app) { 40 $collection = $app->dataStore->tasks;

    41 $tasks = iterator_to_array($collection->find()); 42 $app->render( 43 'tasks', 44 array( 45 'tasks' => $tasks, 46 ) 47 ); 48 });
  31. 39 $app->get('/tasks', function() use ($app) { 40 $collection = $app->dataStore->tasks;

    41 $tasks = iterator_to_array($collection->find()); 42 $app->render( 43 'tasks', 44 array( 45 'tasks' => $tasks, 46 ) 47 ); 48 });
  32. GET http://pomtrac.localhost/tasks Accept-Language: en-us Accept: application/json Content-Type: application/x-www-form-urlencoded Accept-Encoding: gzip,

    deflate
  33. GET http://pomtrac.localhost/tasks Accept-Language: en-us Accept: application/json Content-Type: application/x-www-form-urlencoded Accept-Encoding: gzip,

    deflate HTTP 200 No Error Server: Apache/2.2.21 (Unix) PHP/5.3.8 X-Powered-By: PHP/5.3.8 Content-Type: application/json Date: Tue, 26 Jun 2012 01:41:56 GMT Keep-Alive: timeout=5, max=100 Content-Length: 747 Connection: Keep-Alive
  34. [ { "summary" : "Document all the codes.", "createdDate" :

    "2012-06-24T15:38:54-05:00", "id" : "4fe77ade8ead0ebfd2000000", "estimate" : null, "completed" : false }, { "summary" : "Do something.", "createdDate" : "2012-06-24T17:12:42-05:00", "id" : "4fe790da8ead0ebcd2000000", "estimate" : null, "completed" : false }, { "summary" : "Conference call @ 10AM w/ Josh", "createdDate" : "2012-06-24T17:55:59-05:00", "id" : "4fe79aff8ead0e400a00000a", "estimate" : null, "completed" : false } ]
  35. Implemention Guide ✓ GET /tasks ‣ POST /tasks ‣ PUT

    /tasks/<id> ‣ GET /tasks/<id> ‣ DELETE /tasks/<id>
  36. Implemention Guide ✓ GET /tasks ‣ POST /tasks ‣ PUT

    /tasks/<id> ‣ GET /tasks/<id> ‣ DELETE /tasks/<id>
  37. 68 $app->post('/tasks', function() use ($app) { 69 $request = $app->request();

    70 $response = $app->response(); 71 72 $data = $request->getBody(); 80 81 if(true === $app->dataStore->tasks->insert($data)) { 82 $response->status(201); 83 84 $resourceLocation = Utils\buildUrl( 85 $request, 86 "tasks/".(string)$data["_id"] 87 ); 88 89 $response['Location'] = $resourceLocation; 90 } else { 91 $response->status(500); 92 } 93 });
  38. 68 $app->post('/tasks', function() use ($app) { 69 $request = $app->request();

    70 $response = $app->response(); 71 72 $data = $request->getBody(); 80 81 if(true === $app->dataStore->tasks->insert($data)) { 82 $response->status(201); 83 84 $resourceLocation = Utils\buildUrl( 85 $request, 86 "tasks/".(string)$data["_id"] 87 ); 88 89 $response['Location'] = $resourceLocation; 90 } else { 91 $response->status(500); 92 } 93 });
  39. POST http://pomtrac.localhost/tasks Accept-Language: en-us Accept: application/json Content-Type: application/json Accept-Encoding: gzip,

    deflate { “summary”: “Write tests or GrumpyProgrammer will cut me.” }
  40. POST http://pomtrac.localhost/tasks Accept-Language: en-us Accept: application/json Content-Type: application/json Accept-Encoding: gzip,

    deflate { “summary”: “Write tests or GrumpyProgrammer will cut me.” } HTTP 201 Created Location: http://pomtrac.localhost/tasks/4fe91a7defca290156000000 X-Powered-By: PHP/5.3.8 Server: Apache/2.2.21 (Unix) PHP/5.3.8 Content-Type: application/json Date: Tue, 26 Jun 2012 02:12:13 GMT Keep-Alive: timeout=5, max=100 Content-Length: 0 Connection: Keep-Alive
  41. Implemention Guide ✓ GET /tasks ✓ POST /tasks ‣ PUT

    /tasks/<id> ‣ GET /tasks/<id> ‣ DELETE /tasks/<id>
  42. Implemention Guide ✓ GET /tasks ✓ POST /tasks ‣ PUT

    /tasks/<id> ‣ GET /tasks/<id> ‣ DELETE /tasks/<id>
  43. 50 $app->get('/tasks/:id', function($id) use ($app) { 51 $collection = $app->dataStore->tasks;

    52 $task = $collection->findOne( 53 array('_id' => new MongoId($id)) 54 ); 55 56 if (null === $task) { 57 $app->response()->status(404); 58 } else { 59 $app->render( 60 'task', 61 array( 62 'task' => $task, 63 ) 64 ); 65 } 66 });
  44. 50 $app->get('/tasks/:id', function($id) use ($app) { 51 $collection = $app->dataStore->tasks;

    52 $task = $collection->findOne( 53 array('_id' => new MongoId($id)) 54 ); 55 56 if (null === $task) { 57 $app->response()->status(404); 58 } else { 59 $app->render( 60 'task', 61 array( 62 'task' => $task, 63 ) 64 ); 65 } 66 });
  45. 50 $app->get('/tasks/:id', function($id) use ($app) { 51 $collection = $app->dataStore->tasks;

    52 $task = $collection->findOne( 53 array('_id' => new MongoId($id)) 54 ); 55 56 if (null === $task) { 57 $app->response()->status(404); 58 } else { 59 $app->render( 60 'task', 61 array( 62 'task' => $task, 63 ) 64 ); 65 } 66 });
  46. 50 $app->get('/tasks/:id', function($id) use ($app) { 51 $collection = $app->dataStore->tasks;

    52 $task = $collection->findOne( 53 array('_id' => new MongoId($id)) 54 ); 55 56 if (null === $task) { 57 $app->response()->status(404); 58 } else { 59 $app->render( 60 'task', 61 array( 62 'task' => $task, 63 ) 64 ); 65 } 66 });
  47. GET http://pomtrac.localhost/tasks/4fe91a7defca290156000000 Accept-Language: en-us Accept: application/json Content-Type: application/json Accept-Encoding: gzip,

    deflate
  48. None
  49. HTTP 200 No Error Server: Apache/2.2.21 (Unix) PHP/5.3.8 X-Powered-By: PHP/5.3.8

    Content-Type: application/json Date: Tue, 26 Jun 2012 02:25:17 GMT Keep-Alive: timeout=5, max=100 Content-Length: 181 Connection: Keep-Alive { "id" : "4fe91a7defca290156000000", "summary" : "Write tests or GrumpyProgrammer will cut you.", "createdDate" : "2012-06-25T21:10:09-05:00", "estimate" : null, "completed" : false }
  50. Implemention Guide ✓ GET /tasks ✓ POST /tasks ‣ PUT

    /tasks/<id> ✓ GET /tasks/<id> ‣ DELETE /tasks/<id>
  51. Implemention Guide ✓ GET /tasks ✓ POST /tasks ‣ PUT

    /tasks/<id> ✓ GET /tasks/<id> ‣ DELETE /tasks/<id>
  52. 95 $app->put('/tasks/:id', function($id) use ($app) { 96 $data = $app->request()->getBody();

    106 $updateResult = $app->dataStore->tasks->update( 107 array("_id" => new MongoId($id)), 108 array( 109 '$set' => array( 110 'summary' => $data['summary'], 111 'estimate' => $data['estimate'], 112 'completed' => $data['completed'], 113 ) 114 ) 115 ); 116 117 if (true === $updateResult) { 118 $app->response()->status(200); 119 } else { 120 $app->response()->status(500); 121 $this->render( 122 'failure', 123 array('failure' => 'Could not update the task.') 124 ); 125 } 126 });
  53. 95 $app->put('/tasks/:id', function($id) use ($app) { 96 $data = $app->request()->getBody();

    106 $updateResult = $app->dataStore->tasks->update( 107 array("_id" => new MongoId($id)), 108 array( 109 '$set' => array( 110 'summary' => $data['summary'], 111 'estimate' => $data['estimate'], 112 'completed' => $data['completed'], 113 ) 114 ) 115 ); 116 117 if (true === $updateResult) { 118 $app->response()->status(200); 119 } else { 120 $app->response()->status(500); 121 $this->render( 122 'failure', 123 array('failure' => 'Could not update the task.') 124 ); 125 } 126 });
  54. 95 $app->put('/tasks/:id', function($id) use ($app) { 96 $data = $app->request()->getBody();

    106 $updateResult = $app->dataStore->tasks->update( 107 array("_id" => new MongoId($id)), 108 array( 109 '$set' => array( 110 'summary' => $data['summary'], 111 'estimate' => $data['estimate'], 112 'completed' => $data['completed'], 113 ) 114 ) 115 ); 116 117 if (true === $updateResult) { 118 $app->response()->status(200); 119 } else { 120 $app->response()->status(500); 121 $this->render( 122 'failure', 123 array('failure' => 'Could not update the task.') 124 ); 125 } 126 });
  55. PUT http://pomtrac.localhost/tasks/4fe91a7defca290156000000 Accept-Language: en-us Accept: application/json Content-Type: application/json Accept-Encoding: gzip,

    deflate { “estimate”: “300” }
  56. PUT http://pomtrac.localhost/tasks/4fe91a7defca290156000000 Accept-Language: en-us Accept: application/json Content-Type: application/json Accept-Encoding: gzip,

    deflate { “estimate”: “300” } HTTP 200 No Error Server: Apache/2.2.21 (Unix) PHP/5.3.8 X-Powered-By: PHP/5.3.8 Content-Type: application/json Date: Tue, 26 Jun 2012 02:34:30 GMT Keep-Alive: timeout=5, max=100 Content-Length: 0 Connection: Keep-Alive
  57. Implemention Guide ✓ GET /tasks ✓ POST /tasks ✓ PUT

    /tasks/<id> ✓ GET /tasks/<id> ‣ DELETE /tasks/<id>
  58. Implemention Guide ✓ GET /tasks ✓ POST /tasks ✓ PUT

    /tasks/<id> ✓ GET /tasks/<id> ‣ DELETE /tasks/<id>
  59. 128 $app->delete('/tasks/:id', function($id) use ($app) { 129 $deleteResult = $app->dataStore->tasks->remove(

    130 array("_id" => new MongoId($id)) 131 ); 132 133 if (true === $deleteResult) { 134 $app->response()->status(204); 135 } else { 136 $app->response()->status(500); 137 } 138 });
  60. 128 $app->delete('/tasks/:id', function($id) use ($app) { 129 $deleteResult = $app->dataStore->tasks->remove(

    130 array("_id" => new MongoId($id)) 131 ); 132 133 if (true === $deleteResult) { 134 $app->response()->status(204); 135 } else { 136 $app->response()->status(500); 137 } 138 });
  61. DELETE http://pomtrac.localhost/tasks/4fe91a7defca290156000000 Accept-Language: en-us Accept: application/json Content-Type: application/json Accept-Encoding: gzip,

    deflate
  62. HTTP 204 No Content Server: Apache/2.2.21 (Unix) PHP/5.3.8 X-Powered-By: PHP/5.3.8

    Content-Type: text/html Date: Tue, 26 Jun 2012 02:41:35 GMT Keep-Alive: timeout=5, max=100 Content-Length: 0 Connection: Keep-Alive DELETE http://pomtrac.localhost/tasks/4fe91a7defca290156000000 Accept-Language: en-us Accept: application/json Content-Type: application/json Accept-Encoding: gzip, deflate
  63. GET http://pomtrac.localhost/tasks/4fe91a7defca290156000000 Accept-Language: en-us Accept: application/json Content-Type: application/json Accept-Encoding: gzip,

    deflate
  64. GET http://pomtrac.localhost/tasks/4fe91a7defca290156000000 Accept-Language: en-us Accept: application/json Content-Type: application/json Accept-Encoding: gzip,

    deflate HTTP 404 Not Found Server: Apache/2.2.21 (Unix) PHP/5.3.8 X-Powered-By: PHP/5.3.8 Content-Type: application/json Date: Tue, 26 Jun 2012 02:42:59 GMT Keep-Alive: timeout=5, max=100 Content-Length: 0 Connection: Keep-Alive
  65. Implemention Guide ✓ GET /tasks ✓ POST /tasks ✓ PUT

    /tasks/<id> ✓ GET /tasks/<id> ✓ DELETE /tasks/<id>
  66. Final Stats ‣ Full index.php is 142 LOC ‣ Tasks

    API is 102 LOC ‣ App consists of 6 files (excluding vendor/)
  67. LoneStarPHP+JavaScript DISCLAIMER: name change not cleared with organizers

  68. structure with models, collections, and views simple binding and handling

    of events connection to RESTful JSON API
  69. *scoff* JavaScript doesn’t need STRUCTURE Well, not mine anyway.

  70. 529 var i = 0; 530 var departmentListLength = VisitReg.RegisterUI.AcademicAppointments.departmentList.length;

    531 var deptList = VisitReg.RegisterUI.AcademicAppointments.departmentList; 532 533 for (i = 0; i < departmentListLength; i++) { 534 option = $("<option></option>", {value: deptList[i].departmentId}); 535 option.html(deptList[i].departmentName); 536 537 appointmentSelector.append(option); 538 } 539 540 appointmentSelector.bind("change", function() { 541 var visitDate = VisitReg.RegisterUI.AcademicAppointments.date; 542 var appointmentDataWrapper = $(this).next(); 543 appointmentDataWrapper.empty(); 544 545 var selectedDeptId = $(this).val(); 546 547 if (selectedDeptId === '') { 548 return; 549 } 550 551 //find department in department list data 552 var departmentList = VisitReg.RegisterUI.AcademicAppointments.departmentList; 553 var selectedDepartmentData; 554 for (var i = 0; i < departmentList.length; i++) { 555 if (parseInt(departmentList[i].departmentId, 10) === parseInt(selectedDeptId, 10)) { 556 selectedDepartmentData = departmentList[i]; 557 break; 558 } 559 } 560 561 //TODO: handle error of missing departmentData 562 563 if (selectedDepartmentData.participating === "1") { 564 $.getJSON( 565 VisitReg.settings.baseUrl + "/api/availableacademicappts/date/" + visitDate.replace(/\//g, "-") + "/department/" + selectedDeptId, 566 function(data) { 567 var i = 0; 568 var availApptsLength = data.length; 569 570 if (availApptsLength === 0) { 571 //display department has no available appointments 572 var message = $('<p></p>', { 'class': "message" }); 573 message.html( 'We\'re sorry, but this department does not have any scheduled appointments on the date you wish to visit.'); 574 575 appointmentDataWrapper.append(message); 576 577 return; 578 } 579 580 var appointmentButtons = $('<ul></ul>',
  71. Lightweight JavaScript “MVC”

  72. Model View Controller

  73. View Controller Model Backbone.Model Backbone.Collection Templates Underscore.js Backbone.View Backbone.Router

  74. View Controller Model Backbone.Model Backbone.Collection Templates Underscore.js Backbone.View Backbone.Router Backbone.Events

  75. View Controller Model Backbone.Model Backbone.Collection Templates Underscore.js Backbone.View Backbone.Router Backbone.Events

    Backbone.Sync
  76. Implemention Guide ‣ Retrieve data from API ‣ Render HTML

    elements ‣ Configure the application ‣ Handle events
  77. Implemention Guide ‣ Retrieve data from API ‣ Render HTML

    elements ‣ Configure the application ‣ Handle events
  78. Backbone.Model 1 App.Task = Backbone.Model.extend({});

  79. Backbone.Model 1 App.Task = Backbone.Model.extend({ 2 urlRoot: "tasks" 3 });

  80. Backbone.Model 1 App.Task = Backbone.Model.extend({ 2 urlRoot: "tasks", 3 defaults:

    function() { 4 return { 5 summary: "", 6 createdDate: moment(), 7 completed: false, 8 estimate: null 9 }; 10 } 11 });
  81. Backbone.Collection 1 App.Tasks = Backbone.Collection.extend({});

  82. Backbone.Collection 1 App.Tasks = Backbone.Collection.extend({ 2 model: App.Task 3 });

  83. Backbone.Collection 1 App.Tasks = Backbone.Collection.extend({ 2 model: App.Task, 3 4

    url: "/tasks" 5 });
  84. Backbone.Collection 1 App.Tasks = Backbone.Collection.extend({ 2 model: App.Task, 3 4

    url: "/tasks", 5 6 comparator: function(task) { 7 return moment(task.get("createdDate")); 8 } 9 });
  85. Backbone.Collection 1 App.Tasks = Backbone.Collection.extend({ 2 model: App.Task, 3 4

    url: "/tasks", 5 6 comparator: function(task) { 7 return moment(task.get("createdDate")); 8 } 9 }); 1 var tasks = new App.Tasks(); 2 tasks.fetch();
  86. Backbone.Collection 1 App.Tasks = Backbone.Collection.extend({ 2 model: App.Task, 3 4

    url: "/tasks", 5 6 comparator: function(task) { 7 return moment(task.get("createdDate")); 8 } 9 }); 1 var tasks = new App.Tasks(); 2 tasks.fetch(); GET http://pomtrac.localhost/tasks
  87. Implemention Guide ✓ Retrieve data from API ‣ Render HTML

    elements ‣ Configure the application ‣ Handle events
  88. Implemention Guide ✓ Retrieve data from API ‣ Render HTML

    elements ‣ Configure the application ‣ Handle events
  89. 1 App.TaskView = Backbone.View.extend({}); Backbone.View

  90. 1 App.TaskView = Backbone.View.extend({ 2 tagname: 'div', 3 4 className:

    'inventory-row' 5 }); Backbone.View
  91. 1 App.TaskView = Backbone.View.extend({ 2 tagname: 'div', 3 4 className:

    'inventory-row', 5 6 template: _.template($("#pomtrac-task-tmpl").html()) 7 }); Backbone.View
  92. 1 App.TaskView = Backbone.View.extend({ 2 tagname: 'div', 3 4 className:

    'inventory-row', 5 6 template: _.template($("#pomtrac-task-tmpl").html()) 7 }); Backbone.View <script type="text/javascript" id="pomtrac-task-tmpl"> <input class="summary textinput" type="text" value="<%= summary %>"> <input class="estimate textinput" type="text" value="<%= estimate %>"> <div class="strike">[del]</div> </script>
  93. 1 App.TaskView = Backbone.View.extend({ 2 tagname: 'div', 3 4 className:

    'inventory-row', 5 6 template: _.template($("#pomtrac-task-tmpl").html()), 7 8 initialize: function() { 9 this.render(); 10 } 11 }); Backbone.View
  94. 1 App.TaskView = Backbone.View.extend({ 2 tagname: 'div', 3 4 className:

    'inventory-row', 5 6 template: _.template($("#pomtrac-task-tmpl").html()), 7 8 initialize: function() { 9 this.render(); 10 }, 11 12 render: function() { 13 this.$el.html(this.template(this.model.toJSON())); 14 this.summaryInput = this.$('.summary'); 15 this.estimateInput = this.$('.estimate'); 16 return this; 17 } 18 }); Backbone.View
  95. 1 App.ActivityInventoryView = Backbone.View.extend({ 2 initialize: function() { 3 this.collection

    = new App.Tasks(); 4 this.collection.url = "/tasks"; 5 this.collection.fetch({ 6 success: _.bind(function(resp, status, xhr) { 7 this.render(); 8 }, this) 9 }); 10 } 11 }); Backbone.View
  96. 1 App.ActivityInventoryView = Backbone.View.extend({ 2 initialize: function() { 3 this.collection

    = new App.Tasks(); 4 this.collection.url = "/tasks"; 5 this.collection.fetch({ 6 success: _.bind(function(resp, status, xhr) { 7 this.render(); 8 }, this) 9 }); 10 }, 11 12 render: function() { 13 this.collection.each(this.renderTask, this); 14 } 15 }); Backbone.View
  97. 1 App.ActivityInventoryView = Backbone.View.extend({ 2 initialize: function() { 3 this.collection

    = new App.Tasks(); 4 this.collection.url = "/tasks"; 5 this.collection.fetch({ 6 success: _.bind(function(resp, status, xhr) { 7 this.render(); 8 }, this) 9 }); 10 }, 11 12 render: function() { 13 this.collection.each(this.renderTask, this); 14 }, 15 16 renderTask: function(model) { 17 var taskView = new App.TaskView({model: model}); 18 this.$el.append(taskView.el); 19 } 20 }); Backbone.View
  98. Implemention Guide ✓ Retrieve data from API ✓ Render HTML

    elements ‣ Configure the application ‣ Handle events
  99. Implemention Guide ✓ Retrieve data from API ✓ Render HTML

    elements ‣ Configure the application ‣ Handle events
  100. Implemention Guide ✓ Retrieve data from API ✓ Render HTML

    elements ‣ Configure the application ‣ Handle events change, click, hover, dblclick, etc.
  101. 1 App.TaskView = Backbone.View.extend({ 19 events: { 20 'change .textinput':

    'update', 21 } 22 }); Backbone.View
  102. 1 App.TaskView = Backbone.View.extend({ 19 events: { 20 'change .textinput':

    'update', 21 }, 22 23 update: function() { 24 this.model.save({ 25 'summary': this.summaryInput.val(), 26 'estimate': this.estimateInput.val() 27 }); 28 } 29 }); Backbone.View
  103. 1 App.TaskView = Backbone.View.extend({ 19 events: { 20 'change .textinput':

    'update', 21 'click .strike': 'strike' 22 }, 23 24 update: function() { 25 this.model.save({ 26 'summary': this.summaryInput.val(), 27 'estimate': this.estimateInput.val() 28 }); 29 }, 30 31 strike: function() { 32 this.remove(); 33 this.model.destroy(); 34 } 35 }); Backbone.View
  104. Implemention Guide ✓ Retrieve data from API ✓ Render HTML

    elements ‣ Configure the application ✓ Handle events
  105. Implemention Guide ✓ Retrieve data from API ✓ Render HTML

    elements ‣ Configure the application ✓ Handle events
  106. 1 window.App = {}; 2 3 App.init = function() {

    4 var ActivityInventory = null; 5 6 $.ajaxSetup({ 7 'dataType': 'json' 8 }); 9 10 Backbone.emulateHTTP = true; 11 12 ActivityInventory = new App.ActivityInventoryView({ 13 el: $('#activityinventory') 14 }); 15 }; 1 $(function() { 2 App.init(); 3 });
  107. 1 window.App = {}; 2 3 App.init = function() {

    4 var ActivityInventory = null; 5 6 $.ajaxSetup({ 7 'dataType': 'json' 8 }); 9 10 Backbone.emulateHTTP = true; 11 12 ActivityInventory = new App.ActivityInventoryView({ 13 el: $('#activityinventory') 14 }); 15 }; 1 window.App = {}; 1 $(function() { 2 App.init(); 3 });
  108. 1 window.App = {}; 2 3 App.init = function() {

    4 var ActivityInventory = null; 5 6 $.ajaxSetup({ 7 'dataType': 'json' 8 }); 9 10 Backbone.emulateHTTP = true; 11 12 ActivityInventory = new App.ActivityInventoryView({ 13 el: $('#activityinventory') 14 }); 15 }; 3 App.init = function() { 15 }; 1 $(function() { 2 App.init(); 3 });
  109. 1 window.App = {}; 2 3 App.init = function() {

    4 var ActivityInventory = null; 5 6 $.ajaxSetup({ 7 'dataType': 'json' 8 }); 9 10 Backbone.emulateHTTP = true; 11 12 ActivityInventory = new App.ActivityInventoryView({ 13 el: $('#activityinventory') 14 }); 15 }; 6 $.ajaxSetup({ 7 'dataType': 'json' 8 }); Accept: application/json 1 $(function() { 2 App.init(); 3 });
  110. 1 $(function() { 2 App.init(); 3 }); 1 window.App =

    {}; 2 3 App.init = function() { 4 var ActivityInventory = null; 5 6 $.ajaxSetup({ 7 'dataType': 'json' 8 }); 9 10 Backbone.emulateHTTP = true; 11 12 ActivityInventory = new App.ActivityInventoryView({ 13 el: $('#activityinventory') 14 }); 15 }; 10 Backbone.emulateHTTP = true; Passes a header X-HTTP-Method-Override which works with Slim to allow PUT and DELETE requests as a POST request.
  111. 1 window.App = {}; 2 3 App.init = function() {

    4 var ActivityInventory = null; 5 6 $.ajaxSetup({ 7 'dataType': 'json' 8 }); 9 10 Backbone.emulateHTTP = true; 11 12 ActivityInventory = new App.ActivityInventoryView({ 13 el: $('#activityinventory') 14 }); 15 }; 12 ActivityInventory = new App.ActivityInventoryView({ 13 el: $('#activityinventory') 14 }); 1 $(function() { 2 App.init(); 3 });
  112. 1 window.App = {}; 2 3 App.init = function() {

    4 var ActivityInventory = null; 5 6 $.ajaxSetup({ 7 'dataType': 'json' 8 }); 9 10 Backbone.emulateHTTP = true; 11 12 ActivityInventory = new App.ActivityInventoryView({ 13 el: $('#activityinventory') 14 }); 15 }; 1 $(function() { 2 App.init(); 3 });
  113. Implemention Guide ✓ Retrieve data from API ✓ Render HTML

    elements ✓ Configure the application ✓ Handle events
  114. Wrap Up

  115. Wrap Up MicroPHP is about exposing small-footprint libraries that solve

    common problems.
  116. Wrap Up MicroPHP is about exposing small-footprint libraries that solve

    common problems. Composer and Packagist help the PHP community by exposing micro frameworks and other libraries in an easy-to-consume manner.
  117. Wrap Up MicroPHP is about exposing small-footprint libraries that solve

    common problems. Composer and Packagist help the PHP community by exposing micro frameworks and other libraries in an easy-to-consume manner. Slim makes creating a RESTful API simple.
  118. Wrap Up MicroPHP is about exposing small-footprint libraries that solve

    common problems. Composer and Packagist help the PHP community by exposing micro frameworks and other libraries in an easy-to-consume manner. Slim makes creating a RESTful API simple. Backbone.js provides just the structure a PHP dev like me needs to build on top of such APIs.
  119. Code Available http://github.com/jcarouth/pomtrac The lsp12 branch will not change.

  120. Thank You! Questions? do you have or Comments?

  121. https://joind.in/6351 Speakers NEED Feedback @jcarouth | jcarouth@gmail.com