SlideShare uma empresa Scribd logo
1 de 86
Baixar para ler offline
doctrine
                  Doctrine in the Real World
                          Real world examples




Friday, March 4, 2011
My name is
                        Jonathan H. Wage


Friday, March 4, 2011
• PHP Developer for 10+ years
                        • Long time Symfony and Doctrine
                          contributor

                        • Published Author
                        • Entrepreneur
                        • Currently living in Nashville, Tennessee

Friday, March 4, 2011
Previously employed by
                        SensioLabs


Friday, March 4, 2011
http://mongodbhosting.com

                         Partnered with
                          ServerGrove
                        MongoDB Hosting


Friday, March 4, 2011
Today, I work full-time
                            for OpenSky
                             http://shopopensky.com



Friday, March 4, 2011
What is OpenSky?



Friday, March 4, 2011
A new way to shop

                        • OpenSky connects you with innovators,
                          trendsetters and tastemakers.You choose
                          the ones you like and each week they invite
                          you to their private online sales.




Friday, March 4, 2011
OpenSky Loves
                                     OpenSource
                        •   PHP 5.3

                        •   Apache2

                        •   Symfony2

                        •   Doctrine2

                        •   jQuery

                        •   mule, stomp, hornetq

                        •   MongoDB

                        •   nginx

                        •   varnish

Friday, March 4, 2011
We don’t just use open
                      source projects


Friday, March 4, 2011
We help build them



Friday, March 4, 2011
OpenSky has some of
                        the top committers in
                         Symfony2 and other
                               projects

Friday, March 4, 2011
Symfony2 OpenSky
                             Committers
                        • 65   Kris Wallsmith

                        • 52   Jonathan H. Wage

                        • 36   Jeremy Mikola

                        • 36   Bulat Shakirzyanov

                        •6     Justin Hileman




Friday, March 4, 2011
Doctrine MongoDB
                               Committers
                        •   39 Jonathan H. Wage

                        •   11 Bulat Shakirzyanov

                        •   2   Kris Wallsmith




Friday, March 4, 2011
MongoDB ODM
                                  Committers
                        •   349 Jonathan H. Wage

                        •   226 Bulat Shakirzyanov

                        •   17   Kris Wallsmith

                        •   13   Steven Surowiec

                        •   2    Jeremy Mikola




Friday, March 4, 2011
OpenSky uses both the
           Doctrine ORM and ODM


Friday, March 4, 2011
Why?



Friday, March 4, 2011
We are an eCommerce
                           site


Friday, March 4, 2011
Actions involving
                        commerce need
                          transactions


Friday, March 4, 2011
ORM and MySQL

                        • Order
                        • OrderTransaction
                        • OrderShipment


Friday, March 4, 2011
ODM and MongoDB
                        • Product
                        • Seller
                        • Supplier
                        • User
                        • ... basically everything else that is not
                          involving $$$ and transactions


Friday, March 4, 2011
Blending the Two



Friday, March 4, 2011
Defining our Product
                            Document


Friday, March 4, 2011
/** @mongodb:Document(collection="products") */
                        class Product
                        {
                            /** @mongodb:Id */
                            private $id;

                            /** @mongodb:String */
                            private $title;

                            public function getId()
                            {
                                return $this->id;
                            }

                            public function getTitle()
                            {
                                return $this->title;
                            }

                            public function setTitle($title)
                            {
                                $this->title = $title;
                            }
                        }



Friday, March 4, 2011
Defining our Order
                              Entity


Friday, March 4, 2011
/**
                          * @orm:Entity
                          * @orm:Table(name="orders")
                          * @orm:HasLifecycleCallbacks
                          */
                        class Order
                        {
                             /**
                              * @orm:Id @orm:Column(type="integer")
                              * @orm:GeneratedValue(strategy="AUTO")
                              */
                             private $id;

                            /**
                             * @orm:Column(type="string")
                             */
                            private $productId;

                            /**
                             * @var DocumentsProduct
                             */
                            private $product;

                            // ...
                        }


Friday, March 4, 2011
Setting the Product

                        public function setProduct(Product $product)
                        {
                            $this->productId = $product->getId();
                            $this->product = $product;
                        }




Friday, March 4, 2011
• $productId is mapped and persisted
                        • but $product which stores the Product
                          instance is not a persistent entity property




Friday, March 4, 2011
Order has a reference
                            to product?

                        • How?
                         • Order is an ORM entity stored in MySQL
                         • and Product is an ODM document stored
                            in MongoDB




Friday, March 4, 2011
Loading Product ODM
                      reference in Order
                             Entity


Friday, March 4, 2011
Lifecycle Events to the
                                Rescue


Friday, March 4, 2011
EventManager

   • Event system is controlled by the EventManager
   • Central point of event listener system
   • Listeners are registered on the manager
   • Events are dispatched through the manager

Friday, March 4, 2011
Add EventListener

                        $eventListener = new OrderPostLoadListener($dm);
                        $eventManager = $em->getEventManager();
                        $eventManager->addEventListener(
                            array(DoctrineORMEvents::postLoad), $eventListener
                        );




Friday, March 4, 2011
In Symfony2 DI
   <?xml version="1.0" encoding="utf-8" ?>
   <container xmlns="http://www.symfony-project.org/schema/dic/services"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/
   schema/dic/services/services-1.0.xsd">

         <parameters>
             <parameter key="order.post_load.listener.class">OrderPostLoadListener</parameter>
         </parameters>

       <services>
           <service id="order.post_load.listener" class="%order.post_load.listener.class%" scope="container">
                <argument type="service" id="doctrine.odm.mongodb.default_document_manager" />
                <tag name="doctrine.orm.default_event_listener" event="postLoad" />
           </service>
       </services>
   </container>




Friday, March 4, 2011
OrderPostLoadListener
    use DoctrineODMMongoDBDocumentManager;
    use DoctrineORMEventLifecycleEventArgs;

    class OrderPostLoadListener
    {
        public function __construct(DocumentManager $dm)
        {
            $this->dm = $dm;
        }

           public function postLoad(LifecycleEventArgs $eventArgs)
           {
               // get the order entity
               $order = $eventArgs->getEntity();

                   // get odm reference to order.product_id
                   $productId = $order->getProductId();
                   $product = $this->dm->getReference('MyBundle:DocumentProduct', $productId);

                   // set the product on the order
                   $em = $eventArgs->getEntityManager();
                   $productReflProp = $em->getClassMetadata('MyBundle:EntityOrder')
                       ->reflClass->getProperty('product');
                   $productReflProp->setAccessible(true);
                   $productReflProp->setValue($order, $product);
           }
    }




Friday, March 4, 2011
All Together Now
                        // Create a new product and order
                        $product = new Product();
                        $product->setTitle('Test Product');
                        $dm->persist($product);
                        $dm->flush();

                        $order = new Order();
                        $order->setProduct($product);
                        $em->persist($order);
                        $em->flush();

                        // Find the order later
                        $order = $em->find('Order', $order->getId());

                        // Instance of an uninitialized product proxy
                        $product = $order->getProduct();

                        // Initializes proxy and queries the monogodb database
                        echo "Order Title: " . $product->getTitle();
                        print_r($order);




Friday, March 4, 2011
Seamless

                        • Documents and Entities play together like
                          best friends
                        • Because Doctrine persistence remains
                          transparent from your domain this is
                          possible




Friday, March 4, 2011
print_r($order)
         Order Object
         (
             [id:EntitiesOrder:private] => 53
             [productId:EntitiesOrder:private] => 4c74a1868ead0ed7a9000000
             [product:EntitiesOrder:private] => ProxiesDocumentProductProxy Object
                 (
                     [__isInitialized__] => 1
                     [id:DocumentsProduct:private] => 4c74a1868ead0ed7a9000000
                     [title:DocumentsProduct:private] => Test Product
                 )

         )




Friday, March 4, 2011
Example from Blog

                        • This example was first written on my
                          personal blog http://jwage.com


                        • You can read the blog post here http://
                          jwage.com/2010/08/25/blending-the-
                          doctrine-orm-and-mongodb-odm/



Friday, March 4, 2011
MongoDB ODM
                SoftDelete Functionality


Friday, March 4, 2011
I like my deletes soft,
                               not hard


Friday, March 4, 2011
Why?



Friday, March 4, 2011
Deleting data is
                        dangerous business


Friday, March 4, 2011
Flickr accidentally
                   deleted a pro members
                      account and 5000
                           pictures

Friday, March 4, 2011
They were able to
                        restore it later but it
                          took some time


Friday, March 4, 2011
Instead of deleting, simply
               set a deletedAt field


Friday, March 4, 2011
Install SoftDelete
                   Extension for Doctrine
                      MongoDB ODM
              http://github.com/doctrine/mongodb-odm-softdelete

  $ git clone git://github.com/doctrine/mongodb-odm-softdelete src/
 vendor/doctrine-mongodb-odm-softdelete




Friday, March 4, 2011
Autoload Extension
    $loader = new UniversalClassLoader();
    $loader->registerNamespaces(array(
        // ...
        'DoctrineODMMongoDBSoftDelete' => __DIR__.'/vendor/doctrine-mongodb-odm-
    softdelete/lib',
    ));
    $loader->register();




Friday, March 4, 2011
Raw PHP Configuration

                 use DoctrineODMMongoDBSoftDeleteUnitOfWork;
                 use DoctrineODMMongoDBSoftDeleteSoftDeleteManager;
                 use DoctrineCommonEventManager;

                 // $dm is a DocumentManager instance we should already have
                                               use DoctrineODMMongoDBSoftDeleteConfiguration;


                 $config = new Configuration();
                 $uow = new UnitOfWork($dm, $config);
                 $evm = new EventManager();
                 $sdm = new SoftDeleteManager($dm, $config, $uow, $evm);




Friday, March 4, 2011
Symfony2 Integration
   http://github.com/doctrine/mongodb-odm-softdelete-bundle




  $ git clone git://github.com/doctrine/mongodb-odm-softdelete-bundle.git
 src/vendor/doctrine-mongodb-odm-softdelete-bundle




Friday, March 4, 2011
Autoload the Bundle

    $loader = new UniversalClassLoader();
    $loader->registerNamespaces(array(
        // ...
        'DoctrineODMMongoDBSymfonySoftDeleteBundle' => __DIR__.'/vendor/doctrine-
    mongodb-odm-softdelete-bundle',
    ));
    $loader->register();




Friday, March 4, 2011
Register the Bundle
                 public function registerBundles()
                 {
                     $bundles = array(
                         // ...

                             // register doctrine symfony bundles
                             new DoctrineODMMongoDBSymfonySoftDeleteBundleSoftDeleteBundle()
                        );

                        // ...

                        return $bundles;
                 }




Friday, March 4, 2011
Enable the Bundle


                 // app/config/config.yml

                 doctrine_mongodb_softdelete.config: ~




Friday, March 4, 2011
SoftDeleteManager


          $sdm = $container->get('doctrine.odm.mongodb.soft_delete.manager');




Friday, March 4, 2011
SoftDeleteable
                ODM Documents must implement this interface


                             interface SoftDeleteable
                             {
                                 function getDeletedAt();
                             }




Friday, March 4, 2011
User implements
                              SoftDeletable
                        /** @mongodb:Document */
                        class User implements SoftDeleteable
                        {
                            /** @mongodb:Date @mongodb:Index */
                            private $deletedAt;

                            public function getDeletedAt()
                            {
                                return $this->deletedAt;
                            }
                        }




Friday, March 4, 2011
SoftDelete a User
              $user = new User('jwage');
              // ...
              $dm->persist($user);
              $dm->flush();

              // later we can soft delete the user jwage
              $user = $dm->getRepository('User')->findOneByUsername('jwage');
              $sdm->delete($user);
              $sdm->flush();




Friday, March 4, 2011
Query Executed
              db.users.update(
                  {
                      _id : {
                          $in : [new ObjectId('1234567891011123456')]
                      }
                  },
                  {
                      $set : {
                          deletedAt: new Date()
                      }
                  }
              )




Friday, March 4, 2011
Restore a User

              // now again later we can restore that same user
              $user = $dm->getRepository('User')->findOneByUsername('jwage');
              $sdm->restore($user);
              $sdm->flush();




Friday, March 4, 2011
Query Executed
              db.users.update(
                  {
                      _id : {
                          $in : [new ObjectId('1234567891011123456')]
                      }
                  },
                  {
                      $unset : {
                          deletedAt: true
                      }
                  }
              )



Friday, March 4, 2011
Limit cursors to only
                 show non deleted users

            $qb = $dm->createQueryBuilder('User')
                ->field('deletedAt')->exists(false);
            $query = $qb->getQuery();
            $users = $query->execute();




Friday, March 4, 2011
Get only deleted users

            $qb = $dm->createQueryBuilder('User')
                ->field('deletedAt')->exists(true);
            $query = $qb->getQuery();
            $users = $query->execute();




Friday, March 4, 2011
Restore several deleted
                           users
            $qb = $dm->createQueryBuilder('User')
                ->field('deletedAt')->exists(true)
                ->field('createdAt')->gt(new DateTime('-24 hours'));
            $query = $qb->getQuery();
            $users = $query->execute();

            foreach ($users as $user) {
                $sdm->restore($user);
            }
            $sdm->flush();




Friday, March 4, 2011
Soft Delete Events

                           class TestEventSubscriber implements DoctrineCommonEventSubscriber
                           {
                               public function preSoftDelete(LifecycleEventArgs $args)
                               {
                                   $document = $args->getDocument();
    - preDelete                }
                                   $sdm = $args->getSoftDeleteManager();


    - postDelete               public function getSubscribedEvents()
                               {
    - preRestore               }
                                   return array(Events::preSoftDelete);


    - postRestore          }


                           $eventSubscriber = new TestEventSubscriber();
                           $evm->addEventSubscriber($eventSubscriber);




Friday, March 4, 2011
PHP Daemons



Friday, March 4, 2011
Symfony2 and
                         supervisor
                         http://supervisord.org/



Friday, March 4, 2011
What is supervisor?



Friday, March 4, 2011
Supervisor is a client/server system
         that allows its users to monitor and
         control a number of processes on
         UNIX-like operating systems.

                        http://supervisord.org


Friday, March 4, 2011
Daemonize a Symfony2
                    Console Command
                      with supervisor



Friday, March 4, 2011
Scenario

                        • You want to send an e-mail when new
                          users register in your system.
                        • But, sending an e-mail directly from your
                          action introduces a failure point to your
                          stack.
                        • ....What do you do?

Friday, March 4, 2011
Tailable Cursor
                        • Use a tailable mongodb cursor
                         • Tail a NewUser document collection
                         • Insert NewUser documents from your
                            actions
                         • The daemon will instantly process the
                            NewUser after it is inserted and dispatch
                            the e-mail


Friday, March 4, 2011
Define NewUser
             namespace MyCompanyBundleMyBundleDocument;

             /**
               * @mongodb:Document(collection={
               *   "name"="new_users",
               *   "capped"="true",
               *   "size"="100000",
               *   "max"="1000"
               * }, repositoryClass="MyCompanyBundleMyBundleDocumentNewUserRepository")
               */
             class NewUser
             {
                  /** @mongodb:Id */
                  private $id;

                    /** @mongodb:ReferenceOne(targetDocument="User") */
                    private $user;

                    /** @mongodb:Boolean @mongodb:Index */
                    private $isProcessed = false;

                    // ...
             }


Friday, March 4, 2011
Create Collection
                        • The NewUser collection must be capped in
                          order to tail it so we need to create it.
                        • Luckily, Doctrine has a console command
                          for it.
                        • It will read the mapping information we
                          configured and create the collection

  $ php app/console doctrine:mongodb:schema:create --class="MyBundle:NewUser" --collection



Friday, March 4, 2011
Insert NewUser upon
                             Registration
                        public function register()
                        {
                            // ...

                            $user = new User();
                            $form = new RegisterForm('register', $user, $validator);

                            $form->bind($request, $user);
                            if ($form->isValid()) {
                                $newUser = new NewUser($user);
                                $dm->persist($newUser);
                                $dm->persist($user);
                                $dm->flush();

                                // ...
                            }
                            // ...
                        }



Friday, March 4, 2011
Executing Console
                                   Command
  $ php app/console doctrine:mongodb:tail-cursor MyBundle:NewUser findUnProcessed
  new_user.processor




                        •   The command requires 3 arguments:

                            •   document - the name of the document to tail

                            •   finder - the repository finder method used to get the cursor

                            •   processor - the id of the service used to process the new
                                users


Friday, March 4, 2011
findUnProcessed()

                        • We need the findUnProcessed() method to
                    class NewUserRepository extends DocumentRepository
                    {
                          return the unprocessed cursor to tail
                        public function findUnProcessed()
                        {
                            return $this->createQueryBuilder()
                                ->field('isProcessed')->equals(false)
                                ->getQuery()
                                ->execute();
                        }
                    }




Friday, March 4, 2011
NewUserProcessor
   We need a service id new_user.processor with a
  process(OutputInterface $output, $document) method
                    use Swift_Message;
                    use SymfonyComponentConsoleOutputOutputInterface;

                    class NewUserProcessor
                    {
                        private $mailer;

                        public function __construct($mailer)
                        {
                            $this->mailer = $mailer;
                        }

                        public function process(OutputInterface $output, $document)
                        {
                        }
                    }

Friday, March 4, 2011
Send the e-mail
        public function process(OutputInterface $output, $document)
        {
            $user = $document->getUser();

                   $message = Swift_Message::newInstance()
                       ->setSubject('New Registration')
                       ->setFrom('noreply@domain.com')
                       ->setTo($user->getEmail())
                       ->setBody('New user registration')
                   ;
                   $this->mailer->send($message);

                   $document->setIsProcessed(true);
        }


Friday, March 4, 2011
Tailable Cursor Bundle


          https://github.com/doctrine/doctrine-mongodb-odm-
                           tailable-cursor-bundle




Friday, March 4, 2011
Daemonization
                        • Now, how do we really daemonize the
                          console command and keep it running 24
                          hours a day, 7 days a week?


                        • The answer is supervisor, it will allow us to
                          configure a console command for it to
                          manage the process id of and always keep
                          an instance of it running.


Friday, March 4, 2011
Install supervisor
                        http://supervisord.org/installing.html



                        $ easy_install supervisor




Friday, March 4, 2011
Configure a Profile
              • We need to configure a profile for supervisor to
                      know how to run the console command
/
                            $ vi /etc/supervisor/conf.d/tail-new-user.conf
r
            [program:tail-new-user]
            numprocs=1
            startretries=100
            directory=/
            stdout_logfile=/path/to/symfonyproject/app/logs/tail-new-user-supervisord.log
            autostart=true
            autorestart=true
            user=root
            command=/usr/local/bin/php /path/to/symfonyproject/app/console
            doctrine:mongodb:tail-cursor MyBundle:NewUser findUnprocessed
            new_user.processor
    Friday, March 4, 2011
Start supervisord
                        • Start an instance of supervisord
                        • It will run as a daemon in the background
                        • The tail-new-user.conf will always be
                          running

                                    $ supervisord




Friday, March 4, 2011
Where do I use
                               supervisor?
                        • http://sociallynotable.com
                         • Keeps daemon running that watches
                            twitter
                         • Indexes tweets with links to amazon
                            products
                         • Maintains tweet statistics and ranks the
                            popular products


Friday, March 4, 2011
Thanks!
                    I hope this presentation was useful to you!




Friday, March 4, 2011
Questions?
                        - http://jwage.com
                        - http://twitter.com/jwage
                        - http://facebook.com/jwage
                        - http://about.me/jwage
                        - http://shopopensky.com
                        - http://mongodbhosting.com
                        - http://servergrove.com
                        - http://sociallynotable.com


Friday, March 4, 2011

Mais conteúdo relacionado

Destaque

ORM dont kill your DB, developers do
ORM dont kill your DB, developers doORM dont kill your DB, developers do
ORM dont kill your DB, developers doGuilherme Blanco
 
Effective Doctrine2: Performance Tips for Symfony2 Developers
Effective Doctrine2: Performance Tips for Symfony2 DevelopersEffective Doctrine2: Performance Tips for Symfony2 Developers
Effective Doctrine2: Performance Tips for Symfony2 DevelopersMarcin Chwedziak
 
Web 2.0 Collaboration – Using digital tools for redesigning governance
Web 2.0 Collaboration – Using digital tools for redesigning governanceWeb 2.0 Collaboration – Using digital tools for redesigning governance
Web 2.0 Collaboration – Using digital tools for redesigning governancePaul Gilbreath
 
Projeto software alem da tecnologia v2
Projeto   software alem da tecnologia v2Projeto   software alem da tecnologia v2
Projeto software alem da tecnologia v2Roberto Brandini
 
História do Escritório Virtual de Aracaju
História do Escritório Virtual de AracajuHistória do Escritório Virtual de Aracaju
História do Escritório Virtual de AracajuRosivaldo Nascimento
 
Ecossistemas de startups nordestinos os desafios para a competitividade (2)
Ecossistemas de startups nordestinos  os desafios para a competitividade (2)Ecossistemas de startups nordestinos  os desafios para a competitividade (2)
Ecossistemas de startups nordestinos os desafios para a competitividade (2)Ludmilla Veloso [LION]
 
Análise dos sites dos presidenciáveis - Eleições 2014
Análise dos sites dos presidenciáveis - Eleições 2014Análise dos sites dos presidenciáveis - Eleições 2014
Análise dos sites dos presidenciáveis - Eleições 2014Sueli Bacelar
 
Aula06 matriz em C
Aula06 matriz em CAula06 matriz em C
Aula06 matriz em CYuri Passos
 
Apresentação ForkInSergipe
Apresentação ForkInSergipeApresentação ForkInSergipe
Apresentação ForkInSergipeRafael França
 
IBECC - Contratos Empresariais - Revisão e Controle
IBECC - Contratos Empresariais - Revisão e ControleIBECC - Contratos Empresariais - Revisão e Controle
IBECC - Contratos Empresariais - Revisão e ControleAlexandra Yusiasu dos Santos
 
Network Learning: AI-driven Connectivist Framework for E-Learning 3.0
Network Learning: AI-driven Connectivist Framework for E-Learning 3.0Network Learning: AI-driven Connectivist Framework for E-Learning 3.0
Network Learning: AI-driven Connectivist Framework for E-Learning 3.0Neil Rubens
 
Ruby on rails - CEFET de Lagarto
Ruby on rails - CEFET de LagartoRuby on rails - CEFET de Lagarto
Ruby on rails - CEFET de LagartoDante Regis
 
ThingTank @ MIT-Skoltech Innovation Symposium 2014
ThingTank @ MIT-Skoltech Innovation Symposium 2014ThingTank @ MIT-Skoltech Innovation Symposium 2014
ThingTank @ MIT-Skoltech Innovation Symposium 2014Neil Rubens
 
Social Web Studies - What kind of collaboration is right for your business
Social Web Studies - What kind of collaboration is right for your businessSocial Web Studies - What kind of collaboration is right for your business
Social Web Studies - What kind of collaboration is right for your businessPaul Gilbreath
 
Implementacao e desempenho da virtualizacao no dcomp ufs
Implementacao e desempenho da virtualizacao no dcomp ufsImplementacao e desempenho da virtualizacao no dcomp ufs
Implementacao e desempenho da virtualizacao no dcomp ufsEdward David Moreno
 
Palestra - Bem vindo a era pós-digital: Empreendendo em um ambiente mutante.
Palestra - Bem vindo a era pós-digital: Empreendendo em um ambiente mutante.Palestra - Bem vindo a era pós-digital: Empreendendo em um ambiente mutante.
Palestra - Bem vindo a era pós-digital: Empreendendo em um ambiente mutante.Cássio Nunes
 

Destaque (20)

ORM dont kill your DB, developers do
ORM dont kill your DB, developers doORM dont kill your DB, developers do
ORM dont kill your DB, developers do
 
Doctrine ORM & model
Doctrine ORM & modelDoctrine ORM & model
Doctrine ORM & model
 
Effective Doctrine2: Performance Tips for Symfony2 Developers
Effective Doctrine2: Performance Tips for Symfony2 DevelopersEffective Doctrine2: Performance Tips for Symfony2 Developers
Effective Doctrine2: Performance Tips for Symfony2 Developers
 
Web 2.0 Collaboration – Using digital tools for redesigning governance
Web 2.0 Collaboration – Using digital tools for redesigning governanceWeb 2.0 Collaboration – Using digital tools for redesigning governance
Web 2.0 Collaboration – Using digital tools for redesigning governance
 
Projeto software alem da tecnologia v2
Projeto   software alem da tecnologia v2Projeto   software alem da tecnologia v2
Projeto software alem da tecnologia v2
 
História do Escritório Virtual de Aracaju
História do Escritório Virtual de AracajuHistória do Escritório Virtual de Aracaju
História do Escritório Virtual de Aracaju
 
Ecossistemas de startups nordestinos os desafios para a competitividade (2)
Ecossistemas de startups nordestinos  os desafios para a competitividade (2)Ecossistemas de startups nordestinos  os desafios para a competitividade (2)
Ecossistemas de startups nordestinos os desafios para a competitividade (2)
 
Análise dos sites dos presidenciáveis - Eleições 2014
Análise dos sites dos presidenciáveis - Eleições 2014Análise dos sites dos presidenciáveis - Eleições 2014
Análise dos sites dos presidenciáveis - Eleições 2014
 
Aula06 matriz em C
Aula06 matriz em CAula06 matriz em C
Aula06 matriz em C
 
Apresentação ForkInSergipe
Apresentação ForkInSergipeApresentação ForkInSergipe
Apresentação ForkInSergipe
 
IBECC - Contratos Empresariais - Revisão e Controle
IBECC - Contratos Empresariais - Revisão e ControleIBECC - Contratos Empresariais - Revisão e Controle
IBECC - Contratos Empresariais - Revisão e Controle
 
Desafios da Cocriação
Desafios da CocriaçãoDesafios da Cocriação
Desafios da Cocriação
 
Network Learning: AI-driven Connectivist Framework for E-Learning 3.0
Network Learning: AI-driven Connectivist Framework for E-Learning 3.0Network Learning: AI-driven Connectivist Framework for E-Learning 3.0
Network Learning: AI-driven Connectivist Framework for E-Learning 3.0
 
Ruby on rails - CEFET de Lagarto
Ruby on rails - CEFET de LagartoRuby on rails - CEFET de Lagarto
Ruby on rails - CEFET de Lagarto
 
ThingTank @ MIT-Skoltech Innovation Symposium 2014
ThingTank @ MIT-Skoltech Innovation Symposium 2014ThingTank @ MIT-Skoltech Innovation Symposium 2014
ThingTank @ MIT-Skoltech Innovation Symposium 2014
 
Social Web Studies - What kind of collaboration is right for your business
Social Web Studies - What kind of collaboration is right for your businessSocial Web Studies - What kind of collaboration is right for your business
Social Web Studies - What kind of collaboration is right for your business
 
Seminario - Versão Final
Seminario - Versão FinalSeminario - Versão Final
Seminario - Versão Final
 
Implementacao e desempenho da virtualizacao no dcomp ufs
Implementacao e desempenho da virtualizacao no dcomp ufsImplementacao e desempenho da virtualizacao no dcomp ufs
Implementacao e desempenho da virtualizacao no dcomp ufs
 
Palestra - Bem vindo a era pós-digital: Empreendendo em um ambiente mutante.
Palestra - Bem vindo a era pós-digital: Empreendendo em um ambiente mutante.Palestra - Bem vindo a era pós-digital: Empreendendo em um ambiente mutante.
Palestra - Bem vindo a era pós-digital: Empreendendo em um ambiente mutante.
 
Plano do Projeto
Plano do ProjetoPlano do Projeto
Plano do Projeto
 

Semelhante a Doctrine In The Real World sflive2011 Paris

Doctrine in the Real World
Doctrine in the Real WorldDoctrine in the Real World
Doctrine in the Real WorldJonathan Wage
 
Node js techtalksto
Node js techtalkstoNode js techtalksto
Node js techtalkstoJason Diller
 
The Easy Way - Plone Conference 2011
The Easy Way - Plone Conference 2011The Easy Way - Plone Conference 2011
The Easy Way - Plone Conference 2011Mikko Ohtamaa
 
Using+javascript+to+build+native+i os+applications
Using+javascript+to+build+native+i os+applicationsUsing+javascript+to+build+native+i os+applications
Using+javascript+to+build+native+i os+applicationsMuhammad Ikram Ul Haq
 
Data Driven Innovation
Data Driven InnovationData Driven Innovation
Data Driven Innovationideas.org
 
Data Driven Innovation
Data Driven InnovationData Driven Innovation
Data Driven InnovationSimon Grice
 
Education 2.3 m erwin
Education 2.3 m erwinEducation 2.3 m erwin
Education 2.3 m erwinErwin Huang
 
Railsify your web development
Railsify your web developmentRailsify your web development
Railsify your web developmentThomas Lundström
 
Intridea & open source
Intridea & open sourceIntridea & open source
Intridea & open sourceDaniel Lv
 
Writing jQuery that doesn't suck - London jQuery
Writing jQuery that doesn't suck - London jQueryWriting jQuery that doesn't suck - London jQuery
Writing jQuery that doesn't suck - London jQueryRoss Bruniges
 
Sean coates fifty things and tricks, confoo 2011
Sean coates fifty things and tricks, confoo 2011Sean coates fifty things and tricks, confoo 2011
Sean coates fifty things and tricks, confoo 2011Bachkoutou Toutou
 
Building OBO Foundry ontology using semantic web tools
Building OBO Foundry ontology using semantic web toolsBuilding OBO Foundry ontology using semantic web tools
Building OBO Foundry ontology using semantic web toolsMelanie Courtot
 
Pluggable Django Application Patterns PyCon 2011
Pluggable Django Application Patterns PyCon 2011Pluggable Django Application Patterns PyCon 2011
Pluggable Django Application Patterns PyCon 2011Corey Oordt
 
Doctrator Symfony Live 2011 Paris
Doctrator Symfony Live 2011 ParisDoctrator Symfony Live 2011 Paris
Doctrator Symfony Live 2011 Parispablodip
 
让开发也懂前端
让开发也懂前端让开发也懂前端
让开发也懂前端lifesinger
 
Censorship Detection Techniques
Censorship Detection TechniquesCensorship Detection Techniques
Censorship Detection TechniquesArturo Filastò
 
Sdforum 11-04-2010
Sdforum 11-04-2010Sdforum 11-04-2010
Sdforum 11-04-2010Ted Dunning
 

Semelhante a Doctrine In The Real World sflive2011 Paris (20)

Doctrine in the Real World
Doctrine in the Real WorldDoctrine in the Real World
Doctrine in the Real World
 
Node js techtalksto
Node js techtalkstoNode js techtalksto
Node js techtalksto
 
The Easy Way - Plone Conference 2011
The Easy Way - Plone Conference 2011The Easy Way - Plone Conference 2011
The Easy Way - Plone Conference 2011
 
Open source jura CBS (03 11-2010)
Open source jura CBS (03 11-2010)Open source jura CBS (03 11-2010)
Open source jura CBS (03 11-2010)
 
Using+javascript+to+build+native+i os+applications
Using+javascript+to+build+native+i os+applicationsUsing+javascript+to+build+native+i os+applications
Using+javascript+to+build+native+i os+applications
 
Data Driven Innovation
Data Driven InnovationData Driven Innovation
Data Driven Innovation
 
Data Driven Innovation
Data Driven InnovationData Driven Innovation
Data Driven Innovation
 
Education 2.3 m erwin
Education 2.3 m erwinEducation 2.3 m erwin
Education 2.3 m erwin
 
Railsify your web development
Railsify your web developmentRailsify your web development
Railsify your web development
 
Intridea & open source
Intridea & open sourceIntridea & open source
Intridea & open source
 
Writing jQuery that doesn't suck - London jQuery
Writing jQuery that doesn't suck - London jQueryWriting jQuery that doesn't suck - London jQuery
Writing jQuery that doesn't suck - London jQuery
 
Sean coates fifty things and tricks, confoo 2011
Sean coates fifty things and tricks, confoo 2011Sean coates fifty things and tricks, confoo 2011
Sean coates fifty things and tricks, confoo 2011
 
Building OBO Foundry ontology using semantic web tools
Building OBO Foundry ontology using semantic web toolsBuilding OBO Foundry ontology using semantic web tools
Building OBO Foundry ontology using semantic web tools
 
Tor2web ESC2011
Tor2web ESC2011Tor2web ESC2011
Tor2web ESC2011
 
Pluggable Django Application Patterns PyCon 2011
Pluggable Django Application Patterns PyCon 2011Pluggable Django Application Patterns PyCon 2011
Pluggable Django Application Patterns PyCon 2011
 
Doctrator Symfony Live 2011 Paris
Doctrator Symfony Live 2011 ParisDoctrator Symfony Live 2011 Paris
Doctrator Symfony Live 2011 Paris
 
让开发也懂前端
让开发也懂前端让开发也懂前端
让开发也懂前端
 
Censorship Detection Techniques
Censorship Detection TechniquesCensorship Detection Techniques
Censorship Detection Techniques
 
SD Forum 11 04-2010
SD Forum 11 04-2010SD Forum 11 04-2010
SD Forum 11 04-2010
 
Sdforum 11-04-2010
Sdforum 11-04-2010Sdforum 11-04-2010
Sdforum 11-04-2010
 

Mais de Jonathan Wage

Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
OpenSky Infrastructure
OpenSky InfrastructureOpenSky Infrastructure
OpenSky InfrastructureJonathan Wage
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the TrenchesJonathan Wage
 
ZendCon2010 Doctrine MongoDB ODM
ZendCon2010 Doctrine MongoDB ODMZendCon2010 Doctrine MongoDB ODM
ZendCon2010 Doctrine MongoDB ODMJonathan Wage
 
ZendCon2010 The Doctrine Project
ZendCon2010 The Doctrine ProjectZendCon2010 The Doctrine Project
ZendCon2010 The Doctrine ProjectJonathan Wage
 
Symfony Day 2010 Doctrine MongoDB ODM
Symfony Day 2010 Doctrine MongoDB ODMSymfony Day 2010 Doctrine MongoDB ODM
Symfony Day 2010 Doctrine MongoDB ODMJonathan Wage
 
Doctrine MongoDB Object Document Mapper
Doctrine MongoDB Object Document MapperDoctrine MongoDB Object Document Mapper
Doctrine MongoDB Object Document MapperJonathan Wage
 
Doctrine 2 - Enterprise Persistence Layer For PHP
Doctrine 2 - Enterprise Persistence Layer For PHPDoctrine 2 - Enterprise Persistence Layer For PHP
Doctrine 2 - Enterprise Persistence Layer For PHPJonathan Wage
 
Introduction To Doctrine 2
Introduction To Doctrine 2Introduction To Doctrine 2
Introduction To Doctrine 2Jonathan Wage
 
Doctrine 2 - Not The Same Old Php Orm
Doctrine 2 - Not The Same Old Php OrmDoctrine 2 - Not The Same Old Php Orm
Doctrine 2 - Not The Same Old Php OrmJonathan Wage
 
Doctrine 2: Enterprise Persistence Layer for PHP
Doctrine 2: Enterprise Persistence Layer for PHPDoctrine 2: Enterprise Persistence Layer for PHP
Doctrine 2: Enterprise Persistence Layer for PHPJonathan Wage
 
Sympal A Cmf Based On Symfony
Sympal   A Cmf Based On SymfonySympal   A Cmf Based On Symfony
Sympal A Cmf Based On SymfonyJonathan Wage
 
Symfony 1.3 + Doctrine 1.2
Symfony 1.3 + Doctrine 1.2Symfony 1.3 + Doctrine 1.2
Symfony 1.3 + Doctrine 1.2Jonathan Wage
 
Sympal - The flexible Symfony CMS
Sympal - The flexible Symfony CMSSympal - The flexible Symfony CMS
Sympal - The flexible Symfony CMSJonathan Wage
 
What's new in Doctrine
What's new in DoctrineWhat's new in Doctrine
What's new in DoctrineJonathan Wage
 
Sympal - Symfony CMS Preview
Sympal - Symfony CMS PreviewSympal - Symfony CMS Preview
Sympal - Symfony CMS PreviewJonathan Wage
 
Doctrine Php Object Relational Mapper
Doctrine Php Object Relational MapperDoctrine Php Object Relational Mapper
Doctrine Php Object Relational MapperJonathan Wage
 
Sympal - The Flexible Symfony Cms
Sympal - The Flexible Symfony CmsSympal - The Flexible Symfony Cms
Sympal - The Flexible Symfony CmsJonathan Wage
 

Mais de Jonathan Wage (20)

Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
OpenSky Infrastructure
OpenSky InfrastructureOpenSky Infrastructure
OpenSky Infrastructure
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the Trenches
 
ZendCon2010 Doctrine MongoDB ODM
ZendCon2010 Doctrine MongoDB ODMZendCon2010 Doctrine MongoDB ODM
ZendCon2010 Doctrine MongoDB ODM
 
ZendCon2010 The Doctrine Project
ZendCon2010 The Doctrine ProjectZendCon2010 The Doctrine Project
ZendCon2010 The Doctrine Project
 
Symfony Day 2010 Doctrine MongoDB ODM
Symfony Day 2010 Doctrine MongoDB ODMSymfony Day 2010 Doctrine MongoDB ODM
Symfony Day 2010 Doctrine MongoDB ODM
 
Doctrine MongoDB Object Document Mapper
Doctrine MongoDB Object Document MapperDoctrine MongoDB Object Document Mapper
Doctrine MongoDB Object Document Mapper
 
Libertyvasion2010
Libertyvasion2010Libertyvasion2010
Libertyvasion2010
 
Doctrine 2 - Enterprise Persistence Layer For PHP
Doctrine 2 - Enterprise Persistence Layer For PHPDoctrine 2 - Enterprise Persistence Layer For PHP
Doctrine 2 - Enterprise Persistence Layer For PHP
 
Introduction To Doctrine 2
Introduction To Doctrine 2Introduction To Doctrine 2
Introduction To Doctrine 2
 
Doctrine 2 - Not The Same Old Php Orm
Doctrine 2 - Not The Same Old Php OrmDoctrine 2 - Not The Same Old Php Orm
Doctrine 2 - Not The Same Old Php Orm
 
Doctrine 2: Enterprise Persistence Layer for PHP
Doctrine 2: Enterprise Persistence Layer for PHPDoctrine 2: Enterprise Persistence Layer for PHP
Doctrine 2: Enterprise Persistence Layer for PHP
 
Sympal A Cmf Based On Symfony
Sympal   A Cmf Based On SymfonySympal   A Cmf Based On Symfony
Sympal A Cmf Based On Symfony
 
Symfony 1.3 + Doctrine 1.2
Symfony 1.3 + Doctrine 1.2Symfony 1.3 + Doctrine 1.2
Symfony 1.3 + Doctrine 1.2
 
Sympal - The flexible Symfony CMS
Sympal - The flexible Symfony CMSSympal - The flexible Symfony CMS
Sympal - The flexible Symfony CMS
 
What's new in Doctrine
What's new in DoctrineWhat's new in Doctrine
What's new in Doctrine
 
What Is Doctrine?
What Is Doctrine?What Is Doctrine?
What Is Doctrine?
 
Sympal - Symfony CMS Preview
Sympal - Symfony CMS PreviewSympal - Symfony CMS Preview
Sympal - Symfony CMS Preview
 
Doctrine Php Object Relational Mapper
Doctrine Php Object Relational MapperDoctrine Php Object Relational Mapper
Doctrine Php Object Relational Mapper
 
Sympal - The Flexible Symfony Cms
Sympal - The Flexible Symfony CmsSympal - The Flexible Symfony Cms
Sympal - The Flexible Symfony Cms
 

Último

A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 

Último (20)

A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 

Doctrine In The Real World sflive2011 Paris

  • 1. doctrine Doctrine in the Real World Real world examples Friday, March 4, 2011
  • 2. My name is Jonathan H. Wage Friday, March 4, 2011
  • 3. • PHP Developer for 10+ years • Long time Symfony and Doctrine contributor • Published Author • Entrepreneur • Currently living in Nashville, Tennessee Friday, March 4, 2011
  • 4. Previously employed by SensioLabs Friday, March 4, 2011
  • 5. http://mongodbhosting.com Partnered with ServerGrove MongoDB Hosting Friday, March 4, 2011
  • 6. Today, I work full-time for OpenSky http://shopopensky.com Friday, March 4, 2011
  • 7. What is OpenSky? Friday, March 4, 2011
  • 8. A new way to shop • OpenSky connects you with innovators, trendsetters and tastemakers.You choose the ones you like and each week they invite you to their private online sales. Friday, March 4, 2011
  • 9. OpenSky Loves OpenSource • PHP 5.3 • Apache2 • Symfony2 • Doctrine2 • jQuery • mule, stomp, hornetq • MongoDB • nginx • varnish Friday, March 4, 2011
  • 10. We don’t just use open source projects Friday, March 4, 2011
  • 11. We help build them Friday, March 4, 2011
  • 12. OpenSky has some of the top committers in Symfony2 and other projects Friday, March 4, 2011
  • 13. Symfony2 OpenSky Committers • 65 Kris Wallsmith • 52 Jonathan H. Wage • 36 Jeremy Mikola • 36 Bulat Shakirzyanov •6 Justin Hileman Friday, March 4, 2011
  • 14. Doctrine MongoDB Committers • 39 Jonathan H. Wage • 11 Bulat Shakirzyanov • 2 Kris Wallsmith Friday, March 4, 2011
  • 15. MongoDB ODM Committers • 349 Jonathan H. Wage • 226 Bulat Shakirzyanov • 17 Kris Wallsmith • 13 Steven Surowiec • 2 Jeremy Mikola Friday, March 4, 2011
  • 16. OpenSky uses both the Doctrine ORM and ODM Friday, March 4, 2011
  • 18. We are an eCommerce site Friday, March 4, 2011
  • 19. Actions involving commerce need transactions Friday, March 4, 2011
  • 20. ORM and MySQL • Order • OrderTransaction • OrderShipment Friday, March 4, 2011
  • 21. ODM and MongoDB • Product • Seller • Supplier • User • ... basically everything else that is not involving $$$ and transactions Friday, March 4, 2011
  • 22. Blending the Two Friday, March 4, 2011
  • 23. Defining our Product Document Friday, March 4, 2011
  • 24. /** @mongodb:Document(collection="products") */ class Product { /** @mongodb:Id */ private $id; /** @mongodb:String */ private $title; public function getId() { return $this->id; } public function getTitle() { return $this->title; } public function setTitle($title) { $this->title = $title; } } Friday, March 4, 2011
  • 25. Defining our Order Entity Friday, March 4, 2011
  • 26. /** * @orm:Entity * @orm:Table(name="orders") * @orm:HasLifecycleCallbacks */ class Order { /** * @orm:Id @orm:Column(type="integer") * @orm:GeneratedValue(strategy="AUTO") */ private $id; /** * @orm:Column(type="string") */ private $productId; /** * @var DocumentsProduct */ private $product; // ... } Friday, March 4, 2011
  • 27. Setting the Product public function setProduct(Product $product) { $this->productId = $product->getId(); $this->product = $product; } Friday, March 4, 2011
  • 28. • $productId is mapped and persisted • but $product which stores the Product instance is not a persistent entity property Friday, March 4, 2011
  • 29. Order has a reference to product? • How? • Order is an ORM entity stored in MySQL • and Product is an ODM document stored in MongoDB Friday, March 4, 2011
  • 30. Loading Product ODM reference in Order Entity Friday, March 4, 2011
  • 31. Lifecycle Events to the Rescue Friday, March 4, 2011
  • 32. EventManager • Event system is controlled by the EventManager • Central point of event listener system • Listeners are registered on the manager • Events are dispatched through the manager Friday, March 4, 2011
  • 33. Add EventListener $eventListener = new OrderPostLoadListener($dm); $eventManager = $em->getEventManager(); $eventManager->addEventListener( array(DoctrineORMEvents::postLoad), $eventListener ); Friday, March 4, 2011
  • 34. In Symfony2 DI <?xml version="1.0" encoding="utf-8" ?> <container xmlns="http://www.symfony-project.org/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/ schema/dic/services/services-1.0.xsd"> <parameters> <parameter key="order.post_load.listener.class">OrderPostLoadListener</parameter> </parameters> <services> <service id="order.post_load.listener" class="%order.post_load.listener.class%" scope="container"> <argument type="service" id="doctrine.odm.mongodb.default_document_manager" /> <tag name="doctrine.orm.default_event_listener" event="postLoad" /> </service> </services> </container> Friday, March 4, 2011
  • 35. OrderPostLoadListener use DoctrineODMMongoDBDocumentManager; use DoctrineORMEventLifecycleEventArgs; class OrderPostLoadListener { public function __construct(DocumentManager $dm) { $this->dm = $dm; } public function postLoad(LifecycleEventArgs $eventArgs) { // get the order entity $order = $eventArgs->getEntity(); // get odm reference to order.product_id $productId = $order->getProductId(); $product = $this->dm->getReference('MyBundle:DocumentProduct', $productId); // set the product on the order $em = $eventArgs->getEntityManager(); $productReflProp = $em->getClassMetadata('MyBundle:EntityOrder') ->reflClass->getProperty('product'); $productReflProp->setAccessible(true); $productReflProp->setValue($order, $product); } } Friday, March 4, 2011
  • 36. All Together Now // Create a new product and order $product = new Product(); $product->setTitle('Test Product'); $dm->persist($product); $dm->flush(); $order = new Order(); $order->setProduct($product); $em->persist($order); $em->flush(); // Find the order later $order = $em->find('Order', $order->getId()); // Instance of an uninitialized product proxy $product = $order->getProduct(); // Initializes proxy and queries the monogodb database echo "Order Title: " . $product->getTitle(); print_r($order); Friday, March 4, 2011
  • 37. Seamless • Documents and Entities play together like best friends • Because Doctrine persistence remains transparent from your domain this is possible Friday, March 4, 2011
  • 38. print_r($order) Order Object ( [id:EntitiesOrder:private] => 53 [productId:EntitiesOrder:private] => 4c74a1868ead0ed7a9000000 [product:EntitiesOrder:private] => ProxiesDocumentProductProxy Object ( [__isInitialized__] => 1 [id:DocumentsProduct:private] => 4c74a1868ead0ed7a9000000 [title:DocumentsProduct:private] => Test Product ) ) Friday, March 4, 2011
  • 39. Example from Blog • This example was first written on my personal blog http://jwage.com • You can read the blog post here http:// jwage.com/2010/08/25/blending-the- doctrine-orm-and-mongodb-odm/ Friday, March 4, 2011
  • 40. MongoDB ODM SoftDelete Functionality Friday, March 4, 2011
  • 41. I like my deletes soft, not hard Friday, March 4, 2011
  • 43. Deleting data is dangerous business Friday, March 4, 2011
  • 44. Flickr accidentally deleted a pro members account and 5000 pictures Friday, March 4, 2011
  • 45. They were able to restore it later but it took some time Friday, March 4, 2011
  • 46. Instead of deleting, simply set a deletedAt field Friday, March 4, 2011
  • 47. Install SoftDelete Extension for Doctrine MongoDB ODM http://github.com/doctrine/mongodb-odm-softdelete $ git clone git://github.com/doctrine/mongodb-odm-softdelete src/ vendor/doctrine-mongodb-odm-softdelete Friday, March 4, 2011
  • 48. Autoload Extension $loader = new UniversalClassLoader(); $loader->registerNamespaces(array( // ... 'DoctrineODMMongoDBSoftDelete' => __DIR__.'/vendor/doctrine-mongodb-odm- softdelete/lib', )); $loader->register(); Friday, March 4, 2011
  • 49. Raw PHP Configuration use DoctrineODMMongoDBSoftDeleteUnitOfWork; use DoctrineODMMongoDBSoftDeleteSoftDeleteManager; use DoctrineCommonEventManager; // $dm is a DocumentManager instance we should already have use DoctrineODMMongoDBSoftDeleteConfiguration; $config = new Configuration(); $uow = new UnitOfWork($dm, $config); $evm = new EventManager(); $sdm = new SoftDeleteManager($dm, $config, $uow, $evm); Friday, March 4, 2011
  • 50. Symfony2 Integration http://github.com/doctrine/mongodb-odm-softdelete-bundle $ git clone git://github.com/doctrine/mongodb-odm-softdelete-bundle.git src/vendor/doctrine-mongodb-odm-softdelete-bundle Friday, March 4, 2011
  • 51. Autoload the Bundle $loader = new UniversalClassLoader(); $loader->registerNamespaces(array( // ... 'DoctrineODMMongoDBSymfonySoftDeleteBundle' => __DIR__.'/vendor/doctrine- mongodb-odm-softdelete-bundle', )); $loader->register(); Friday, March 4, 2011
  • 52. Register the Bundle public function registerBundles() { $bundles = array( // ... // register doctrine symfony bundles new DoctrineODMMongoDBSymfonySoftDeleteBundleSoftDeleteBundle() ); // ... return $bundles; } Friday, March 4, 2011
  • 53. Enable the Bundle // app/config/config.yml doctrine_mongodb_softdelete.config: ~ Friday, March 4, 2011
  • 54. SoftDeleteManager $sdm = $container->get('doctrine.odm.mongodb.soft_delete.manager'); Friday, March 4, 2011
  • 55. SoftDeleteable ODM Documents must implement this interface interface SoftDeleteable { function getDeletedAt(); } Friday, March 4, 2011
  • 56. User implements SoftDeletable /** @mongodb:Document */ class User implements SoftDeleteable { /** @mongodb:Date @mongodb:Index */ private $deletedAt; public function getDeletedAt() { return $this->deletedAt; } } Friday, March 4, 2011
  • 57. SoftDelete a User $user = new User('jwage'); // ... $dm->persist($user); $dm->flush(); // later we can soft delete the user jwage $user = $dm->getRepository('User')->findOneByUsername('jwage'); $sdm->delete($user); $sdm->flush(); Friday, March 4, 2011
  • 58. Query Executed db.users.update( { _id : { $in : [new ObjectId('1234567891011123456')] } }, { $set : { deletedAt: new Date() } } ) Friday, March 4, 2011
  • 59. Restore a User // now again later we can restore that same user $user = $dm->getRepository('User')->findOneByUsername('jwage'); $sdm->restore($user); $sdm->flush(); Friday, March 4, 2011
  • 60. Query Executed db.users.update( { _id : { $in : [new ObjectId('1234567891011123456')] } }, { $unset : { deletedAt: true } } ) Friday, March 4, 2011
  • 61. Limit cursors to only show non deleted users $qb = $dm->createQueryBuilder('User') ->field('deletedAt')->exists(false); $query = $qb->getQuery(); $users = $query->execute(); Friday, March 4, 2011
  • 62. Get only deleted users $qb = $dm->createQueryBuilder('User') ->field('deletedAt')->exists(true); $query = $qb->getQuery(); $users = $query->execute(); Friday, March 4, 2011
  • 63. Restore several deleted users $qb = $dm->createQueryBuilder('User') ->field('deletedAt')->exists(true) ->field('createdAt')->gt(new DateTime('-24 hours')); $query = $qb->getQuery(); $users = $query->execute(); foreach ($users as $user) { $sdm->restore($user); } $sdm->flush(); Friday, March 4, 2011
  • 64. Soft Delete Events class TestEventSubscriber implements DoctrineCommonEventSubscriber { public function preSoftDelete(LifecycleEventArgs $args) { $document = $args->getDocument(); - preDelete } $sdm = $args->getSoftDeleteManager(); - postDelete public function getSubscribedEvents() { - preRestore } return array(Events::preSoftDelete); - postRestore } $eventSubscriber = new TestEventSubscriber(); $evm->addEventSubscriber($eventSubscriber); Friday, March 4, 2011
  • 66. Symfony2 and supervisor http://supervisord.org/ Friday, March 4, 2011
  • 68. Supervisor is a client/server system that allows its users to monitor and control a number of processes on UNIX-like operating systems. http://supervisord.org Friday, March 4, 2011
  • 69. Daemonize a Symfony2 Console Command with supervisor Friday, March 4, 2011
  • 70. Scenario • You want to send an e-mail when new users register in your system. • But, sending an e-mail directly from your action introduces a failure point to your stack. • ....What do you do? Friday, March 4, 2011
  • 71. Tailable Cursor • Use a tailable mongodb cursor • Tail a NewUser document collection • Insert NewUser documents from your actions • The daemon will instantly process the NewUser after it is inserted and dispatch the e-mail Friday, March 4, 2011
  • 72. Define NewUser namespace MyCompanyBundleMyBundleDocument; /** * @mongodb:Document(collection={ * "name"="new_users", * "capped"="true", * "size"="100000", * "max"="1000" * }, repositoryClass="MyCompanyBundleMyBundleDocumentNewUserRepository") */ class NewUser { /** @mongodb:Id */ private $id; /** @mongodb:ReferenceOne(targetDocument="User") */ private $user; /** @mongodb:Boolean @mongodb:Index */ private $isProcessed = false; // ... } Friday, March 4, 2011
  • 73. Create Collection • The NewUser collection must be capped in order to tail it so we need to create it. • Luckily, Doctrine has a console command for it. • It will read the mapping information we configured and create the collection $ php app/console doctrine:mongodb:schema:create --class="MyBundle:NewUser" --collection Friday, March 4, 2011
  • 74. Insert NewUser upon Registration public function register() { // ... $user = new User(); $form = new RegisterForm('register', $user, $validator); $form->bind($request, $user); if ($form->isValid()) { $newUser = new NewUser($user); $dm->persist($newUser); $dm->persist($user); $dm->flush(); // ... } // ... } Friday, March 4, 2011
  • 75. Executing Console Command $ php app/console doctrine:mongodb:tail-cursor MyBundle:NewUser findUnProcessed new_user.processor • The command requires 3 arguments: • document - the name of the document to tail • finder - the repository finder method used to get the cursor • processor - the id of the service used to process the new users Friday, March 4, 2011
  • 76. findUnProcessed() • We need the findUnProcessed() method to class NewUserRepository extends DocumentRepository { return the unprocessed cursor to tail public function findUnProcessed() { return $this->createQueryBuilder() ->field('isProcessed')->equals(false) ->getQuery() ->execute(); } } Friday, March 4, 2011
  • 77. NewUserProcessor We need a service id new_user.processor with a process(OutputInterface $output, $document) method use Swift_Message; use SymfonyComponentConsoleOutputOutputInterface; class NewUserProcessor { private $mailer; public function __construct($mailer) { $this->mailer = $mailer; } public function process(OutputInterface $output, $document) { } } Friday, March 4, 2011
  • 78. Send the e-mail public function process(OutputInterface $output, $document) { $user = $document->getUser(); $message = Swift_Message::newInstance() ->setSubject('New Registration') ->setFrom('noreply@domain.com') ->setTo($user->getEmail()) ->setBody('New user registration') ; $this->mailer->send($message); $document->setIsProcessed(true); } Friday, March 4, 2011
  • 79. Tailable Cursor Bundle https://github.com/doctrine/doctrine-mongodb-odm- tailable-cursor-bundle Friday, March 4, 2011
  • 80. Daemonization • Now, how do we really daemonize the console command and keep it running 24 hours a day, 7 days a week? • The answer is supervisor, it will allow us to configure a console command for it to manage the process id of and always keep an instance of it running. Friday, March 4, 2011
  • 81. Install supervisor http://supervisord.org/installing.html $ easy_install supervisor Friday, March 4, 2011
  • 82. Configure a Profile • We need to configure a profile for supervisor to know how to run the console command / $ vi /etc/supervisor/conf.d/tail-new-user.conf r [program:tail-new-user] numprocs=1 startretries=100 directory=/ stdout_logfile=/path/to/symfonyproject/app/logs/tail-new-user-supervisord.log autostart=true autorestart=true user=root command=/usr/local/bin/php /path/to/symfonyproject/app/console doctrine:mongodb:tail-cursor MyBundle:NewUser findUnprocessed new_user.processor Friday, March 4, 2011
  • 83. Start supervisord • Start an instance of supervisord • It will run as a daemon in the background • The tail-new-user.conf will always be running $ supervisord Friday, March 4, 2011
  • 84. Where do I use supervisor? • http://sociallynotable.com • Keeps daemon running that watches twitter • Indexes tweets with links to amazon products • Maintains tweet statistics and ranks the popular products Friday, March 4, 2011
  • 85. Thanks! I hope this presentation was useful to you! Friday, March 4, 2011
  • 86. Questions? - http://jwage.com - http://twitter.com/jwage - http://facebook.com/jwage - http://about.me/jwage - http://shopopensky.com - http://mongodbhosting.com - http://servergrove.com - http://sociallynotable.com Friday, March 4, 2011