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.

Jeff Carouth

June 30, 2012
Tweet

More Decks by Jeff Carouth

Other Decks in Programming

Transcript

  1. 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.
  2. To grok where a Micro Framework fits in today’s PHP

    ecosystem, it is imperative to examine PHP’s roots.
  3. 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
  4. 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
  5. PomTrac Eventually will be a pomodoro tracker. But we’ll start

    by just implementing tasks and the activity inventory.
  6. Implemention Guide ‣ GET /tasks ‣ POST /tasks ‣ PUT

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

    /tasks/<id> ‣ GET /tasks/<id> ‣ DELETE /tasks/<id>
  8. 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 });
  9. 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 });
  10. 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 });
  11. 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
  12. [ { "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 } ]
  13. Implemention Guide ✓ GET /tasks ‣ POST /tasks ‣ PUT

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

    /tasks/<id> ‣ GET /tasks/<id> ‣ DELETE /tasks/<id>
  15. 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 });
  16. 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 });
  17. 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
  18. Implemention Guide ✓ GET /tasks ✓ POST /tasks ‣ PUT

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

    /tasks/<id> ‣ GET /tasks/<id> ‣ DELETE /tasks/<id>
  20. 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 });
  21. 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 });
  22. 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 });
  23. 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 });
  24. 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 }
  25. Implemention Guide ✓ GET /tasks ✓ POST /tasks ‣ PUT

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

    /tasks/<id> ✓ GET /tasks/<id> ‣ DELETE /tasks/<id>
  27. 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 });
  28. 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 });
  29. 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 });
  30. 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
  31. Implemention Guide ✓ GET /tasks ✓ POST /tasks ✓ PUT

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

    /tasks/<id> ✓ GET /tasks/<id> ‣ DELETE /tasks/<id>
  33. 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 });
  34. 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 });
  35. 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
  36. 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
  37. Implemention Guide ✓ GET /tasks ✓ POST /tasks ✓ PUT

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

    API is 102 LOC ‣ App consists of 6 files (excluding vendor/)
  39. 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>',
  40. Implemention Guide ‣ Retrieve data from API ‣ Render HTML

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

    elements ‣ Configure the application ‣ Handle events
  42. 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 });
  43. 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 });
  44. 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();
  45. 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
  46. Implemention Guide ✓ Retrieve data from API ‣ Render HTML

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

    elements ‣ Configure the application ‣ Handle events
  48. 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
  49. 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>
  50. 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
  51. 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
  52. 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
  53. 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
  54. 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
  55. Implemention Guide ✓ Retrieve data from API ✓ Render HTML

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

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

    elements ‣ Configure the application ‣ Handle events change, click, hover, dblclick, etc.
  58. 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
  59. 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
  60. Implemention Guide ✓ Retrieve data from API ✓ Render HTML

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

    elements ‣ Configure the application ✓ Handle events
  62. 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 });
  63. 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 });
  64. 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 });
  65. 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 });
  66. 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.
  67. 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 });
  68. 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 });
  69. Implemention Guide ✓ Retrieve data from API ✓ Render HTML

    elements ✓ Configure the application ✓ Handle events
  70. 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.
  71. 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.
  72. 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.