SlideShare a Scribd company logo
1 of 171
Download to read offline
Extending Doctrine 2
For Your Domain Model


                     Ross Tuck
                  June 9th, DPC
Who Am I?
Ross Tuck
Team Lead at Ibuildings
Codemonkey
REST nut
Hat guy
Token Foreigner America,
               &@* ! Yeah
@rosstuck
#dashinglyHandsome
Quick But Necessary
Doctrine 101
Doctrine 102
Doctrine 102.5
Model


  /** @Entity */
  class Article {
        /** @Id @GeneratedValue
         *   @Column(type="integer") */
        protected $id;


        /** @Column(type="string") */
        protected $title;


        /** @Column(type="text") */
        protected $content;




  }
Controller




   $article = $em->find('Article', 1);
   $article->setTitle('Awesome New Story');


   $em->flush();
Filters
TPS Request
We need a way to approve comments
before they appear to all users.
Controller




   $comments = $em->getRepository('Comment')->findAll();
   foreach($comments as $comment) {
        echo $comment->getContent()."n";
   }



Output:
Doug isn't human
Doug is the best
Controller




   $comments = $em->getRepository('Comment')->findAll();
   foreach($comments as $comment) {
        echo $comment->getContent()."n";
   }
Controller




   $comments = $em->createQuery('SELECT c FROM Comment c');
   foreach($comments->getResult() as $comment) {
        echo $comment->getContent()."n";
   }
Controller




   $comments = $em->find('Article', 1)->getComments();
   foreach($comments as $comment) {
        echo $comment->getContent()."n";
   }
                                             !
                            ce s in your code
             Approx 100 pla
Filters
New in 2.2
Filter



  use DoctrineORMQueryFilterSQLFilter;


  class CommentFilter extends SQLFilter {


         public function addFilterConstraint($entityInfo, $alias) {
             if ($entityInfo->name !== 'Comment') {
                 return "";
             }
             return $alias.".approved = 1";
         }


  }
Bootstrap




                         y
               Ha ndy ke


   $em->getConfiguration()
       ->addFilter('approved_comments', 'CommentFilter');




                                         Class name
Bootstrap




   $em->getConfiguration()
       ->addFilter('approved_comments', 'CommentFilter');
   $em->getFilters()->enable('approved_comments');
Bootstrap




   if ($this->isNormalUser()) {
        $em->getConfiguration()
            ->addFilter('approved_comments', 'CommentFilter');
        $em->getFilters()->enable('approved_comments');
   }
Controller




   $comments = $em->getRepository('Comment')->findAll();
   foreach($comments as $comment) {
        echo $comment->getContent()."n";
   }


                                                     al User
Output:                                     A s Norm
Doug is the best
Controller




   $comments = $em->getRepository('Comment')->findAll();
   foreach($comments as $comment) {
        echo $comment->getContent()."n";
   }


                                                       in
Output:
                                            As the Adm
Doug isn't human
Doug is the best
Parameters
Bootstrap




   $filter = $em->getFilters()->getFilter('approved_comments');
   $filter->setParameter('level', $this->getUserLevel());
Filter

  use DoctrineORMQueryFilterSQLFilter;


  class CommentFilter extends SQLFilter {


         public function addFilterConstraint($entityInfo, $alias) {
             if ($entityInfo->name !== 'Comment') {
                 return "";
             }


             $level = $this->getParameter('level');
             return $alias.".approved = 1";
         }


  }
Filter

  use DoctrineORMQueryFilterSQLFilter;


  class CommentFilter extends SQLFilter {


         public function addFilterConstraint($entityInfo, $alias) {
             if ($entityInfo->name !== 'Comment') {
                 return "";
             }


             $level = $this->getParameter('level');
             return $alias.".approved = ".$level;
         }


  }
Filter

  use DoctrineORMQueryFilterSQLFilter;


  class CommentFilter extends SQLFilter {


         public function addFilterConstraint($entityInfo, $alias) {
             if ($entityInfo->name !== 'Comment') {
                 return "";
             }                                      Already
                                                    escaped
             $level = $this->getParameter('level');
             return $alias.".approved = ".$level;
         }


  }
Limitations
Events
Inser
          t
prePersist    postLoad
postPersist   loadClassMetadata
preUpdate     preFlush
postUpdate    onFlush
preRemove     postFlush
postRemove    onClear
Callbacks        Listeners
On the model   External objects
Lifecycle Callbacks
Model


  /** @Entity */
  class Article {
        /** @Id @GeneratedValue
         *   @Column(type="integer") */
        protected $id;


        /** @Column(type="string") */
        protected $title;


        /** @Column(type="text") */
        protected $content;




  }
Controller




   $article = $em->find('Article', 1);
   $article->setTitle('Awesome New Story');


   $em->flush();
TPS Request
Articles must record the last date they
were modified in any way.
Controller




   $article = $em->find('Article', 1);
   $article->setTitle('Awesome New Story');
   $article->setUpdatedAt(new DateTime('now'));

                                         + 100 other files
   $em->flush();                         + testing
                                         + missed bugs
Model


  /** @Entity */
  class Article {
        /** @Id @GeneratedValue
        *   @Column(type="integer") */
        protected $id;


        /** @Column(type="string") */
        protected $title;




  }
Model


  /** @Entity */
  class Article {
        /** @Id @GeneratedValue
        *   @Column(type="integer") */
        protected $id;


        /** @Column(type="string") */
        protected $title;


        /** @Column(type="datetime") */
        protected $updatedAt;



  }
Model


  /** @Entity */
  class Article {
        // ...



        public function markAsUpdated() {
            $this->updatedAt = new Datetime('now');
        }




  }
Model


  /** @Entity @HasLifecycleCallbacks */
  class Article {
        // ...
                                            Event mapping
        /** @PreUpdate */
        public function markAsUpdated() {
            $this->updatedAt = new Datetime('now');
        }




  }
Controller




   $article = $em->find('Article', 1);
   $article->setTitle('Awesome New Story');      No
                                                 Changes!
   $em->flush();

   echo $article->getUpdatedAt()->format('c');
   // 2012-05-29T10:48:00+02:00
Model


  /** @Entity @HasLifecycleCallbacks */
  class Article {
        // ...


        /** @PreUpdate */
        public function markAsUpdated() {
            $this->updatedAt = new Datetime('now');
        }




  }
What else can I do?
prePersist    postLoad
postPersist   loadClassMetadata
preUpdate     preFlush
postUpdate    onFlush
preRemove     postFlush
postRemove    onClear
prePersist    postLoad
postPersist   loadClassMetadata
preUpdate     preFlush
postUpdate    onFlush
preRemove     postFlush
postRemove    onClear
Protip:
Only fires if you're dirty
new MudWrestling();
Controller




   $article = $em->find('Article', 1);
   $article->setTitle('Awesome New Story');


   $em->flush();
   // 2012-05-29T10:48:00+02:00
   // 2012-05-29T10:48:00+02:00    2X
Controller




   $article = $em->find('Article', 1);
   $article->setTitle('Awesome New Story');


   $em->flush();

             'Awesome New Story' === 'Awesome New Story'
                           No update
Controller




   $article = $em->find('Article', 1);
   $article->setTitle('Fantabulous Updated Story');


   $em->flush();

     'Awesome New Story' !== 'Fantabulous Updated Story'
                    Update, yeah!
Controller




   $article = $em->find('Article', 1);
   $article->setTitle('Awesome New Project'.uniqid());


   $em->flush();
So, here's the thing.
The Thing
Events are cool.
Callbacks?
Eeeeeeeeeeeeeeeeeeh.
•   Limited to model dependencies
•   Do you really need that function?
•   Protected function? Silent error
•   Repeated Code
•   Performance implications
Listeners
Listener




   class UpdateTimeListener {



           public function preUpdate($event) {


           }




   }
Bootstrap




   $em->getEventManager()->addEventListener(
        array(DoctrineORMEvents::preUpdate),
        new UpdateTimeListener()
   );
Invert
Invert
Bootstrap




   $em->getEventManager()->addEventListener(
        array(DoctrineORMEvents::preUpdate),
        new UpdateTimeListener()
   );
Bootstrap




   $em->getEventManager()->addEventSubscriber(
        new UpdateTimeListener()
   );
Listener




   class UpdateTimeListener {


           public function preUpdate($event) {


           }


           public function getSubscribedEvents() {
               return array(Events::preUpdate);
           }



   }
Listener


   use DoctrineCommonEventSubscriber;
   class UpdateTimeListener implements EventSubscriber {


           public function preUpdate($event) {


           }


           public function getSubscribedEvents() {
               return array(Events::preUpdate);
           }



   }
Functionally?   No difference
Design?         Subscriber
Bootstrap




   $em->getEventManager()->addEventSubscriber(
        new ChangeMailListener()
   );
Bootstrap




   $em->getEventManager()->addEventSubscriber(
        new ChangeMailListener($mailer)
   );
Listener

   class UpdateTimeListener implements EventSubscriber {


           public function getSubscribedEvents() {
               return array(Events::preUpdate);
           }


           public function preUpdate($event) {


           }




   }
Listener

   public function preUpdate($event) {
           $em = $event->getEntityManager();
           $model = $event->getEntity();
           if (!$model instanceof Article) { return; }


           $model->setUpdatedAt(new Datetime('now'));


           $uow = $event->getEntityManager()->getUnitOfWork();
           $uow->recomputeSingleEntityChangeSet(
                $em->getClassMetadata('Article'),
                $model
           );

   }
Controller




   $article = $em->find('Article', 1);
   $article->setTitle('Awesome New Story');


   $em->flush();

   echo $article->getUpdatedAt()->format('c');
   // 2012-05-29T10:48:00+02:00
Whoopity-doo
Theory Land




   interface LastUpdatedInterface {
        public function setUpdatedAt(Datetime $date);
        public function getUpdatedAt();
   }
Listener

   public function preUpdate($event) {
           $em = $event->getEntityManager();
           $model = $event->getEntity();
           if (!$model instanceof Article) { return; }


           $model->setUpdatedAt(new Datetime('now'));


           $uow = $event->getEntityManager()->getUnitOfWork();
           $uow->recomputeSingleEntityChangeSet(
                $em->getClassMetadata('Article'),
                $model
           );

   }
Listener

   public function preUpdate($event) {
           $em = $event->getEntityManager();
           $model = $event->getEntity();
           if (!$model instanceof LastUpdatedInterface) { return; }


           $model->setUpdatedAt(new Datetime('now'));


           $uow = $event->getEntityManager()->getUnitOfWork();
           $uow->recomputeSingleEntityChangeSet(
                $em->getClassMetadata('Article'),
                $model
           );

   }
Listener

   public function preUpdate($event) {
           $em = $event->getEntityManager();
           $model = $event->getEntity();
           if (!$model instanceof LastUpdatedInterface) { return; }


           $model->setUpdatedAt(new Datetime('now'));


           $uow = $event->getEntityManager()->getUnitOfWork();
           $uow->recomputeSingleEntityChangeSet(
                $em->getClassMetadata(get_class($model)),
                $model
           );

   }
The POWAH
Flushing And Multiple Events
OnFlush
TPS Request
After every update to an article, I want
an email of the changes sent to me.

Also, bring me more lettuce.
Listener


   class ChangeMailListener implements EventSubscriber {
           protected $mailMessage;


           public function getSubscribedEvents()   {
               return array(Events::onFlush, Events::postFlush);
           }
   }
Listener


   public function onFlush($event) {
           $uow = $event->getEntityManager()->getUnitOfWork();


           foreach($uow->getScheduledEntityUpdates() as $model) {
               $changeset = $uow->getEntityChangeSet($model);


           }
   }
array(1) {
    ["title"]=>
        array(2) {
          [0]=> string(16) "Boring Old Title"
          [1]=> string(16) "Great New Title!"
    }
}
Listener


   public function onFlush($event) {
           $uow = $event->getEntityManager()->getUnitOfWork();


           foreach($uow->getScheduledEntityUpdates() as $model) {
               $changeset = $uow->getEntityChangeSet($model);
               $this->formatAllPrettyInMail($model, $changeset);
           }
   }
Listener


   public function onFlush($event) {
           $uow = $event->getEntityManager()->getUnitOfWork();


           foreach($uow->getScheduledEntityUpdates() as $model) {
               $changeset = $uow->getEntityChangeSet($model);
               $this->formatAllPrettyInMail($model, $changeset);
           }
   }


   public function postFlush($event) {
           if (!$this->hasMessage()) { return; }
           $this->mailTheBoss($this->message);
   }
That's really all there is to it.
Listener


   public function formatAllPrettyInMail($model, $changes) {
           if (!$model instanceof Article) {
               return;
           }
           $msg = "";
           foreach($changes as $field => $values) {
               $msg .= "{$field} ----- n".
                    "old: {$values[0]} n".
                    "new: {$values[1]} nn";
           }


           $this->mailMessage .= $msg;
   }
Advice:
Treat your listeners like controllers
Keep it thin
Crazy Town
TPS Request
Write the change messages to the
database instead of emailing them.
Model

  /** @Entity */
  class ChangeLog {
        /** @Id @GeneratedValue
        *   @Column(type="integer") */
        protected $id;


        /** @Column(type="text") */
        protected $description;
  }
Listener



   class ChangeLogger implements EventSubscriber {
           public function getSubscribedEvents() {
               return array(Events::onFlush);
           }


           public function onFlush($event) {


           }
   }
Listener::onFlush

   public function onFlush($event) {
        $em = $event->getEntityManager();
        $uow = $em->getUnitOfWork();


        foreach($uow->getScheduledEntityUpdates() as $model) {
              if (!$model instanceof Article) { continue; }
              $log = new ChangeLog();
              $log->setDescription($this->meFormatPrettyOneDay());


              $em->persist($log);
              $uow->computeChangeSet(
                    $em->getClassMetadata('ChangeLog'), $log
              );
        }
Shiny
Yes, I really used PHPMyAdmin there
Shiny
Also, wow, it worked!
UnitOfWork API




                 $uow->getScheduledEntityInsertions();
                 $uow->getScheduledEntityUpdates();
                 $uow->getScheduledEntityDeletions();


                 $uow->getScheduledCollectionUpdates();
                 $uow->getScheduledCollectionDeletions();
UnitOfWork API




                 $uow->scheduleForInsert();
                 $uow->scheduleForUpdate();
                 $uow->scheduleExtraUpdate();
                 $uow->scheduleForDelete();
UnitOfWork API




                 And many more...
postLoad
Model


  /** @Entity */
  class Article {
        //...


        /** @Column(type="integer") */
        protected $viewCount;




  }
+
MySQL       Redis
TPS Request
Move view counts out of the database
into a faster system.

And don't break everything.
Controller




   $article = $em->find('Article', 1);
   echo $article->getViewCount();
Model


  /** @Entity */
  class Article {
        //...


        /** @Column(type="integer") */
        protected $viewCount;




  }
Listener

   class ViewCountListener implements EventSubscriber {
           public function getSubscribedEvents() {
               return DoctrineORMEvents::postLoad;
           }


           public function postLoad($event) {
               $model = $event->getEntity();
               if (!$model instanceof Article) {
                   return;
               }
               $currentRank = $this->getCountFromRedis($model);
               $model->setViewCount($currentRank);
           }
   }
Controller




   $article = $em->find('Article', 1);
   echo $article->getViewCount();
Many, many other uses.
Class Metadata
Model


  /** @Entity */
  class Article {
        /** @Id @GeneratedValue
        *   @Column(type="integer") */
        protected $id;
                                          Where do
        /** @Column(type="string") */     they go?
        protected $title;


        /** @Column(type="datetime") */
        protected $updatedAt;


  }
Model


  /** @Entity */
  class Article {
        /** @Id @GeneratedValue
        *   @Column(type="integer") */
        protected $id;


        /** @Column(type="string") */
        protected $title;


        /** @Column(type="datetime") */
        protected $updatedAt;


  }
DoctrineORMMappingClassMetadata
???




      $metadata = $em->getClassMetadata('Article');
???




      $metadata = $em->getClassMetadata('Article');
      echo $metadata->getTableName();




Output:
Article
???




      $metadata = $em->getClassMetadata('Article');
      echo $metadata->getTableName();




Output:
articles
Doctrine 2.3 will bring NamingStrategy
???




      $conn = $em->getConnection();
      $tableName = $metadata->getQuotedTableName($conn);


      $results = $conn->query('SELECT * FROM '.$tableName);
Tip of the Iceberg
???




      $metadata->getFieldMapping('title');


array(8) {
  fieldName     =>   "title"
  type          =>   "string"
  length        =>   NULL
  precision     =>   0
  scale         =>   0
  nullable      =>   false
  unique        =>   false
  columnName    =>   "title"
}
???




      $metadata->getFieldMapping('title');
      $metadata->getFieldNames();


      $metadata->getReflectionClass();
      $metadata->getReflectionProperty('title');


      $metadata->getColumnName('title');
      $metadata->getTypeOfField('title');
Simple Example
Symple Example
Form in Symfony2




   class ArticleType extends AbstractType {


        public function buildForm($builder, array $options) {
             $builder
                   ->add('title')
                   ->add('content');
        }


   }
Controller in Symfony2




   $entity = new Article();
   $form      = $this->createForm(new ArticleType(), $entity);
Form in Symfony2




   class ArticleType extends AbstractType {


        public function buildForm($builder, array $options) {
             $builder
                   ->add('title')
                   ->add('content');
        }


   }
Symfony Doctrine Bridge

  switch ($metadata->getTypeOfField($property)) {
       case 'string':
            return new TypeGuess('text', ...);
       case 'boolean':
            return new TypeGuess('checkbox', ...);
       case 'integer':
       case 'bigint':
       case 'smallint':
            return new TypeGuess('integer', ...);
       case 'text':
            return new TypeGuess('textarea', ...);
       default:
            return new TypeGuess('text', ...);
  }
Cool, huh?
Cool, huh?
???




      $metadata->getAssociationMapping('comments');


array(15) {
  fieldName        =>   "comments"
  mappedBy         =>   "article"
  targetEntity     =>   "Comment"
  cascade          =>   array(0) {}
  fetch            =>   2
  type             =>   4
  isOwningSide     =>   false
  sourceEntity     =>   "Article"
  ...
???




      $metadata->getAssociationMapping('comments');
      $metadata->getAssociationMappings();


      $metadata->isSingleValuedAssociation('comments');
      $metadata->isCollectionValuedAssociation('comments');


      $metadata->getAssociationTargetClass('comments');
Form in Symfony2




   class ArticleType extends AbstractType {


        public function buildForm($builder, array $options) {
             $builder
                   ->add('title')
                   ->add('content')
                   ->add('comments');
        }


   }
Oh yeah.
You can set all of this stuff.
100~ public functions
But is there a good reason?
Unless you're writing a driver, probably not.
Listener




   class MyListener {


           public function loadClassMetadata($event) {
               echo $event->getClassMetadata()->getName();
           }


   }
Where's the manatee?
You're the manatee.
Epilogue & Service Layers
Model




Controller




  View
Model




Service Layer




 Controller




    View
Be smart.
Be smart.
Be simple
Don't overuse this.
Test
Test test test
test test test test test test test test test test
test test test test test test test test test test
test test test test test test test test test test
test test test test test test test test test test
test test test test test test test test test test
test test test test test test test test test test
test test test test test test test test test test
test test test test test test test test test test
test test test test test test test test test test
test test test test test test test test test test
test test test test test test test test test test
RTFM.
Go forth and rock.
You're the manatee.
Thanks to:

@boekkooi
#doctrine (freenode)
Wikipedia




            OwnMoment (sxc.hu)
https://joind.in/6251

More Related Content

Viewers also liked

Symfony2 and Doctrine2 Integration
Symfony2 and Doctrine2 IntegrationSymfony2 and Doctrine2 Integration
Symfony2 and Doctrine2 IntegrationJonathan Wage
 
Criando aplicações RestFul com Zend Framework 2
Criando aplicações RestFul com Zend Framework 2Criando aplicações RestFul com Zend Framework 2
Criando aplicações RestFul com Zend Framework 2Elton Minetto
 
An Architectural Model for Adapting Domain-Specific AOM Applications
An Architectural Model for Adapting Domain-Specific AOM ApplicationsAn Architectural Model for Adapting Domain-Specific AOM Applications
An Architectural Model for Adapting Domain-Specific AOM Applicationseduardomg23
 
ORM dont kill your DB, developers do
ORM dont kill your DB, developers doORM dont kill your DB, developers do
ORM dont kill your DB, developers doGuilherme Blanco
 
Domain Model
Domain ModelDomain Model
Domain Modeldzenanr
 
Prof.dr. halit hami oz 01-hastane otomasyonu-amaç kapsam ve standartlar
Prof.dr. halit hami oz 01-hastane otomasyonu-amaç kapsam ve standartlarProf.dr. halit hami oz 01-hastane otomasyonu-amaç kapsam ve standartlar
Prof.dr. halit hami oz 01-hastane otomasyonu-amaç kapsam ve standartlarProf. Dr. Halit Hami Öz
 
Improving application design with a rich domain model (springone 2007)
Improving application design with a rich domain model (springone 2007)Improving application design with a rich domain model (springone 2007)
Improving application design with a rich domain model (springone 2007)Chris Richardson
 
Domain model example
Domain model exampleDomain model example
Domain model exampleHeba Fathy
 
003 Uml Semalari [94 Slides]
003 Uml Semalari [94 Slides]003 Uml Semalari [94 Slides]
003 Uml Semalari [94 Slides]Erol Bozkurt
 
Enterprise PHP: mappers, models and services
Enterprise PHP: mappers, models and servicesEnterprise PHP: mappers, models and services
Enterprise PHP: mappers, models and servicesAaron Saray
 
Building Rich Domain Models
Building Rich Domain ModelsBuilding Rich Domain Models
Building Rich Domain ModelsChris Richardson
 
Doctrine In The Real World sflive2011 Paris
Doctrine In The Real World sflive2011 ParisDoctrine In The Real World sflive2011 Paris
Doctrine In The Real World sflive2011 ParisJonathan Wage
 
NCBTS Domain 2: Learning Environment
NCBTS Domain 2: Learning EnvironmentNCBTS Domain 2: Learning Environment
NCBTS Domain 2: Learning EnvironmentJenny Reyes
 
Okul otomasyon rapor
Okul otomasyon raporOkul otomasyon rapor
Okul otomasyon raporEnes Caglar
 

Viewers also liked (20)

Symfony2 and Doctrine2 Integration
Symfony2 and Doctrine2 IntegrationSymfony2 and Doctrine2 Integration
Symfony2 and Doctrine2 Integration
 
Criando aplicações RestFul com Zend Framework 2
Criando aplicações RestFul com Zend Framework 2Criando aplicações RestFul com Zend Framework 2
Criando aplicações RestFul com Zend Framework 2
 
An Architectural Model for Adapting Domain-Specific AOM Applications
An Architectural Model for Adapting Domain-Specific AOM ApplicationsAn Architectural Model for Adapting Domain-Specific AOM Applications
An Architectural Model for Adapting Domain-Specific AOM Applications
 
ORM dont kill your DB, developers do
ORM dont kill your DB, developers doORM dont kill your DB, developers do
ORM dont kill your DB, developers do
 
Doctrine ORM & model
Doctrine ORM & modelDoctrine ORM & model
Doctrine ORM & model
 
Domain Model
Domain ModelDomain Model
Domain Model
 
Class Diagram
Class DiagramClass Diagram
Class Diagram
 
Prof.dr. halit hami oz 01-hastane otomasyonu-amaç kapsam ve standartlar
Prof.dr. halit hami oz 01-hastane otomasyonu-amaç kapsam ve standartlarProf.dr. halit hami oz 01-hastane otomasyonu-amaç kapsam ve standartlar
Prof.dr. halit hami oz 01-hastane otomasyonu-amaç kapsam ve standartlar
 
Improving application design with a rich domain model (springone 2007)
Improving application design with a rich domain model (springone 2007)Improving application design with a rich domain model (springone 2007)
Improving application design with a rich domain model (springone 2007)
 
Domain model example
Domain model exampleDomain model example
Domain model example
 
003 Uml Semalari [94 Slides]
003 Uml Semalari [94 Slides]003 Uml Semalari [94 Slides]
003 Uml Semalari [94 Slides]
 
Enterprise PHP: mappers, models and services
Enterprise PHP: mappers, models and servicesEnterprise PHP: mappers, models and services
Enterprise PHP: mappers, models and services
 
Building Rich Domain Models
Building Rich Domain ModelsBuilding Rich Domain Models
Building Rich Domain Models
 
Ncbts vs. code of ethics
Ncbts  vs. code of ethicsNcbts  vs. code of ethics
Ncbts vs. code of ethics
 
Doctrine In The Real World sflive2011 Paris
Doctrine In The Real World sflive2011 ParisDoctrine In The Real World sflive2011 Paris
Doctrine In The Real World sflive2011 Paris
 
NCBTS Domain 2: Learning Environment
NCBTS Domain 2: Learning EnvironmentNCBTS Domain 2: Learning Environment
NCBTS Domain 2: Learning Environment
 
Okul otomasyon rapor
Okul otomasyon raporOkul otomasyon rapor
Okul otomasyon rapor
 
Hastane Poliklinik Otomasyonu
Hastane Poliklinik OtomasyonuHastane Poliklinik Otomasyonu
Hastane Poliklinik Otomasyonu
 
UML ile Modelleme
UML ile ModellemeUML ile Modelleme
UML ile Modelleme
 
Domain model
Domain modelDomain model
Domain model
 

Recently uploaded

08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CVKhem
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsJoaquim Jorge
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 

Recently uploaded (20)

08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 

Extending Doctrine 2 for your Domain Model

  • 1. Extending Doctrine 2 For Your Domain Model Ross Tuck June 9th, DPC
  • 2.
  • 5. Team Lead at Ibuildings Codemonkey REST nut Hat guy Token Foreigner America, &@* ! Yeah
  • 11. Model /** @Entity */ class Article { /** @Id @GeneratedValue * @Column(type="integer") */ protected $id; /** @Column(type="string") */ protected $title; /** @Column(type="text") */ protected $content; }
  • 12. Controller $article = $em->find('Article', 1); $article->setTitle('Awesome New Story'); $em->flush();
  • 14.
  • 15. TPS Request We need a way to approve comments before they appear to all users.
  • 16. Controller $comments = $em->getRepository('Comment')->findAll(); foreach($comments as $comment) { echo $comment->getContent()."n"; } Output: Doug isn't human Doug is the best
  • 17. Controller $comments = $em->getRepository('Comment')->findAll(); foreach($comments as $comment) { echo $comment->getContent()."n"; }
  • 18. Controller $comments = $em->createQuery('SELECT c FROM Comment c'); foreach($comments->getResult() as $comment) { echo $comment->getContent()."n"; }
  • 19. Controller $comments = $em->find('Article', 1)->getComments(); foreach($comments as $comment) { echo $comment->getContent()."n"; } ! ce s in your code Approx 100 pla
  • 21. Filter use DoctrineORMQueryFilterSQLFilter; class CommentFilter extends SQLFilter { public function addFilterConstraint($entityInfo, $alias) { if ($entityInfo->name !== 'Comment') { return ""; } return $alias.".approved = 1"; } }
  • 22. Bootstrap y Ha ndy ke $em->getConfiguration() ->addFilter('approved_comments', 'CommentFilter'); Class name
  • 23. Bootstrap $em->getConfiguration() ->addFilter('approved_comments', 'CommentFilter'); $em->getFilters()->enable('approved_comments');
  • 24. Bootstrap if ($this->isNormalUser()) { $em->getConfiguration() ->addFilter('approved_comments', 'CommentFilter'); $em->getFilters()->enable('approved_comments'); }
  • 25. Controller $comments = $em->getRepository('Comment')->findAll(); foreach($comments as $comment) { echo $comment->getContent()."n"; } al User Output: A s Norm Doug is the best
  • 26. Controller $comments = $em->getRepository('Comment')->findAll(); foreach($comments as $comment) { echo $comment->getContent()."n"; } in Output: As the Adm Doug isn't human Doug is the best
  • 28. Bootstrap $filter = $em->getFilters()->getFilter('approved_comments'); $filter->setParameter('level', $this->getUserLevel());
  • 29. Filter use DoctrineORMQueryFilterSQLFilter; class CommentFilter extends SQLFilter { public function addFilterConstraint($entityInfo, $alias) { if ($entityInfo->name !== 'Comment') { return ""; } $level = $this->getParameter('level'); return $alias.".approved = 1"; } }
  • 30. Filter use DoctrineORMQueryFilterSQLFilter; class CommentFilter extends SQLFilter { public function addFilterConstraint($entityInfo, $alias) { if ($entityInfo->name !== 'Comment') { return ""; } $level = $this->getParameter('level'); return $alias.".approved = ".$level; } }
  • 31. Filter use DoctrineORMQueryFilterSQLFilter; class CommentFilter extends SQLFilter { public function addFilterConstraint($entityInfo, $alias) { if ($entityInfo->name !== 'Comment') { return ""; } Already escaped $level = $this->getParameter('level'); return $alias.".approved = ".$level; } }
  • 34. Inser t prePersist postLoad postPersist loadClassMetadata preUpdate preFlush postUpdate onFlush preRemove postFlush postRemove onClear
  • 35. Callbacks Listeners On the model External objects
  • 37. Model /** @Entity */ class Article { /** @Id @GeneratedValue * @Column(type="integer") */ protected $id; /** @Column(type="string") */ protected $title; /** @Column(type="text") */ protected $content; }
  • 38. Controller $article = $em->find('Article', 1); $article->setTitle('Awesome New Story'); $em->flush();
  • 39.
  • 40. TPS Request Articles must record the last date they were modified in any way.
  • 41. Controller $article = $em->find('Article', 1); $article->setTitle('Awesome New Story'); $article->setUpdatedAt(new DateTime('now')); + 100 other files $em->flush(); + testing + missed bugs
  • 42. Model /** @Entity */ class Article { /** @Id @GeneratedValue * @Column(type="integer") */ protected $id; /** @Column(type="string") */ protected $title; }
  • 43. Model /** @Entity */ class Article { /** @Id @GeneratedValue * @Column(type="integer") */ protected $id; /** @Column(type="string") */ protected $title; /** @Column(type="datetime") */ protected $updatedAt; }
  • 44. Model /** @Entity */ class Article { // ... public function markAsUpdated() { $this->updatedAt = new Datetime('now'); } }
  • 45. Model /** @Entity @HasLifecycleCallbacks */ class Article { // ... Event mapping /** @PreUpdate */ public function markAsUpdated() { $this->updatedAt = new Datetime('now'); } }
  • 46. Controller $article = $em->find('Article', 1); $article->setTitle('Awesome New Story'); No Changes! $em->flush(); echo $article->getUpdatedAt()->format('c'); // 2012-05-29T10:48:00+02:00
  • 47. Model /** @Entity @HasLifecycleCallbacks */ class Article { // ... /** @PreUpdate */ public function markAsUpdated() { $this->updatedAt = new Datetime('now'); } }
  • 48. What else can I do?
  • 49. prePersist postLoad postPersist loadClassMetadata preUpdate preFlush postUpdate onFlush preRemove postFlush postRemove onClear
  • 50. prePersist postLoad postPersist loadClassMetadata preUpdate preFlush postUpdate onFlush preRemove postFlush postRemove onClear
  • 51. Protip: Only fires if you're dirty
  • 53. Controller $article = $em->find('Article', 1); $article->setTitle('Awesome New Story'); $em->flush(); // 2012-05-29T10:48:00+02:00 // 2012-05-29T10:48:00+02:00 2X
  • 54. Controller $article = $em->find('Article', 1); $article->setTitle('Awesome New Story'); $em->flush(); 'Awesome New Story' === 'Awesome New Story' No update
  • 55. Controller $article = $em->find('Article', 1); $article->setTitle('Fantabulous Updated Story'); $em->flush(); 'Awesome New Story' !== 'Fantabulous Updated Story' Update, yeah!
  • 56. Controller $article = $em->find('Article', 1); $article->setTitle('Awesome New Project'.uniqid()); $em->flush();
  • 57. So, here's the thing.
  • 62. Limited to model dependencies • Do you really need that function? • Protected function? Silent error • Repeated Code • Performance implications
  • 64. Listener class UpdateTimeListener { public function preUpdate($event) { } }
  • 65. Bootstrap $em->getEventManager()->addEventListener( array(DoctrineORMEvents::preUpdate), new UpdateTimeListener() );
  • 68. Bootstrap $em->getEventManager()->addEventListener( array(DoctrineORMEvents::preUpdate), new UpdateTimeListener() );
  • 69. Bootstrap $em->getEventManager()->addEventSubscriber( new UpdateTimeListener() );
  • 70. Listener class UpdateTimeListener { public function preUpdate($event) { } public function getSubscribedEvents() { return array(Events::preUpdate); } }
  • 71. Listener use DoctrineCommonEventSubscriber; class UpdateTimeListener implements EventSubscriber { public function preUpdate($event) { } public function getSubscribedEvents() { return array(Events::preUpdate); } }
  • 72. Functionally? No difference Design? Subscriber
  • 73. Bootstrap $em->getEventManager()->addEventSubscriber( new ChangeMailListener() );
  • 74. Bootstrap $em->getEventManager()->addEventSubscriber( new ChangeMailListener($mailer) );
  • 75. Listener class UpdateTimeListener implements EventSubscriber { public function getSubscribedEvents() { return array(Events::preUpdate); } public function preUpdate($event) { } }
  • 76. Listener public function preUpdate($event) { $em = $event->getEntityManager(); $model = $event->getEntity(); if (!$model instanceof Article) { return; } $model->setUpdatedAt(new Datetime('now')); $uow = $event->getEntityManager()->getUnitOfWork(); $uow->recomputeSingleEntityChangeSet( $em->getClassMetadata('Article'), $model ); }
  • 77. Controller $article = $em->find('Article', 1); $article->setTitle('Awesome New Story'); $em->flush(); echo $article->getUpdatedAt()->format('c'); // 2012-05-29T10:48:00+02:00
  • 79. Theory Land interface LastUpdatedInterface { public function setUpdatedAt(Datetime $date); public function getUpdatedAt(); }
  • 80. Listener public function preUpdate($event) { $em = $event->getEntityManager(); $model = $event->getEntity(); if (!$model instanceof Article) { return; } $model->setUpdatedAt(new Datetime('now')); $uow = $event->getEntityManager()->getUnitOfWork(); $uow->recomputeSingleEntityChangeSet( $em->getClassMetadata('Article'), $model ); }
  • 81. Listener public function preUpdate($event) { $em = $event->getEntityManager(); $model = $event->getEntity(); if (!$model instanceof LastUpdatedInterface) { return; } $model->setUpdatedAt(new Datetime('now')); $uow = $event->getEntityManager()->getUnitOfWork(); $uow->recomputeSingleEntityChangeSet( $em->getClassMetadata('Article'), $model ); }
  • 82. Listener public function preUpdate($event) { $em = $event->getEntityManager(); $model = $event->getEntity(); if (!$model instanceof LastUpdatedInterface) { return; } $model->setUpdatedAt(new Datetime('now')); $uow = $event->getEntityManager()->getUnitOfWork(); $uow->recomputeSingleEntityChangeSet( $em->getClassMetadata(get_class($model)), $model ); }
  • 86.
  • 87. TPS Request After every update to an article, I want an email of the changes sent to me. Also, bring me more lettuce.
  • 88. Listener class ChangeMailListener implements EventSubscriber { protected $mailMessage; public function getSubscribedEvents() { return array(Events::onFlush, Events::postFlush); } }
  • 89. Listener public function onFlush($event) { $uow = $event->getEntityManager()->getUnitOfWork(); foreach($uow->getScheduledEntityUpdates() as $model) { $changeset = $uow->getEntityChangeSet($model); } }
  • 90. array(1) { ["title"]=> array(2) { [0]=> string(16) "Boring Old Title" [1]=> string(16) "Great New Title!" } }
  • 91. Listener public function onFlush($event) { $uow = $event->getEntityManager()->getUnitOfWork(); foreach($uow->getScheduledEntityUpdates() as $model) { $changeset = $uow->getEntityChangeSet($model); $this->formatAllPrettyInMail($model, $changeset); } }
  • 92. Listener public function onFlush($event) { $uow = $event->getEntityManager()->getUnitOfWork(); foreach($uow->getScheduledEntityUpdates() as $model) { $changeset = $uow->getEntityChangeSet($model); $this->formatAllPrettyInMail($model, $changeset); } } public function postFlush($event) { if (!$this->hasMessage()) { return; } $this->mailTheBoss($this->message); }
  • 93. That's really all there is to it.
  • 94. Listener public function formatAllPrettyInMail($model, $changes) { if (!$model instanceof Article) { return; } $msg = ""; foreach($changes as $field => $values) { $msg .= "{$field} ----- n". "old: {$values[0]} n". "new: {$values[1]} nn"; } $this->mailMessage .= $msg; }
  • 95. Advice: Treat your listeners like controllers
  • 98.
  • 99. TPS Request Write the change messages to the database instead of emailing them.
  • 100. Model /** @Entity */ class ChangeLog { /** @Id @GeneratedValue * @Column(type="integer") */ protected $id; /** @Column(type="text") */ protected $description; }
  • 101. Listener class ChangeLogger implements EventSubscriber { public function getSubscribedEvents() { return array(Events::onFlush); } public function onFlush($event) { } }
  • 102. Listener::onFlush public function onFlush($event) { $em = $event->getEntityManager(); $uow = $em->getUnitOfWork(); foreach($uow->getScheduledEntityUpdates() as $model) { if (!$model instanceof Article) { continue; } $log = new ChangeLog(); $log->setDescription($this->meFormatPrettyOneDay()); $em->persist($log); $uow->computeChangeSet( $em->getClassMetadata('ChangeLog'), $log ); }
  • 103. Shiny
  • 104. Yes, I really used PHPMyAdmin there
  • 105. Shiny
  • 106. Also, wow, it worked!
  • 107.
  • 108. UnitOfWork API $uow->getScheduledEntityInsertions(); $uow->getScheduledEntityUpdates(); $uow->getScheduledEntityDeletions(); $uow->getScheduledCollectionUpdates(); $uow->getScheduledCollectionDeletions();
  • 109. UnitOfWork API $uow->scheduleForInsert(); $uow->scheduleForUpdate(); $uow->scheduleExtraUpdate(); $uow->scheduleForDelete();
  • 110. UnitOfWork API And many more...
  • 112.
  • 113. Model /** @Entity */ class Article { //... /** @Column(type="integer") */ protected $viewCount; }
  • 114.
  • 115. + MySQL Redis
  • 116. TPS Request Move view counts out of the database into a faster system. And don't break everything.
  • 117. Controller $article = $em->find('Article', 1); echo $article->getViewCount();
  • 118. Model /** @Entity */ class Article { //... /** @Column(type="integer") */ protected $viewCount; }
  • 119. Listener class ViewCountListener implements EventSubscriber { public function getSubscribedEvents() { return DoctrineORMEvents::postLoad; } public function postLoad($event) { $model = $event->getEntity(); if (!$model instanceof Article) { return; } $currentRank = $this->getCountFromRedis($model); $model->setViewCount($currentRank); } }
  • 120. Controller $article = $em->find('Article', 1); echo $article->getViewCount();
  • 123. Model /** @Entity */ class Article { /** @Id @GeneratedValue * @Column(type="integer") */ protected $id; Where do /** @Column(type="string") */ they go? protected $title; /** @Column(type="datetime") */ protected $updatedAt; }
  • 124. Model /** @Entity */ class Article { /** @Id @GeneratedValue * @Column(type="integer") */ protected $id; /** @Column(type="string") */ protected $title; /** @Column(type="datetime") */ protected $updatedAt; }
  • 126. ??? $metadata = $em->getClassMetadata('Article');
  • 127. ??? $metadata = $em->getClassMetadata('Article'); echo $metadata->getTableName(); Output: Article
  • 128. ??? $metadata = $em->getClassMetadata('Article'); echo $metadata->getTableName(); Output: articles
  • 129. Doctrine 2.3 will bring NamingStrategy
  • 130. ??? $conn = $em->getConnection(); $tableName = $metadata->getQuotedTableName($conn); $results = $conn->query('SELECT * FROM '.$tableName);
  • 131. Tip of the Iceberg
  • 132. ??? $metadata->getFieldMapping('title'); array(8) { fieldName => "title" type => "string" length => NULL precision => 0 scale => 0 nullable => false unique => false columnName => "title" }
  • 133. ??? $metadata->getFieldMapping('title'); $metadata->getFieldNames(); $metadata->getReflectionClass(); $metadata->getReflectionProperty('title'); $metadata->getColumnName('title'); $metadata->getTypeOfField('title');
  • 136. Form in Symfony2 class ArticleType extends AbstractType { public function buildForm($builder, array $options) { $builder ->add('title') ->add('content'); } }
  • 137. Controller in Symfony2 $entity = new Article(); $form = $this->createForm(new ArticleType(), $entity);
  • 138.
  • 139. Form in Symfony2 class ArticleType extends AbstractType { public function buildForm($builder, array $options) { $builder ->add('title') ->add('content'); } }
  • 140.
  • 141. Symfony Doctrine Bridge switch ($metadata->getTypeOfField($property)) { case 'string': return new TypeGuess('text', ...); case 'boolean': return new TypeGuess('checkbox', ...); case 'integer': case 'bigint': case 'smallint': return new TypeGuess('integer', ...); case 'text': return new TypeGuess('textarea', ...); default: return new TypeGuess('text', ...); }
  • 144. ??? $metadata->getAssociationMapping('comments'); array(15) { fieldName => "comments" mappedBy => "article" targetEntity => "Comment" cascade => array(0) {} fetch => 2 type => 4 isOwningSide => false sourceEntity => "Article" ...
  • 145. ??? $metadata->getAssociationMapping('comments'); $metadata->getAssociationMappings(); $metadata->isSingleValuedAssociation('comments'); $metadata->isCollectionValuedAssociation('comments'); $metadata->getAssociationTargetClass('comments');
  • 146. Form in Symfony2 class ArticleType extends AbstractType { public function buildForm($builder, array $options) { $builder ->add('title') ->add('content') ->add('comments'); } }
  • 147.
  • 149. You can set all of this stuff.
  • 151. But is there a good reason? Unless you're writing a driver, probably not.
  • 152. Listener class MyListener { public function loadClassMetadata($event) { echo $event->getClassMetadata()->getName(); } }
  • 162. Test
  • 164. test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test
  • 165. RTFM.
  • 166. Go forth and rock.
  • 167.
  • 170. Wikipedia OwnMoment (sxc.hu)