SlideShare a Scribd company logo
1 of 93
Download to read offline
doctrine
                 Doctrine in the Real World
                            Real world examples




Tuesday, February 8, 2011
My name is
                            Jonathan H. Wage


Tuesday, February 8, 2011
• PHP Developer for 10+ years
                    • Long time Symfony and Doctrine
                            contributor

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

Tuesday, February 8, 2011
I work full-time for
                                 OpenSky
                               http://shopopensky.com



Tuesday, February 8, 2011
Previously employed by
                        SensioLabs


Tuesday, February 8, 2011
What is OpenSky?



Tuesday, February 8, 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.




Tuesday, February 8, 2011
We Love OpenSource
                    •       PHP 5.3

                    •       Apache2

                    •       Symfony2

                    •       Doctrine2

                    •       jQuery

                    •       mule, stomp, hornetq

                    •       MongoDB

                    •       nginx

                    •       varnish

Tuesday, February 8, 2011
We don’t just use open
                     source projects


Tuesday, February 8, 2011
We help build them



Tuesday, February 8, 2011
OpenSky has some of
                      the top committers in
                       Symfony2 and other
                             projects

Tuesday, February 8, 2011
Symfony2 OpenSky
                               Committers
                    • 65    Kris Wallsmith

                    • 52    Jonathan H. Wage

                    • 36    Jeremy Mikola

                    • 36    Bulat Shakirzyanov

                    •6       Justin Hileman




Tuesday, February 8, 2011
Doctrine MongoDB
                               Committers
                    •       39 Jonathan H. Wage

                    •       11 Bulat Shakirzyanov

                    •       2   Kris Wallsmith




Tuesday, February 8, 2011
MongoDB ODM
                                  Committers
                    •       349 Jonathan H. Wage

                    •       226 Bulat Shakirzyanov

                    •       17   Kris Wallsmith

                    •       13   Steven Surowiec

                    •       2    Jeremy Mikola




Tuesday, February 8, 2011
Sorry to bore you



Tuesday, February 8, 2011
Moving on to the stuff
                      you came here for


Tuesday, February 8, 2011
OpenSky uses Doctrine
                   ORM and ODM


Tuesday, February 8, 2011
Why?



Tuesday, February 8, 2011
We are an eCommerce
                          site


Tuesday, February 8, 2011
Actions involving
                            commerce need
                              transactions


Tuesday, February 8, 2011
ORM and MySQL

                    • Order
                    • OrderTransaction
                    • OrderShipment


Tuesday, February 8, 2011
ODM and MongoDB
                    • Product
                    • Seller
                    • Supplier
                    • User
                    • ... basically everything else that is not
                            involving $$$ and transactions


Tuesday, February 8, 2011
Blending the Two



Tuesday, February 8, 2011
Defining our Product
                            Document


Tuesday, February 8, 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;
                                }
                            }



Tuesday, February 8, 2011
Defining our Order
                                  Entity


Tuesday, February 8, 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;

                                // ...
                            }


Tuesday, February 8, 2011
Setting the Product

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




Tuesday, February 8, 2011
• $productId is mapped and persisted
                    • but $product which stores the Product
                            instance is not a persistent entity property




Tuesday, February 8, 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




Tuesday, February 8, 2011
Loading Product ODM
                     reference in Order
                            Entity


Tuesday, February 8, 2011
Lifecycle Events to the
                             Rescue


Tuesday, February 8, 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

Tuesday, February 8, 2011
Add EventListener

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




Tuesday, February 8, 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>




Tuesday, February 8, 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);
           }
    }




Tuesday, February 8, 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);




Tuesday, February 8, 2011
Seamless

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




Tuesday, February 8, 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
                )

        )




Tuesday, February 8, 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/



Tuesday, February 8, 2011
MongoDB ODM
                SoftDelete Functionality


Tuesday, February 8, 2011
I like my deletes soft,
                                   not hard


Tuesday, February 8, 2011
Why?



Tuesday, February 8, 2011
Deleting data is
                            dangerous business


Tuesday, February 8, 2011
Flickr accidentally
                  deleted a pro members
                     account and 4000
                          pictures

Tuesday, February 8, 2011
They were able to
                            restore it later but it
                              took some time


Tuesday, February 8, 2011
Instead of deleting, simply
               set a deletedAt field


Tuesday, February 8, 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




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




Tuesday, February 8, 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);




Tuesday, February 8, 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




Tuesday, February 8, 2011
Autoload the Bundle

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




Tuesday, February 8, 2011
Register the Bundle
                public function registerBundles()
                {
                    $bundles = array(
                        // ...

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

                       // ...

                       return $bundles;
                }




Tuesday, February 8, 2011
Enable the Bundle


                // app/config/config.yml

                doctrine_mongodb_softdelete.config: ~




Tuesday, February 8, 2011
SoftDeleteManager


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




Tuesday, February 8, 2011
SoftDeleteable
               ODM Documents must implement this interface

                              interface SoftDeleteable
                              {
                                  function getDeletedAt();
                                  function isDeleted();
                              }




Tuesday, February 8, 2011
User implements
                                  SoftDeletable
                            /** @mongodb:Document */
                            class User implements SoftDeleteable
                            {
                                /** @mongodb:Date @mongodb:Index */
                                private $deletedAt;

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

                                public function isDeleted()
                                {
                                    return $this->deletedAt !== null ? true : false;
                                }
                            }


Tuesday, February 8, 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();




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




Tuesday, February 8, 2011
Restore a User

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




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



Tuesday, February 8, 2011
Limit cursors to only
                show non deleted users

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




Tuesday, February 8, 2011
Get only deleted users

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




Tuesday, February 8, 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();




Tuesday, February 8, 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);




Tuesday, February 8, 2011
Symfony2 and
                             supervisor
                             http://supervisord.org/



Tuesday, February 8, 2011
What is supervisor?



Tuesday, February 8, 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


Tuesday, February 8, 2011
Daemonize a Symfony2
                   Console Command
                     with supervisor



Tuesday, February 8, 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?

Tuesday, February 8, 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


Tuesday, February 8, 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;

                    // ...
            }


Tuesday, February 8, 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



Tuesday, February 8, 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();

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



Tuesday, February 8, 2011
The Daemon Console
                          Command

                    • You can find the console command code to
                            use to tail a cursor here:
                    • https://gist.github.com/812942


Tuesday, February 8, 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


Tuesday, February 8, 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();
                       }
                   }




Tuesday, February 8, 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)
                            {
                            }
                   }

Tuesday, February 8, 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);
        }


Tuesday, February 8, 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.


Tuesday, February 8, 2011
Install supervisor
                            http://supervisord.org/installing.html



                            $ easy_install supervisor




Tuesday, February 8, 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
    Tuesday, February 8, 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




Tuesday, February 8, 2011
Where do I use
                                 supervisor?
                    • sociallynotable.com
                     • Indexes tweets with links to Amazon.com
                              products
                            • Maintains statistics on each product and
                              lets you shop the popular products each
                              day



Tuesday, February 8, 2011
sn:watch-twitter
                            console command


Tuesday, February 8, 2011
I can manually start it



Tuesday, February 8, 2011
But, what if it crashes
                   or stops unexpectedly?


Tuesday, February 8, 2011
This is exactly what
                             supervisor is for


Tuesday, February 8, 2011
Setup a configuration profile for supervisor
                  and it will ensure the console command is
                                 always running




Tuesday, February 8, 2011
[program:watch-twitter]
numprocs=1
startretries=100000000000
directory=/
stdout_logfile=/var/www/vhosts/sociallynotable.com/socially-notable/sociallynotable/logs/watch-twitter-supervisord.log
autostart=true
autorestart=true
user=root
command=/usr/local/bin/php /var/www/vhosts/sociallynotable.com/socially-notable/sociallynotable/console sn:watch-twitter




Tuesday, February 8, 2011
Now when I start supervisor the
                    twitter watcher will always
                 remain running. Even if I kill the
                  pid myself, it will start back up.



Tuesday, February 8, 2011
Thanks!
                    I hope this presentation was useful to you!




Tuesday, February 8, 2011
Questions?
                            - http://jwage.com
                            - http://shopopensky.com
                            - http://sociallynotable.com
                            - http://twitter.com/jwage
                            - http://facebook.com/jwage
                            - http://about.me/jwage




Tuesday, February 8, 2011

More Related Content

Similar to Doctrine in the Real World

Scaling atlassian os v4
Scaling atlassian os v4Scaling atlassian os v4
Scaling atlassian os v4Soren Harner
 
Monitoring is easy, why are we so bad at it presentation
Monitoring is easy, why are we so bad at it  presentationMonitoring is easy, why are we so bad at it  presentation
Monitoring is easy, why are we so bad at it presentationTheo Schlossnagle
 
Developing a Language
Developing a LanguageDeveloping a Language
Developing a LanguageEngine Yard
 
Node js techtalksto
Node js techtalkstoNode js techtalksto
Node js techtalkstoJason Diller
 
OmniAuth: From the Ground Up (RailsConf 2011)
OmniAuth: From the Ground Up (RailsConf 2011)OmniAuth: From the Ground Up (RailsConf 2011)
OmniAuth: From the Ground Up (RailsConf 2011)Michael Bleigh
 
Developing a Language
Developing a LanguageDeveloping a Language
Developing a Languageevanphx
 
2011 JavaOne Fun with EJB 3.1 and OpenEJB
2011 JavaOne Fun with EJB 3.1 and OpenEJB2011 JavaOne Fun with EJB 3.1 and OpenEJB
2011 JavaOne Fun with EJB 3.1 and OpenEJBDavid Blevins
 
iPhone Python love affair
iPhone Python love affairiPhone Python love affair
iPhone Python love affairAnna Callahan
 
Minegems
MinegemsMinegems
Minegemsjodosha
 
Ruby can haz Everyone
Ruby can haz EveryoneRuby can haz Everyone
Ruby can haz EveryoneRubyX
 
Performance Optimization for Ext GWT 3.0
Performance Optimization for Ext GWT 3.0Performance Optimization for Ext GWT 3.0
Performance Optimization for Ext GWT 3.0Sencha
 
Pluggable Django Application Patterns PyCon 2011
Pluggable Django Application Patterns PyCon 2011Pluggable Django Application Patterns PyCon 2011
Pluggable Django Application Patterns PyCon 2011Corey Oordt
 
Cassandra 1.0 and the future of big data (Cassandra Tokyo 2011)
Cassandra 1.0 and the future of big data (Cassandra Tokyo 2011)Cassandra 1.0 and the future of big data (Cassandra Tokyo 2011)
Cassandra 1.0 and the future of big data (Cassandra Tokyo 2011)jbellis
 
Web micro-framework BATTLE!
Web micro-framework BATTLE!Web micro-framework BATTLE!
Web micro-framework BATTLE!Richard Jones
 
How to Make Entities and Influence Drupal - Emerging Patterns from Drupal Con...
How to Make Entities and Influence Drupal - Emerging Patterns from Drupal Con...How to Make Entities and Influence Drupal - Emerging Patterns from Drupal Con...
How to Make Entities and Influence Drupal - Emerging Patterns from Drupal Con...Ronald Ashri
 
Atlassian RoadTrip 2011 Slide Deck
Atlassian RoadTrip 2011 Slide DeckAtlassian RoadTrip 2011 Slide Deck
Atlassian RoadTrip 2011 Slide DeckAtlassian
 
Intridea & open source
Intridea & open sourceIntridea & open source
Intridea & open sourceDaniel Lv
 
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
 
Fosdem chef-101-app-deploy
Fosdem chef-101-app-deployFosdem chef-101-app-deploy
Fosdem chef-101-app-deployjtimberman
 

Similar to Doctrine in the Real World (20)

Scaling atlassian os v4
Scaling atlassian os v4Scaling atlassian os v4
Scaling atlassian os v4
 
When?, Why? and What? of MongoDB
When?, Why? and What? of MongoDBWhen?, Why? and What? of MongoDB
When?, Why? and What? of MongoDB
 
Monitoring is easy, why are we so bad at it presentation
Monitoring is easy, why are we so bad at it  presentationMonitoring is easy, why are we so bad at it  presentation
Monitoring is easy, why are we so bad at it presentation
 
Developing a Language
Developing a LanguageDeveloping a Language
Developing a Language
 
Node js techtalksto
Node js techtalkstoNode js techtalksto
Node js techtalksto
 
OmniAuth: From the Ground Up (RailsConf 2011)
OmniAuth: From the Ground Up (RailsConf 2011)OmniAuth: From the Ground Up (RailsConf 2011)
OmniAuth: From the Ground Up (RailsConf 2011)
 
Developing a Language
Developing a LanguageDeveloping a Language
Developing a Language
 
2011 JavaOne Fun with EJB 3.1 and OpenEJB
2011 JavaOne Fun with EJB 3.1 and OpenEJB2011 JavaOne Fun with EJB 3.1 and OpenEJB
2011 JavaOne Fun with EJB 3.1 and OpenEJB
 
iPhone Python love affair
iPhone Python love affairiPhone Python love affair
iPhone Python love affair
 
Minegems
MinegemsMinegems
Minegems
 
Ruby can haz Everyone
Ruby can haz EveryoneRuby can haz Everyone
Ruby can haz Everyone
 
Performance Optimization for Ext GWT 3.0
Performance Optimization for Ext GWT 3.0Performance Optimization for Ext GWT 3.0
Performance Optimization for Ext GWT 3.0
 
Pluggable Django Application Patterns PyCon 2011
Pluggable Django Application Patterns PyCon 2011Pluggable Django Application Patterns PyCon 2011
Pluggable Django Application Patterns PyCon 2011
 
Cassandra 1.0 and the future of big data (Cassandra Tokyo 2011)
Cassandra 1.0 and the future of big data (Cassandra Tokyo 2011)Cassandra 1.0 and the future of big data (Cassandra Tokyo 2011)
Cassandra 1.0 and the future of big data (Cassandra Tokyo 2011)
 
Web micro-framework BATTLE!
Web micro-framework BATTLE!Web micro-framework BATTLE!
Web micro-framework BATTLE!
 
How to Make Entities and Influence Drupal - Emerging Patterns from Drupal Con...
How to Make Entities and Influence Drupal - Emerging Patterns from Drupal Con...How to Make Entities and Influence Drupal - Emerging Patterns from Drupal Con...
How to Make Entities and Influence Drupal - Emerging Patterns from Drupal Con...
 
Atlassian RoadTrip 2011 Slide Deck
Atlassian RoadTrip 2011 Slide DeckAtlassian RoadTrip 2011 Slide Deck
Atlassian RoadTrip 2011 Slide Deck
 
Intridea & open source
Intridea & open sourceIntridea & open source
Intridea & open source
 
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
 
Fosdem chef-101-app-deploy
Fosdem chef-101-app-deployFosdem chef-101-app-deploy
Fosdem chef-101-app-deploy
 

More from 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
 
Symfony2 and Doctrine2 Integration
Symfony2 and Doctrine2 IntegrationSymfony2 and Doctrine2 Integration
Symfony2 and Doctrine2 IntegrationJonathan 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
 

More from 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
 
Symfony2 and Doctrine2 Integration
Symfony2 and Doctrine2 IntegrationSymfony2 and Doctrine2 Integration
Symfony2 and Doctrine2 Integration
 
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
 

Doctrine in the Real World

  • 1. doctrine Doctrine in the Real World Real world examples Tuesday, February 8, 2011
  • 2. My name is Jonathan H. Wage Tuesday, February 8, 2011
  • 3. • PHP Developer for 10+ years • Long time Symfony and Doctrine contributor • Published Author • Entrepreneur • Currently living in Nashville, Tennessee Tuesday, February 8, 2011
  • 4. I work full-time for OpenSky http://shopopensky.com Tuesday, February 8, 2011
  • 5. Previously employed by SensioLabs Tuesday, February 8, 2011
  • 6. What is OpenSky? Tuesday, February 8, 2011
  • 7. 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. Tuesday, February 8, 2011
  • 8. We Love OpenSource • PHP 5.3 • Apache2 • Symfony2 • Doctrine2 • jQuery • mule, stomp, hornetq • MongoDB • nginx • varnish Tuesday, February 8, 2011
  • 9. We don’t just use open source projects Tuesday, February 8, 2011
  • 10. We help build them Tuesday, February 8, 2011
  • 11. OpenSky has some of the top committers in Symfony2 and other projects Tuesday, February 8, 2011
  • 12. Symfony2 OpenSky Committers • 65 Kris Wallsmith • 52 Jonathan H. Wage • 36 Jeremy Mikola • 36 Bulat Shakirzyanov •6 Justin Hileman Tuesday, February 8, 2011
  • 13. Doctrine MongoDB Committers • 39 Jonathan H. Wage • 11 Bulat Shakirzyanov • 2 Kris Wallsmith Tuesday, February 8, 2011
  • 14. MongoDB ODM Committers • 349 Jonathan H. Wage • 226 Bulat Shakirzyanov • 17 Kris Wallsmith • 13 Steven Surowiec • 2 Jeremy Mikola Tuesday, February 8, 2011
  • 15. Sorry to bore you Tuesday, February 8, 2011
  • 16. Moving on to the stuff you came here for Tuesday, February 8, 2011
  • 17. OpenSky uses Doctrine ORM and ODM Tuesday, February 8, 2011
  • 19. We are an eCommerce site Tuesday, February 8, 2011
  • 20. Actions involving commerce need transactions Tuesday, February 8, 2011
  • 21. ORM and MySQL • Order • OrderTransaction • OrderShipment Tuesday, February 8, 2011
  • 22. ODM and MongoDB • Product • Seller • Supplier • User • ... basically everything else that is not involving $$$ and transactions Tuesday, February 8, 2011
  • 23. Blending the Two Tuesday, February 8, 2011
  • 24. Defining our Product Document Tuesday, February 8, 2011
  • 25. /** @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; } } Tuesday, February 8, 2011
  • 26. Defining our Order Entity Tuesday, February 8, 2011
  • 27. /** * @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; // ... } Tuesday, February 8, 2011
  • 28. Setting the Product public function setProduct(Product $product) { $this->productId = $product->getId(); $this->product = $product; } Tuesday, February 8, 2011
  • 29. • $productId is mapped and persisted • but $product which stores the Product instance is not a persistent entity property Tuesday, February 8, 2011
  • 30. Order has a reference to product? • How? • Order is an ORM entity stored in MySQL • and Product is an ODM document stored in MongoDB Tuesday, February 8, 2011
  • 31. Loading Product ODM reference in Order Entity Tuesday, February 8, 2011
  • 32. Lifecycle Events to the Rescue Tuesday, February 8, 2011
  • 33. 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 Tuesday, February 8, 2011
  • 34. Add EventListener $eventListener = new OrderPostLoadListener($dm); $eventManager = $em->getEventManager(); $eventManager->addEventListener( array(DoctrineORMEvents::postLoad), $eventListener ); Tuesday, February 8, 2011
  • 35. 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> Tuesday, February 8, 2011
  • 36. 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); } } Tuesday, February 8, 2011
  • 37. 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); Tuesday, February 8, 2011
  • 38. Seamless • Documents and Entities play together like best friends • Because Doctrine persistence remains transparent from your domain this is possible Tuesday, February 8, 2011
  • 39. 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 ) ) Tuesday, February 8, 2011
  • 40. 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/ Tuesday, February 8, 2011
  • 41. MongoDB ODM SoftDelete Functionality Tuesday, February 8, 2011
  • 42. I like my deletes soft, not hard Tuesday, February 8, 2011
  • 44. Deleting data is dangerous business Tuesday, February 8, 2011
  • 45. Flickr accidentally deleted a pro members account and 4000 pictures Tuesday, February 8, 2011
  • 46. They were able to restore it later but it took some time Tuesday, February 8, 2011
  • 47. Instead of deleting, simply set a deletedAt field Tuesday, February 8, 2011
  • 48. 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 Tuesday, February 8, 2011
  • 49. Autoload Extension $loader = new UniversalClassLoader(); $loader->registerNamespaces(array( // ... 'DoctrineODMMongoDBSoftDelete' => __DIR__.'/vendor/doctrine-mongodb-odm- softdelete/lib', )); $loader->register(); Tuesday, February 8, 2011
  • 50. 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); Tuesday, February 8, 2011
  • 51. 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 Tuesday, February 8, 2011
  • 52. Autoload the Bundle $loader = new UniversalClassLoader(); $loader->registerNamespaces(array( // ... 'DoctrineODMMongoDBSymfonySoftDeleteBundle' => __DIR__.'/vendor/doctrine- mongodb-odm-softdelete-bundle', )); $loader->register(); Tuesday, February 8, 2011
  • 53. Register the Bundle public function registerBundles() { $bundles = array( // ... // register doctrine symfony bundles new DoctrineODMMongoDBSymfonySoftDeleteBundleSoftDeleteBundle() ); // ... return $bundles; } Tuesday, February 8, 2011
  • 54. Enable the Bundle // app/config/config.yml doctrine_mongodb_softdelete.config: ~ Tuesday, February 8, 2011
  • 55. SoftDeleteManager $sdm = $container->get('doctrine.odm.mongodb.soft_delete.manager'); Tuesday, February 8, 2011
  • 56. SoftDeleteable ODM Documents must implement this interface interface SoftDeleteable { function getDeletedAt(); function isDeleted(); } Tuesday, February 8, 2011
  • 57. User implements SoftDeletable /** @mongodb:Document */ class User implements SoftDeleteable { /** @mongodb:Date @mongodb:Index */ private $deletedAt; public function getDeletedAt() { return $this->deletedAt; } public function isDeleted() { return $this->deletedAt !== null ? true : false; } } Tuesday, February 8, 2011
  • 58. 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(); Tuesday, February 8, 2011
  • 59. Query Executed db.users.update( { _id : { $in : [new ObjectId('1234567891011123456')] } }, { $set : { deletedAt: new Date() } } ) Tuesday, February 8, 2011
  • 60. Restore a User // now again later we can restore that same user $user = $dm->getRepository('User')->findOneByUsername('jwage'); $sdm->restore($user); $sdm->flush(); Tuesday, February 8, 2011
  • 61. Query Executed db.users.update( { _id : { $in : [new ObjectId('1234567891011123456')] } }, { $unset : { deletedAt: true } } ) Tuesday, February 8, 2011
  • 62. Limit cursors to only show non deleted users $qb = $dm->createQueryBuilder('User') ->field('deletedAt')->exists(false); $query = $qb->getQuery(); $users = $query->execute(); Tuesday, February 8, 2011
  • 63. Get only deleted users $qb = $dm->createQueryBuilder('User') ->field('deletedAt')->exists(true); $query = $qb->getQuery(); $users = $query->execute(); Tuesday, February 8, 2011
  • 64. 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(); Tuesday, February 8, 2011
  • 65. 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); Tuesday, February 8, 2011
  • 66. Symfony2 and supervisor http://supervisord.org/ Tuesday, February 8, 2011
  • 67. What is supervisor? Tuesday, February 8, 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 Tuesday, February 8, 2011
  • 69. Daemonize a Symfony2 Console Command with supervisor Tuesday, February 8, 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? Tuesday, February 8, 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 Tuesday, February 8, 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; // ... } Tuesday, February 8, 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 Tuesday, February 8, 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(); // ... } // ... } Tuesday, February 8, 2011
  • 75. The Daemon Console Command • You can find the console command code to use to tail a cursor here: • https://gist.github.com/812942 Tuesday, February 8, 2011
  • 76. 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 Tuesday, February 8, 2011
  • 77. 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(); } } Tuesday, February 8, 2011
  • 78. 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) { } } Tuesday, February 8, 2011
  • 79. 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); } Tuesday, February 8, 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. Tuesday, February 8, 2011
  • 81. Install supervisor http://supervisord.org/installing.html $ easy_install supervisor Tuesday, February 8, 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 Tuesday, February 8, 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 Tuesday, February 8, 2011
  • 84. Where do I use supervisor? • sociallynotable.com • Indexes tweets with links to Amazon.com products • Maintains statistics on each product and lets you shop the popular products each day Tuesday, February 8, 2011
  • 85. sn:watch-twitter console command Tuesday, February 8, 2011
  • 86. I can manually start it Tuesday, February 8, 2011
  • 87. But, what if it crashes or stops unexpectedly? Tuesday, February 8, 2011
  • 88. This is exactly what supervisor is for Tuesday, February 8, 2011
  • 89. Setup a configuration profile for supervisor and it will ensure the console command is always running Tuesday, February 8, 2011
  • 91. Now when I start supervisor the twitter watcher will always remain running. Even if I kill the pid myself, it will start back up. Tuesday, February 8, 2011
  • 92. Thanks! I hope this presentation was useful to you! Tuesday, February 8, 2011
  • 93. Questions? - http://jwage.com - http://shopopensky.com - http://sociallynotable.com - http://twitter.com/jwage - http://facebook.com/jwage - http://about.me/jwage Tuesday, February 8, 2011