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. Micro Framework and
    JavaScript Applications
    brought to you via the MicroPHP Manifesto
    Jeff Carouth

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  7. 1 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?

    I thought so. I’ll take your questions now.
    Thank you for
    coming.

    View Slide

  8. Agenda
    The MicroPHP Manifesto

    View Slide

  9. View Slide

  10. Learning
    a comprehensive
    framework
    is hard.

    View Slide

  11. PHP is complicated enough

    View Slide

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

    View Slide

  13. To grok where a Micro Framework fits
    in today’s PHP ecosystem, it is
    imperative to examine PHP’s roots.

    View Slide

  14. hypertext preprocessing language
    which is easily extensible
    modules and extensions
    leading to community contributed
    1998

    View Slide

  15. Exposing MicroPHP
    microphp.org……good start
    packagist.org……better choice

    View Slide

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

    View Slide

  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

    View Slide

  18. View Slide

  19. View Slide

  20. Slim: micro framework

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  24. Tasty API Implementation
    CREDIT: http://www.flickr.com/photos/agustinrafaelreyes/6895890406/

    View Slide

  25. PomTrac
    Eventually will be a pomodoro tracker.
    But we’ll start by just implementing tasks and
    the activity inventory.

    View Slide

  26. Resource: tasks
    http://pomtrac.localhost/tasks

    View Slide

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

    View Slide

  28. Implemention Guide
    ‣ GET /tasks
    ‣ POST /tasks
    ‣ PUT /tasks/
    ‣ GET /tasks/
    ‣ DELETE /tasks/

    View Slide

  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 });

    View Slide

  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 });

    View Slide

  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 });

    View Slide

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

    View Slide

  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

    View Slide

  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
    }
    ]

    View Slide

  35. Implemention Guide
    ✓ GET /tasks
    ‣ POST /tasks
    ‣ PUT /tasks/
    ‣ GET /tasks/
    ‣ DELETE /tasks/

    View Slide

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

    View Slide

  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 });

    View Slide

  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 });

    View Slide

  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.” }

    View Slide

  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

    View Slide

  41. Implemention Guide
    ✓ GET /tasks
    ✓ POST /tasks
    ‣ PUT /tasks/
    ‣ GET /tasks/
    ‣ DELETE /tasks/

    View Slide

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

    View Slide

  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 });

    View Slide

  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 });

    View Slide

  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 });

    View Slide

  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 });

    View Slide

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

    View Slide

  48. View Slide

  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
    }

    View Slide

  50. Implemention Guide
    ✓ GET /tasks
    ✓ POST /tasks
    ‣ PUT /tasks/
    ✓ GET /tasks/
    ‣ DELETE /tasks/

    View Slide

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

    View Slide

  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 });

    View Slide

  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 });

    View Slide

  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 });

    View Slide

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

    View Slide

  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

    View Slide

  57. Implemention Guide
    ✓ GET /tasks
    ✓ POST /tasks
    ✓ PUT /tasks/
    ✓ GET /tasks/
    ‣ DELETE /tasks/

    View Slide

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

    View Slide

  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 });

    View Slide

  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 });

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  65. Implemention Guide
    ✓ GET /tasks
    ✓ POST /tasks
    ✓ PUT /tasks/
    ✓ GET /tasks/
    ✓ DELETE /tasks/

    View Slide

  66. Final Stats
    ‣ Full index.php is 142 LOC
    ‣ Tasks API is 102 LOC
    ‣ App consists of 6 files (excluding vendor/)

    View Slide

  67. LoneStarPHP+JavaScript
    DISCLAIMER: name change not cleared with organizers

    View Slide

  68. structure with models, collections, and views
    simple binding and handling of events
    connection to RESTful JSON API

    View Slide

  69. *scoff* JavaScript
    doesn’t need
    STRUCTURE
    Well, not mine anyway.

    View Slide

  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 = $("", {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 = $('', { '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 = $('',

    View Slide

  71. Lightweight JavaScript “MVC”

    View Slide

  72. Model
    View Controller

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  78. Backbone.Model
    1 App.Task = Backbone.Model.extend({});

    View Slide

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

    View Slide

  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 });

    View Slide

  81. Backbone.Collection
    1 App.Tasks = Backbone.Collection.extend({});

    View Slide

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

    View Slide

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

    View Slide

  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 });

    View Slide

  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();

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  89. 1 App.TaskView = Backbone.View.extend({});
    Backbone.View

    View Slide

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

    View Slide

  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

    View Slide

  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
    <br/><input class="summary textinput" type="text"<br/>value="<%= summary %>"><br/><input class="estimate textinput" type="text"<br/>value="<%= estimate %>"><br/><div class="strike">[del]</div><br/>

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  100. Implemention Guide
    ✓ Retrieve data from API
    ✓ Render HTML elements
    ‣ Configure the application
    ‣ Handle events change, click, hover, dblclick, etc.

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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 });

    View Slide

  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 });

    View Slide

  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 });

    View Slide

  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 });

    View Slide

  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.

    View Slide

  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 });

    View Slide

  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 });

    View Slide

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

    View Slide

  114. Wrap Up

    View Slide

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

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  119. Code Available
    http://github.com/jcarouth/pomtrac
    The lsp12 branch will not change.

    View Slide

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

    View Slide

  121. https://joind.in/6351
    Speakers
    NEED
    Feedback
    @jcarouth | [email protected]

    View Slide