SlideShare a Scribd company logo
Symfony2, Backbone.js
     & socket.io


    Symfony live Paris 2013

         @guillaumepotier
    guillaume@wisembly.com
app.wisembly.com/sflive


app.wisembly.com/sflive
2011, September

app.wisembly.com/sflive
MySQL

                          PHP



app.wisembly.com/sflive
Twig

                         Symfony 2

                         Doctrine 2

                          MySQL

                            PHP



app.wisembly.com/sflive
Twig js

                          jQuery

                          Assetic

                           Twig

                         Symfony 2

                         Doctrine 2

                          MySQL

                            PHP



app.wisembly.com/sflive
Backbone.js

                         Underscore.js

                            Twig js

                            jQuery

                            Assetic

                             Twig

                          Symfony 2

                          Doctrine 2

                            MySQL

                             PHP



app.wisembly.com/sflive
Backbone.js

                              Underscore.js

                                 Twig js

                                 jQuery
                                              H !
                                 UC
                                 Assetic


                              O M Twig

                         TO    Symfony 2

                               Doctrine 2

                                 MySQL

                                  PHP



app.wisembly.com/sflive
Client




  Server


app.wisembly.com/sflive
Client




                         Twig                REST
                                Symfony 2

  Server                        Doctrine 2
                                 MySQL

                                   PHP

app.wisembly.com/sflive
Backbone.js
                            Underscore.js
  Client                          jQuery
                                  HTML




                         Twig                REST
                                Symfony 2

  Server                        Doctrine 2
                                  MySQL

                                   PHP

app.wisembly.com/sflive
Backbone.js
                                  Underscore.js
  Client                                jQuery


                                                            !
                                        HTML


                                                        ING
                                     L                L
                                  PO
                               NG
                         L   O Twig                REST
                                      Symfony 2

  Server                              Doctrine 2
                                        MySQL

                                         PHP

app.wisembly.com/sflive
Users want fast & smooth SaaS apps




app.wisembly.com/sflive
Users want fast & smooth SaaS apps
                 Users want multiplateform SaaS apps




app.wisembly.com/sflive
Users want fast & smooth SaaS apps
              Users want multiplateform SaaS apps
           Users want dynamic & interactive SaaS apps




app.wisembly.com/sflive
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
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 U
app.wisembly.com/sflive
app.wisembly.com/sflive
?
app.wisembly.com/sflive
E L L
                         H !
                          N  O
                           ?
app.wisembly.com/sflive
app.wisembly.com/sflive
Nowadays


app.wisembly.com/sflive
app.wisembly.com/sflive
small / lightweight / stable




app.wisembly.com/sflive
small / lightweight / stable

                         easy to learn, easy to extend




app.wisembly.com/sflive
small / lightweight / stable

                         easy to learn, easy to extend

                               great resources:

                                 layoutManager
                                   relational



app.wisembly.com/sflive
Models   Models




app.wisembly.com/sflive
Models       Models

             Collections   Repositories




app.wisembly.com/sflive
Models       Models

             Collections   Repositories

                  Views    Controllers




app.wisembly.com/sflive
Models       Models

             Collections   Repositories

                  Views    Controllers

              Templates       Views



app.wisembly.com/sflive
Models       Models

             Collections   Repositories

                  Views    Controllers

              Templates       Views

                Routing      Routing
app.wisembly.com/sflive
Models           Models

             Collections       Repositories

                  Views        Controllers
                                              S H
                               ViewsP
                                     U
              Templates          +
                           RE ST
                Routing          Routing
app.wisembly.com/sflive
FOSJsRouting
 BazingaExposeTranslation
             JMSSerializer
          FOSRestBundle




app.wisembly.com/sflive
FOSJsRouting
 BazingaExposeTranslation
             JMSSerializer
          FOSRestBundle




app.wisembly.com/sflive
1 - MAKE AN API



app.wisembly.com/sflive
MUST READ


           http://fr.slideshare.net/nachomartin/symfony-
           javascript-combining-the-best-of-two-worlds




app.wisembly.com/sflive
ar tin
                                    nacm
                                   @


                         Books = new
                         Backbone.collection();
                         Books.url = ‘/books’;




app.wisembly.com/sflive
ar tin
                                    nacm
                                   @


                         Books = new
                         Backbone.collection();
                         Books.url = ‘/books’;
                         Books.fetch();




                           GET /books
app.wisembly.com/sflive
ar tin
                                    nacm
                                   @

                         events:
                         { ‘click
                         .mybutton’:‘doStuffAndSave’ }

                         doStuffAndSave: function() {
                           var book = Books.get(3);
                           book.stuff();
                           book.save();
                         }




app.wisembly.com/sflive
ar tin
                                    nacm
                                   @

                         events:
                         { ‘click
                         .mybutton’:‘doStuffAndSave’ }

                         doStuffAndSave: function() {
                           var book = Books.get(3);
                           book.stuff();
                           book.save();
                         }




                           PUT /books/3
app.wisembly.com/sflive
/**
                                         * @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
II - MAKE A GOOD
                    REST API


app.wisembly.com/sflive
MUST READ 2


           http://williamdurand.fr/2012/08/02/rest-apis-
           with-symfony2-the-right-way




app.wisembly.com/sflive
JMSSerializer

                              or




app.wisembly.com/sflive
JMSSerializer

                                           or
         class User implements UserInterface, EquatableInterface, ApiAbleInterface
         {




         }




app.wisembly.com/sflive
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
FOSRestBundle

                              or




app.wisembly.com/sflive
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
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
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
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
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
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
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
Controller


                    Entity Service       API Service


                         Entity       EntityRepository




app.wisembly.com/sflive
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
/**
         * @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
/**
         * @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
/**
         * @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
/**
         * @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
/**
         * @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
app.wisembly.com/sflive
app.wisembly.com/sflive
FOSJsRouting
 BazingaExposeTranslation
             JMSSerializer
          FOSRestBundle




app.wisembly.com/sflive
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
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 BASED
app.wisembly.com/sflive
websocketData?


                              Who sends what?


                         Which port, which protocol?




app.wisembly.com/sflive
app.wisembly.com/sflive
FOSJsRouting
 BazingaExposeTranslation
             JMSSerializer
          FOSRestBundle




app.wisembly.com/sflive
Authenticate user against
     PUSH server




app.wisembly.com/sflive
Authenticate user against
     PUSH server


                            sessionToken
                               domain




app.wisembly.com/sflive
Authenticate user against
     PUSH server


                                           sessionToken
                                              domain




                             REST
                            sessionToken
                               domain


app.wisembly.com/sflive
Authenticate user against
     PUSH server


                                           sessionToken
                                              domain




                             rights

                             REST
                            sessionToken
                               domain


app.wisembly.com/sflive
Authenticate user against
     PUSH server




                         Authenticated!

                              rights

                              REST
                             sessionToken
                                domain


app.wisembly.com/sflive
PUSH: The «Classic» way




                            rights

app.wisembly.com/sflive
PUSH: The «Classic» way




                     REST
                   sessionToken
                      domain




                                  rights

app.wisembly.com/sflive
PUSH: The «Classic» way




                     REST
                   sessionToken
                                  data
                      domain




                                         rights

app.wisembly.com/sflive
PUSH: The «Classic» way




                     REST
                   sessionToken
                                  data
                      domain




                                         rights

app.wisembly.com/sflive
PUSH: The «Classic» way




                                                  websocketData


                     REST
                   sessionToken
                                  data
                      domain




                                         rights

app.wisembly.com/sflive
• Slow: HTTP ajax round-trip
           • !DRY: Double front processing (Ajax / Push)
           • Push server complexity: authorizations


app.wisembly.com/sflive
PUSH: The «Wisembly» way




                                                  websocketData


                     REST
                   sessionToken
                                  data
                      domain




                                         rights

app.wisembly.com/sflive
PUSH: The «Wisembly» way




                                                           websocketData


                     REST
                   sessionToken
                      domain



                                  websocketData


                         secret
                                                  rights

app.wisembly.com/sflive
PUSH: The «Wisembly» way




                                                            websocketData


                     REST
                   sessionToken
                      domain            websocketData



                                  websocketData


                         secret
                                                   rights

app.wisembly.com/sflive
PUSH: The «Wisembly» way




                                                                   websocketData


                     REST
                   sessionToken
                                  data         websocketData
                      domain



                                         websocketData


                         secret
                                                          rights

app.wisembly.com/sflive
Push «surprises»




app.wisembly.com/sflive
Push «surprises»

      • Must find always opened port




app.wisembly.com/sflive
Push «surprises»

      • Must find always opened port
      • Websocket protocol must go through firewalls



app.wisembly.com/sflive
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
app.wisembly.com/sflive
• 80 always opened, but websocket very
               often blocked -> FAIL -> goto 443 w/ https




app.wisembly.com/sflive
• 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
The «Wisembly» way




                         hashN: { eventName, args }


app.wisembly.com/sflive
The «Wisembly» way




                         hashN: { eventName, args }


app.wisembly.com/sflive
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
The «Wisembly» way

                                       hashM   hashM



                               hashN




 hashN: { eventName, args }
            ...
hashN+M: { eventName, args }




app.wisembly.com/sflive
The «Wisembly» way

                                                      hashM   hashM



                                              hashN
                                    REST
                               onReconect()
                                since hashN

 hashN: { eventName, args }
            ...
hashN+M: { eventName, args }




app.wisembly.com/sflive
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
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
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-way




app.wisembly.com/sflive
@guillaumepotier



   http://wisembly.com/en/about#jobs




app.wisembly.com/sflive
Any Questions ?




app.wisembly.com/sflive
app.wisembly.com/sflive

More Related Content

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

  • 1. Symfony2, Backbone.js & socket.io Symfony live Paris 2013 @guillaumepotier guillaume@wisembly.com
  • 4. MySQL PHP app.wisembly.com/sflive
  • 5. Twig Symfony 2 Doctrine 2 MySQL PHP app.wisembly.com/sflive
  • 6. Twig js jQuery Assetic Twig Symfony 2 Doctrine 2 MySQL PHP app.wisembly.com/sflive
  • 7. Backbone.js Underscore.js Twig js jQuery Assetic Twig Symfony 2 Doctrine 2 MySQL PHP app.wisembly.com/sflive
  • 8. Backbone.js Underscore.js Twig js jQuery H ! UC Assetic O M Twig TO Symfony 2 Doctrine 2 MySQL PHP app.wisembly.com/sflive
  • 10. Client Twig REST Symfony 2 Server Doctrine 2 MySQL PHP app.wisembly.com/sflive
  • 11. Backbone.js Underscore.js Client jQuery HTML Twig REST Symfony 2 Server Doctrine 2 MySQL PHP app.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 PHP app.wisembly.com/sflive
  • 13. Users want fast & smooth SaaS apps app.wisembly.com/sflive
  • 14. Users want fast & smooth SaaS apps Users want multiplateform SaaS apps app.wisembly.com/sflive
  • 15. Users want fast & smooth SaaS apps Users want multiplateform SaaS apps Users want dynamic & interactive SaaS apps app.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 U app.wisembly.com/sflive
  • 20. E L L H ! N O ? app.wisembly.com/sflive
  • 24. small / lightweight / stable app.wisembly.com/sflive
  • 25. small / lightweight / stable easy to learn, easy to extend app.wisembly.com/sflive
  • 26. small / lightweight / stable easy to learn, easy to extend great resources: layoutManager relational app.wisembly.com/sflive
  • 27. Models Models app.wisembly.com/sflive
  • 28. Models Models Collections Repositories app.wisembly.com/sflive
  • 29. Models Models Collections Repositories Views Controllers app.wisembly.com/sflive
  • 30. Models Models Collections Repositories Views Controllers Templates Views app.wisembly.com/sflive
  • 31. Models Models Collections Repositories Views Controllers Templates Views Routing Routing app.wisembly.com/sflive
  • 32. Models Models Collections Repositories Views Controllers S H ViewsP U Templates + RE ST Routing Routing app.wisembly.com/sflive
  • 33. FOSJsRouting BazingaExposeTranslation JMSSerializer FOSRestBundle app.wisembly.com/sflive
  • 34. FOSJsRouting BazingaExposeTranslation JMSSerializer FOSRestBundle app.wisembly.com/sflive
  • 35. 1 - MAKE AN API app.wisembly.com/sflive
  • 36. MUST READ http://fr.slideshare.net/nachomartin/symfony- javascript-combining-the-best-of-two-worlds app.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 /books app.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/3 app.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 API app.wisembly.com/sflive
  • 43. MUST READ 2 http://williamdurand.fr/2012/08/02/rest-apis- with-symfony2-the-right-way app.wisembly.com/sflive
  • 44. JMSSerializer or app.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 or app.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 EntityRepository app.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
  • 64. FOSJsRouting BazingaExposeTranslation JMSSerializer FOSRestBundle app.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 BASED app.wisembly.com/sflive
  • 67. websocketData? Who sends what? Which port, which protocol? app.wisembly.com/sflive
  • 69. FOSJsRouting BazingaExposeTranslation JMSSerializer FOSRestBundle app.wisembly.com/sflive
  • 70. Authenticate user against PUSH server app.wisembly.com/sflive
  • 71. Authenticate user against PUSH server sessionToken domain app.wisembly.com/sflive
  • 72. Authenticate user against PUSH server sessionToken domain REST sessionToken domain app.wisembly.com/sflive
  • 73. Authenticate user against PUSH server sessionToken domain rights REST sessionToken domain app.wisembly.com/sflive
  • 74. Authenticate user against PUSH server Authenticated! rights REST sessionToken domain app.wisembly.com/sflive
  • 75. PUSH: The «Classic» way rights app.wisembly.com/sflive
  • 76. PUSH: The «Classic» way REST sessionToken domain rights app.wisembly.com/sflive
  • 77. PUSH: The «Classic» way REST sessionToken data domain rights app.wisembly.com/sflive
  • 78. PUSH: The «Classic» way REST sessionToken data domain rights app.wisembly.com/sflive
  • 79. PUSH: The «Classic» way websocketData REST sessionToken data domain rights app.wisembly.com/sflive
  • 80. • Slow: HTTP ajax round-trip • !DRY: Double front processing (Ajax / Push) • Push server complexity: authorizations app.wisembly.com/sflive
  • 81. PUSH: The «Wisembly» way websocketData REST sessionToken data domain rights app.wisembly.com/sflive
  • 82. PUSH: The «Wisembly» way websocketData REST sessionToken domain websocketData secret rights app.wisembly.com/sflive
  • 83. PUSH: The «Wisembly» way websocketData REST sessionToken domain websocketData websocketData secret rights app.wisembly.com/sflive
  • 84. PUSH: The «Wisembly» way websocketData REST sessionToken data websocketData domain websocketData secret rights app.wisembly.com/sflive
  • 86. Push «surprises» • Must find always opened port app.wisembly.com/sflive
  • 87. Push «surprises» • Must find always opened port • Websocket protocol must go through firewalls app.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
  • 90. • 80 always opened, but websocket very often blocked -> FAIL -> goto 443 w/ https app.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-way app.wisembly.com/sflive
  • 100. @guillaumepotier http://wisembly.com/en/about#jobs app.wisembly.com/sflive