SlideShare a Scribd company logo
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

More Related Content

PDF
Building Brilliant APIs
PDF
Social Network Technologies and Usages
PDF
let's talk web standards
PDF
Web 2.0 Collaboration – Using digital tools for redesigning governance
PDF
Understanding Doctrine at True North PHP 2013
PDF
Symfony2. Database and Doctrine
PDF
Symfony2 and Doctrine2 Integration
PDF
ORO Meetups - Doctrine Events
Building Brilliant APIs
Social Network Technologies and Usages
let's talk web standards
Web 2.0 Collaboration – Using digital tools for redesigning governance
Understanding Doctrine at True North PHP 2013
Symfony2. Database and Doctrine
Symfony2 and Doctrine2 Integration
ORO Meetups - Doctrine Events

Viewers also liked (20)

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

Similar to Doctrine In The Real World sflive2011 Paris (20)

PDF
Doctrine in the Real World
PDF
The Solar Framework for PHP
PDF
Doctrator Symfony Live 2011 Paris
PDF
Symfony2 - from the trenches
PDF
Symfony2 from the Trenches
PDF
Symfony2 and MongoDB - MidwestPHP 2013
PDF
Fluent Development with FLOW3 1.0
PDF
Drizzle 7.0, Future of Virtualizing
PDF
MySQL & NoSQL from a PHP Perspective
PDF
Ruby on CouchDB - SimplyStored and RockingChair
PDF
MongoDB at Sailthru: Scaling and Schema Design
PDF
Symfony2 and MongoDB
PDF
PDF
No Really, It's All About You
PDF
Pluggable Django Application Patterns PyCon 2011
PDF
Symfony in the Cloud
PDF
OpenSky Infrastructure
KEY
MongoDB, PHP and the cloud - php cloud summit 2011
KEY
Mongo NYC PHP Development
PDF
Everyday - mongodb
Doctrine in the Real World
The Solar Framework for PHP
Doctrator Symfony Live 2011 Paris
Symfony2 - from the trenches
Symfony2 from the Trenches
Symfony2 and MongoDB - MidwestPHP 2013
Fluent Development with FLOW3 1.0
Drizzle 7.0, Future of Virtualizing
MySQL & NoSQL from a PHP Perspective
Ruby on CouchDB - SimplyStored and RockingChair
MongoDB at Sailthru: Scaling and Schema Design
Symfony2 and MongoDB
No Really, It's All About You
Pluggable Django Application Patterns PyCon 2011
Symfony in the Cloud
OpenSky Infrastructure
MongoDB, PHP and the cloud - php cloud summit 2011
Mongo NYC PHP Development
Everyday - mongodb
Ad

More from Jonathan Wage (19)

PDF
Doctrine For Beginners
PDF
ZendCon2010 Doctrine MongoDB ODM
PDF
ZendCon2010 The Doctrine Project
PDF
Symfony Day 2010 Doctrine MongoDB ODM
PDF
Doctrine MongoDB Object Document Mapper
PDF
Libertyvasion2010
PDF
Doctrine 2 - Enterprise Persistence Layer For PHP
PDF
Introduction To Doctrine 2
PDF
Doctrine 2 - Not The Same Old Php Orm
PDF
Doctrine 2: Enterprise Persistence Layer for PHP
PDF
Sympal A Cmf Based On Symfony
PDF
Symfony 1.3 + Doctrine 1.2
PDF
Sympal - The flexible Symfony CMS
PDF
What's new in Doctrine
PDF
What Is Doctrine?
PDF
Sympal - Symfony CMS Preview
PDF
Doctrine Php Object Relational Mapper
PDF
Sympal - The Flexible Symfony Cms
PDF
What's New In Doctrine
Doctrine For Beginners
ZendCon2010 Doctrine MongoDB ODM
ZendCon2010 The Doctrine Project
Symfony Day 2010 Doctrine MongoDB ODM
Doctrine MongoDB Object Document Mapper
Libertyvasion2010
Doctrine 2 - Enterprise Persistence Layer For PHP
Introduction To Doctrine 2
Doctrine 2 - Not The Same Old Php Orm
Doctrine 2: Enterprise Persistence Layer for PHP
Sympal A Cmf Based On Symfony
Symfony 1.3 + Doctrine 1.2
Sympal - The flexible Symfony CMS
What's new in Doctrine
What Is Doctrine?
Sympal - Symfony CMS Preview
Doctrine Php Object Relational Mapper
Sympal - The Flexible Symfony Cms
What's New In Doctrine

Recently uploaded (20)

PDF
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
PDF
Modernizing your data center with Dell and AMD
PPT
Teaching material agriculture food technology
PDF
Empathic Computing: Creating Shared Understanding
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PPTX
Cloud computing and distributed systems.
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PDF
Machine learning based COVID-19 study performance prediction
PDF
Network Security Unit 5.pdf for BCA BBA.
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Encapsulation theory and applications.pdf
PDF
CIFDAQ's Market Insight: SEC Turns Pro Crypto
PDF
Approach and Philosophy of On baking technology
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PPTX
A Presentation on Artificial Intelligence
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
Electronic commerce courselecture one. Pdf
DOCX
The AUB Centre for AI in Media Proposal.docx
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
Modernizing your data center with Dell and AMD
Teaching material agriculture food technology
Empathic Computing: Creating Shared Understanding
Digital-Transformation-Roadmap-for-Companies.pptx
Cloud computing and distributed systems.
Reach Out and Touch Someone: Haptics and Empathic Computing
Machine learning based COVID-19 study performance prediction
Network Security Unit 5.pdf for BCA BBA.
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Encapsulation theory and applications.pdf
CIFDAQ's Market Insight: SEC Turns Pro Crypto
Approach and Philosophy of On baking technology
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
A Presentation on Artificial Intelligence
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
Per capita expenditure prediction using model stacking based on satellite ima...
Electronic commerce courselecture one. Pdf
The AUB Centre for AI in Media Proposal.docx

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