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

PPT
Workshop: Symfony2 Intruduction: (Controller, Routing, Model)
PPT
Symfony2 and AngularJS
PPT
Building Single Page Application (SPA) with Symfony2 and AngularJS
PDF
Symfony3 w duecie z Vue.js
PPTX
Spring Surf 101
PDF
Fisl 11 - Dicas de Desenvolvimento Web com Ruby
PPTX
How to Build SPA with Vue Router 2.0
PDF
Bullet: The Functional PHP Micro-Framework
Workshop: Symfony2 Intruduction: (Controller, Routing, Model)
Symfony2 and AngularJS
Building Single Page Application (SPA) with Symfony2 and AngularJS
Symfony3 w duecie z Vue.js
Spring Surf 101
Fisl 11 - Dicas de Desenvolvimento Web com Ruby
How to Build SPA with Vue Router 2.0
Bullet: The Functional PHP Micro-Framework

What's hot (20)

PDF
2021laravelconftwslides11
PDF
Curso Symfony - Clase 4
PPTX
Service approach for development REST API in Symfony2
ODP
Creating REST Applications with the Slim Micro-Framework by Vikram Vaswani
PDF
Symfony tips and tricks
PPTX
Service approach for development Rest API in Symfony2
PDF
REST in practice with Symfony2
KEY
Silex, the microframework
PDF
AngularJS with Slim PHP Micro Framework
PDF
Hello World on Slim Framework 3.x
PDF
Using Renderless Components in Vue.js during your software development.
PPT
Creating the interfaces of the future with the APIs of today
PPT
How to learn to build your own PHP framework
PDF
Silex: From nothing to an API
PDF
Curso Symfony - Clase 2
PPTX
Wykorzystanie form request przy implementacji API w Laravelu
KEY
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
PDF
Workshop 6: Designer tools
PDF
20130528 solution linux_frousseau_nopain_webdev
PDF
In The Trenches With Tomster, Upgrading Ember.js & Ember Data
2021laravelconftwslides11
Curso Symfony - Clase 4
Service approach for development REST API in Symfony2
Creating REST Applications with the Slim Micro-Framework by Vikram Vaswani
Symfony tips and tricks
Service approach for development Rest API in Symfony2
REST in practice with Symfony2
Silex, the microframework
AngularJS with Slim PHP Micro Framework
Hello World on Slim Framework 3.x
Using Renderless Components in Vue.js during your software development.
Creating the interfaces of the future with the APIs of today
How to learn to build your own PHP framework
Silex: From nothing to an API
Curso Symfony - Clase 2
Wykorzystanie form request przy implementacji API w Laravelu
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Workshop 6: Designer tools
20130528 solution linux_frousseau_nopain_webdev
In The Trenches With Tomster, Upgrading Ember.js & Ember Data

Viewers also liked (11)

PDF
How Kris Writes Symfony Apps
PDF
Symfony & Javascript. Combining the best of two worlds
PDF
Matters of State
PDF
Scaling Symfony2 apps with RabbitMQ - Symfony UK Meetup
PDF
Introduction to WAMP, a protocol enabling PUB/SUB and RPC over Websocket
PPTX
Behind the scenes of Real-Time Notifications
PPT
Going crazy with Node.JS and CakePHP
PDF
Building microservices
PDF
The Wonderful World of Symfony Components
PDF
Git Flow: un processus de développement Agile
PDF
Node Foundation Membership Overview 20160907
How Kris Writes Symfony Apps
Symfony & Javascript. Combining the best of two worlds
Matters of State
Scaling Symfony2 apps with RabbitMQ - Symfony UK Meetup
Introduction to WAMP, a protocol enabling PUB/SUB and RPC over Websocket
Behind the scenes of Real-Time Notifications
Going crazy with Node.JS and CakePHP
Building microservices
The Wonderful World of Symfony Components
Git Flow: un processus de développement Agile
Node Foundation Membership Overview 20160907

Similar to Symfony2, Backbone.js &amp; socket.io - SfLive Paris 2k13 - Wisembly (20)

PDF
Bentobox model for understanding technology stacks
PDF
Building Cross Platform Mobile Web Apps
PPTX
WebBee rapid web app development teck stack
PDF
Bentobox exercise
PDF
symfony: Simplify your professional web development with PHP (IPC Frankfurt 2...
PDF
Find me if you can – smart fuzzing and discovery! shreeraj shah
PDF
Cross platform mobile web apps
PDF
Don Schwarz App Engine Talk
PDF
Vaadin - Rich Web Applications in Server-side Java without Plug-ins or JavaSc...
PDF
symfony: Open-Source Enterprise Framework
PPTX
Women Who Code, Ground Floor
PDF
HTML5 and the dawn of rich mobile web applications pt 1
PPT
FIND ME IF YOU CAN – SMART FUZZING AND DISCOVERY
PDF
What is Google App Engine?
PDF
Jax2010 adobe lcds
PDF
WCM-7 Surfing with CMIS
PDF
Frameworks
KEY
Cloud Foundry Bootcamp
PDF
RubyConf Brazil 2011
PDF
Shreeraj-Hacking_Web_2
Bentobox model for understanding technology stacks
Building Cross Platform Mobile Web Apps
WebBee rapid web app development teck stack
Bentobox exercise
symfony: Simplify your professional web development with PHP (IPC Frankfurt 2...
Find me if you can – smart fuzzing and discovery! shreeraj shah
Cross platform mobile web apps
Don Schwarz App Engine Talk
Vaadin - Rich Web Applications in Server-side Java without Plug-ins or JavaSc...
symfony: Open-Source Enterprise Framework
Women Who Code, Ground Floor
HTML5 and the dawn of rich mobile web applications pt 1
FIND ME IF YOU CAN – SMART FUZZING AND DISCOVERY
What is Google App Engine?
Jax2010 adobe lcds
WCM-7 Surfing with CMIS
Frameworks
Cloud Foundry Bootcamp
RubyConf Brazil 2011
Shreeraj-Hacking_Web_2

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

  • 1. Symfony2, Backbone.js & socket.io Symfony live Paris 2013 @guillaumepotier [email protected]
  • 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