Symfony2, Backbone.js & socket.io - SfLive Paris 2k13 - Wisembly
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share

Symfony2, Backbone.js & socket.io - SfLive Paris 2k13 - Wisembly

  • 9,767 views
Uploaded on

Wisembly experience sharing on building one-page js app w/ Backbone.js over Symfony2 REST API and socket.io push server.

Wisembly experience sharing on building one-page js app w/ Backbone.js over Symfony2 REST API and socket.io push server.

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
9,767
On Slideshare
9,723
From Embeds
44
Number of Embeds
3

Actions

Shares
Downloads
116
Comments
0
Likes
41

Embeds 44

http://eventifier.co 25
https://twitter.com 17
http://symfony2developer.com 2

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Symfony2, Backbone.js & socket.io Symfony live Paris 2013 @guillaumepotier guillaume@wisembly.com
  • 2. app.wisembly.com/sfliveapp.wisembly.com/sflive
  • 3. 2011, Septemberapp.wisembly.com/sflive
  • 4. MySQL PHPapp.wisembly.com/sflive
  • 5. Twig Symfony 2 Doctrine 2 MySQL PHPapp.wisembly.com/sflive
  • 6. Twig js jQuery Assetic Twig Symfony 2 Doctrine 2 MySQL PHPapp.wisembly.com/sflive
  • 7. Backbone.js Underscore.js Twig js jQuery Assetic Twig Symfony 2 Doctrine 2 MySQL PHPapp.wisembly.com/sflive
  • 8. Backbone.js Underscore.js Twig js jQuery H ! UC Assetic O M Twig TO Symfony 2 Doctrine 2 MySQL PHPapp.wisembly.com/sflive
  • 9. Client Serverapp.wisembly.com/sflive
  • 10. Client Twig REST Symfony 2 Server Doctrine 2 MySQL PHPapp.wisembly.com/sflive
  • 11. Backbone.js Underscore.js Client jQuery HTML Twig REST Symfony 2 Server Doctrine 2 MySQL PHPapp.wisembly.com/sflive
  • 12. Backbone.js Underscore.js Client jQuery ! HTML ING L L PO NG L O Twig REST Symfony 2 Server Doctrine 2 MySQL PHPapp.wisembly.com/sflive
  • 13. Users want fast & smooth SaaS appsapp.wisembly.com/sflive
  • 14. Users want fast & smooth SaaS apps Users want multiplateform SaaS appsapp.wisembly.com/sflive
  • 15. Users want fast & smooth SaaS apps Users want multiplateform SaaS apps Users want dynamic & interactive SaaS appsapp.wisembly.com/sflive
  • 16. Users want fast & smooth SaaS apps Users want multiplateform SaaS apps Users want dynamic & interactive SaaS apps You should do so!app.wisembly.com/sflive
  • 17. Users want fast & smooth SaaS apps Users want multiplateform SaaS apps Users want dynamic & interactive SaaS apps W ? You shouldO so! do T H B Uapp.wisembly.com/sflive
  • 18. app.wisembly.com/sflive
  • 19. ?app.wisembly.com/sflive
  • 20. E L L H ! N O ?app.wisembly.com/sflive
  • 21. app.wisembly.com/sflive
  • 22. Nowadaysapp.wisembly.com/sflive
  • 23. app.wisembly.com/sflive
  • 24. small / lightweight / stableapp.wisembly.com/sflive
  • 25. small / lightweight / stable easy to learn, easy to extendapp.wisembly.com/sflive
  • 26. small / lightweight / stable easy to learn, easy to extend great resources: layoutManager relationalapp.wisembly.com/sflive
  • 27. Models Modelsapp.wisembly.com/sflive
  • 28. Models Models Collections Repositoriesapp.wisembly.com/sflive
  • 29. Models Models Collections Repositories Views Controllersapp.wisembly.com/sflive
  • 30. Models Models Collections Repositories Views Controllers Templates Viewsapp.wisembly.com/sflive
  • 31. Models Models Collections Repositories Views Controllers Templates Views Routing Routingapp.wisembly.com/sflive
  • 32. Models Models Collections Repositories Views Controllers S H ViewsP U Templates + RE ST Routing Routingapp.wisembly.com/sflive
  • 33. FOSJsRouting BazingaExposeTranslation JMSSerializer FOSRestBundleapp.wisembly.com/sflive
  • 34. FOSJsRouting BazingaExposeTranslation JMSSerializer FOSRestBundleapp.wisembly.com/sflive
  • 35. 1 - MAKE AN APIapp.wisembly.com/sflive
  • 36. MUST READ http://fr.slideshare.net/nachomartin/symfony- javascript-combining-the-best-of-two-worldsapp.wisembly.com/sflive
  • 37. ar tin nacm @ Books = new Backbone.collection(); Books.url = ‘/books’;app.wisembly.com/sflive
  • 38. ar tin nacm @ Books = new Backbone.collection(); Books.url = ‘/books’; Books.fetch(); GET /booksapp.wisembly.com/sflive
  • 39. ar tin nacm @ events: { ‘click .mybutton’:‘doStuffAndSave’ } doStuffAndSave: function() { var book = Books.get(3); book.stuff(); book.save(); }app.wisembly.com/sflive
  • 40. ar tin nacm @ events: { ‘click .mybutton’:‘doStuffAndSave’ } doStuffAndSave: function() { var book = Books.get(3); book.stuff(); book.save(); } PUT /books/3app.wisembly.com/sflive
  • 41. /** * @var integer $id * * @ORMColumn(name="id", type="integer") * @ORMId [ { * @ORMGeneratedValue(strategy="AUTO") “id”:1, */ “name”:”guillaume”, private $id; “phone”: “0611010011” /** ... * @var string $name * }, * @ORMColumn(name="name", type="string", {...}, length=50, nullable=true) ] */ private $name; /** * @var string $phone * * @ORMColumn(name="phone", type="string", length=20, nullable=true) */ private $phone; ...app.wisembly.com/sflive
  • 42. II - MAKE A GOOD REST APIapp.wisembly.com/sflive
  • 43. MUST READ 2 http://williamdurand.fr/2012/08/02/rest-apis- with-symfony2-the-right-wayapp.wisembly.com/sflive
  • 44. JMSSerializer orapp.wisembly.com/sflive
  • 45. JMSSerializer or class User implements UserInterface, EquatableInterface, ApiAbleInterface { }app.wisembly.com/sflive
  • 46. JMSSerializer or class User implements UserInterface, EquatableInterface, ApiAbleInterface { public function toArray() { return [ id => $this->getId(), name => $this->getName(), email => $this->getEmail(), ]; } }app.wisembly.com/sflive
  • 47. FOSRestBundle orapp.wisembly.com/sflive
  • 48. FOSRestBundle or /** * @Route("{keyword}/quote/{id}", name="api3_quote_get", options={"expose"=true}) * @Method({"GET", "OPTIONS"}) */ public function getQuote(EventInterface $event, $id) { try { $quote = $this->get(api3.quote)->get($id); return $this->container->get(api3.response)->newSuccessResponse($quote- >toArray(), 200); } catch (NoResultException $e) { return $this->container->get(api3.response)->newErrorResponse(No quote found, ErrorCode::NO_QUOTE, 404); } }app.wisembly.com/sflive
  • 49. FOSRestBundle or /** * @Route("{keyword}/quote/{id}", name="api3_quote_get", options={"expose"=true}) * @Method({"GET", "OPTIONS"}) */ public function getQuote(EventInterface $event, $id) { try { $quote = $this->get(api3.quote)->get($id); return $this->container->get(api3.response)->newSuccessResponse($quote- >toArray(), 200); } catch (NoResultException $e) { return $this->container->get(api3.response)->newErrorResponse(No quote found, ErrorCode::NO_QUOTE, 404); } }app.wisembly.com/sflive
  • 50. FOSRestBundle or /** * @Route("{keyword}/quote/{id}", name="api3_quote_get", options={"expose"=true}) * @Method({"GET", "OPTIONS"}) */ public function getQuote(EventInterface $event, $id) { try { $quote = $this->get(api3.quote)->get($id); return $this->container->get(api3.response)->newSuccessResponse($quote- >toArray(), 200); } catch (NoResultException $e) { return $this->container->get(api3.response)->newErrorResponse(No quote found, ErrorCode::NO_QUOTE, 404); } }app.wisembly.com/sflive
  • 51. FOSRestBundle or /** * @Route("{keyword}/quote/{id}", name="api3_quote_get", options={"expose"=true}) * @Method({"GET", "OPTIONS"}) */ public function getQuote(EventInterface $event, $id) { try { $quote = $this->get(api3.quote)->get($id); return $this->container->get(api3.response)->newSuccessResponse($quote- >toArray(), 200); } catch (NoResultException $e) { return $this->container->get(api3.response)->newErrorResponse(No quote found, ErrorCode::NO_QUOTE, 404); } }app.wisembly.com/sflive
  • 52. FOSRestBundle or /** * @Route("{keyword}/quote/{id}", name="api3_quote_get", options={"expose"=true}) * @Method({"GET", "OPTIONS"}) */ public function getQuote(EventInterface $event, $id) { try { $quote = $this->get(api3.quote)->get($id); return $this->container->get(api3.response)->newSuccessResponse($quote- >toArray(), 200); } catch (NoResultException $e) { return $this->container->get(api3.response)->newErrorResponse(No quote found, ErrorCode::NO_QUOTE, 404); } }app.wisembly.com/sflive
  • 53. FOSRestBundle or /** * @Route("{keyword}/quote/{id}", name="api3_quote_get", options={"expose"=true}) * @Method({"GET", "OPTIONS"}) */ public function getQuote(EventInterface $event, $id) { try { $quote = $this->get(api3.quote)->get($id); return $this->container->get(api3.response)->newSuccessResponse($quote- >toArray(), 200); } catch (NoResultException $e) { return $this->container->get(api3.response)->newErrorResponse(No quote found, ErrorCode::NO_QUOTE, 404); } }app.wisembly.com/sflive
  • 54. FOSRestBundle or /** * @Route("{keyword}/quote/{id}", name="api3_quote_get", options={"expose"=true}) * @Method({"GET", "OPTIONS"}) */ public function getQuote(EventInterface $event, $id) { try { $quote = $this->get(api3.quote)->get($id); return $this->container->get(api3.response)->newSuccessResponse($quote- >toArray(), 200); } catch (NoResultException $e) { return $this->container->get(api3.response)->newErrorResponse(No quote found, ErrorCode::NO_QUOTE, 404); } }app.wisembly.com/sflive
  • 55. Controller Entity Service API Service Entity EntityRepositoryapp.wisembly.com/sflive
  • 56. Use Validator and Form ouac @c <?php // ... private function processForm(User $user) { $form = $this->createForm(new UserType(), $user); $form->bind($this->getRequest()); if ($form->isValid()) { $this->doYourStuff(); return $user; } // ... }app.wisembly.com/sflive
  • 57. /** * @Route("event/{keyword}/poll/{id}", name="api3_poll_edit", requirements={"id" = "d+"}, options={"expose"=true}) * @Method({"POST", "PUT", "OPTIONS"}) */ public function editPollAction(EventInterface $event, Request $request, $id) { try { $poll = $this->get(api3.poll)->get($event, $id); $poll = $this->get(api3.poll)->edit($poll, $request); return $this->container->get(api3.response)->newSuccessResponse($poll- >toArray(), 201); } catch (NoResultException $e) { // ... } catch (AccessDeniedException $e) { // ... } catch (Exception $e) { // ... } }app.wisembly.com/sflive
  • 58. /** * @Route("event/{keyword}/poll/{id}", name="api3_poll_edit", requirements={"id" = "d+"}, options={"expose"=true}) * @Method({"POST", "PUT", "OPTIONS"}) */ public function editPollAction(EventInterface $event, Request $request, $id) { try { $poll = $this->get(api3.poll)->get($event, $id); $poll = $this->get(api3.poll)->edit($poll, $request); return $this->container->get(api3.response)->newSuccessResponse($poll- >toArray(), 201); } catch (NoResultException $e) { // ... } catch (AccessDeniedException $e) { // ... } catch (Exception $e) { // ... } }app.wisembly.com/sflive
  • 59. /** * @Route("event/{keyword}/poll/{id}", name="api3_poll_edit", requirements={"id" = "d+"}, options={"expose"=true}) * @Method({"POST", "PUT", "OPTIONS"}) */ public function editPollAction(EventInterface $event, Request $request, $id) { try { $poll = $this->get(api3.poll)->get($event, $id); $poll = $this->get(api3.poll)->edit($poll, $request); return $this->container->get(api3.response)->newSuccessResponse($poll- >toArray(), 201); } catch (NoResultException $e) { // ... } catch (AccessDeniedException $e) { // ... } catch (Exception $e) { // ... } }app.wisembly.com/sflive
  • 60. /** * @Route("event/{keyword}/poll/{id}", name="api3_poll_edit", requirements={"id" = "d+"}, options={"expose"=true}) * @Method({"POST", "PUT", "OPTIONS"}) */ public function editPollAction(EventInterface $event, Request $request, $id) { try { $poll = $this->get(api3.poll)->get($event, $id); $poll = $this->get(api3.poll)->edit($poll, $request); return $this->container->get(api3.response)->newSuccessResponse($poll- >toArray(), 201); } catch (NoResultException $e) { // ... } catch (AccessDeniedException $e) { // ... } catch (Exception $e) { // ... } }app.wisembly.com/sflive
  • 61. /** * @Route("event/{keyword}/poll/{id}", name="api3_poll_edit", requirements={"id" = "d+"}, options={"expose"=true}) * @Method({"POST", "PUT", "OPTIONS"}) */ public function editPollAction(EventInterface $event, Request $request, $id) { try { $poll = $this->get(api3.poll)->get($event, $id); $poll = $this->get(api3.poll)->edit($poll, $request); return $this->container->get(api3.response)->newSuccessResponse($poll- >toArray(), 201); } catch (NoResultException $e) { // ... } catch (AccessDeniedException $e) { // ... } catch (Exception $e) { // ... } }app.wisembly.com/sflive
  • 62. app.wisembly.com/sflive
  • 63. app.wisembly.com/sflive
  • 64. FOSJsRouting BazingaExposeTranslation JMSSerializer FOSRestBundleapp.wisembly.com/sflive
  • 65. ar tin nacm @ events: { ‘bookUpdated’:‘update’, ‘bookCreated’: ‘create’, ‘bookDeleted’:‘delete’, } update: function(websocketData) { doStuff(websocketData); }, create: function(websocketData) { doOtherStuff(websocketData); }, delete: function(websocketData) { stillDoOtherStuff(websocketData); }app.wisembly.com/sflive
  • 66. ar tin nacm @ events: { ‘bookUpdated’:‘update’, ‘bookCreated’: ‘create’, ‘bookDeleted’:‘delete’, } update: function(websocketData) { doStuff(websocketData); }, create: function(websocketData) { doOtherStuff(websocketData); }, delete: function(websocketData) { stillDoOtherStuff(websocketData); } REAL TIME FULL EVENT BASEDapp.wisembly.com/sflive
  • 67. websocketData? Who sends what? Which port, which protocol?app.wisembly.com/sflive
  • 68. app.wisembly.com/sflive
  • 69. FOSJsRouting BazingaExposeTranslation JMSSerializer FOSRestBundleapp.wisembly.com/sflive
  • 70. Authenticate user against PUSH serverapp.wisembly.com/sflive
  • 71. Authenticate user against PUSH server sessionToken domainapp.wisembly.com/sflive
  • 72. Authenticate user against PUSH server sessionToken domain REST sessionToken domainapp.wisembly.com/sflive
  • 73. Authenticate user against PUSH server sessionToken domain rights REST sessionToken domainapp.wisembly.com/sflive
  • 74. Authenticate user against PUSH server Authenticated! rights REST sessionToken domainapp.wisembly.com/sflive
  • 75. PUSH: The «Classic» way rightsapp.wisembly.com/sflive
  • 76. PUSH: The «Classic» way REST sessionToken domain rightsapp.wisembly.com/sflive
  • 77. PUSH: The «Classic» way REST sessionToken data domain rightsapp.wisembly.com/sflive
  • 78. PUSH: The «Classic» way REST sessionToken data domain rightsapp.wisembly.com/sflive
  • 79. PUSH: The «Classic» way websocketData REST sessionToken data domain rightsapp.wisembly.com/sflive
  • 80. • Slow: HTTP ajax round-trip • !DRY: Double front processing (Ajax / Push) • Push server complexity: authorizationsapp.wisembly.com/sflive
  • 81. PUSH: The «Wisembly» way websocketData REST sessionToken data domain rightsapp.wisembly.com/sflive
  • 82. PUSH: The «Wisembly» way websocketData REST sessionToken domain websocketData secret rightsapp.wisembly.com/sflive
  • 83. PUSH: The «Wisembly» way websocketData REST sessionToken domain websocketData websocketData secret rightsapp.wisembly.com/sflive
  • 84. PUSH: The «Wisembly» way websocketData REST sessionToken data websocketData domain websocketData secret rightsapp.wisembly.com/sflive
  • 85. Push «surprises»app.wisembly.com/sflive
  • 86. Push «surprises» • Must find always opened portapp.wisembly.com/sflive
  • 87. Push «surprises» • Must find always opened port • Websocket protocol must go through firewallsapp.wisembly.com/sflive
  • 88. Push «surprises» • Must find always opened port • Websocket protocol must go through firewalls • Push may disconnect (very!) frequently and loose events (duh!)app.wisembly.com/sflive
  • 89. app.wisembly.com/sflive
  • 90. • 80 always opened, but websocket very often blocked -> FAIL -> goto 443 w/ httpsapp.wisembly.com/sflive
  • 91. • 80 always opened, but websocket very often blocked -> FAIL -> goto 443 w/ https • Implement disconnection mechanism and lost events in case of socket.io «degraded» protocol (xhr polling, jsonp polling)app.wisembly.com/sflive
  • 92. The «Wisembly» way hashN: { eventName, args }app.wisembly.com/sflive
  • 93. The «Wisembly» way hashN: { eventName, args }app.wisembly.com/sflive
  • 94. The «Wisembly» way hashN hashN hashN hash1: { eventName, args } hashN: { eventName, args } hash2: { eventName, args } ... hashN: { eventName, args } hashN: { eventName, args } hashN: { eventName, args }app.wisembly.com/sflive
  • 95. The «Wisembly» way hashM hashM hashN hashN: { eventName, args } ...hashN+M: { eventName, args }app.wisembly.com/sflive
  • 96. The «Wisembly» way hashM hashM hashN REST onReconect() since hashN hashN: { eventName, args } ...hashN+M: { eventName, args }app.wisembly.com/sflive
  • 97. The «Wisembly» way hashM hashM hashN REST onReconect() since hashN hashN: { eventName, args } ...hashN+M: { eventName, args } hashN+1: { eventName, args } ... hashN+M: { eventName, args }app.wisembly.com/sflive
  • 98. The «Wisembly» way hashM hashM hashM REST onReconect() since hashN hashN: { eventName, args } ...hashN+M: { eventName, args } hashN+1: { eventName, args } ... hashN+M: { eventName, args }app.wisembly.com/sflive
  • 99. Great Ressources http://fr.slideshare.net/nachomartin/symfony-javascript- combining-the-best-of-two-worlds http://williamdurand.fr/2012/08/02/rest-apis-with- symfony2-the-right-wayapp.wisembly.com/sflive
  • 100. @guillaumepotier http://wisembly.com/en/about#jobsapp.wisembly.com/sflive
  • 101. Any Questions ?app.wisembly.com/sflive
  • 102. app.wisembly.com/sflive