Extending Doctrine 2For Your Domain Model                     Ross Tuck                  June 9th, DPC
Who Am I?
Ross Tuck
Team Lead at IbuildingsCodemonkeyREST nutHat guyToken Foreigner America,               &@* ! Yeah
@rosstuck#dashinglyHandsome
Quick But Necessary
Doctrine 101
Doctrine 102
Doctrine 102.5
Model  /** @Entity */  class Article {        /** @Id @GeneratedValue         *   @Column(type="integer") */        protec...
Controller   $article = $em->find(Article, 1);   $article->setTitle(Awesome New Story);   $em->flush();
Filters
TPS RequestWe need a way to approve commentsbefore they appear to all users.
Controller   $comments = $em->getRepository(Comment)->findAll();   foreach($comments as $comment) {        echo $comment->...
Controller   $comments = $em->getRepository(Comment)->findAll();   foreach($comments as $comment) {        echo $comment->...
Controller   $comments = $em->createQuery(SELECT c FROM Comment c);   foreach($comments->getResult() as $comment) {       ...
Controller   $comments = $em->find(Article, 1)->getComments();   foreach($comments as $comment) {        echo $comment->ge...
FiltersNew in 2.2
Filter  use DoctrineORMQueryFilterSQLFilter;  class CommentFilter extends SQLFilter {         public function addFilterCon...
Bootstrap                         y               Ha ndy ke   $em->getConfiguration()       ->addFilter(approved_comments,...
Bootstrap   $em->getConfiguration()       ->addFilter(approved_comments, CommentFilter);   $em->getFilters()->enable(appro...
Bootstrap   if ($this->isNormalUser()) {        $em->getConfiguration()            ->addFilter(approved_comments, CommentF...
Controller   $comments = $em->getRepository(Comment)->findAll();   foreach($comments as $comment) {        echo $comment->...
Controller   $comments = $em->getRepository(Comment)->findAll();   foreach($comments as $comment) {        echo $comment->...
Parameters
Bootstrap   $filter = $em->getFilters()->getFilter(approved_comments);   $filter->setParameter(level, $this->getUserLevel(...
Filter  use DoctrineORMQueryFilterSQLFilter;  class CommentFilter extends SQLFilter {         public function addFilterCon...
Filter  use DoctrineORMQueryFilterSQLFilter;  class CommentFilter extends SQLFilter {         public function addFilterCon...
Filter  use DoctrineORMQueryFilterSQLFilter;  class CommentFilter extends SQLFilter {         public function addFilterCon...
Limitations
Events
Inser          tprePersist    postLoadpostPersist   loadClassMetadatapreUpdate     preFlushpostUpdate    onFlushpreRemove ...
Callbacks        ListenersOn the model   External objects
Lifecycle Callbacks
Model  /** @Entity */  class Article {        /** @Id @GeneratedValue         *   @Column(type="integer") */        protec...
Controller   $article = $em->find(Article, 1);   $article->setTitle(Awesome New Story);   $em->flush();
TPS RequestArticles must record the last date theywere modified in any way.
Controller   $article = $em->find(Article, 1);   $article->setTitle(Awesome New Story);   $article->setUpdatedAt(new DateT...
Model  /** @Entity */  class Article {        /** @Id @GeneratedValue        *   @Column(type="integer") */        protect...
Model  /** @Entity */  class Article {        /** @Id @GeneratedValue        *   @Column(type="integer") */        protect...
Model  /** @Entity */  class Article {        // ...        public function markAsUpdated() {            $this->updatedAt ...
Model  /** @Entity @HasLifecycleCallbacks */  class Article {        // ...                                            Eve...
Controller   $article = $em->find(Article, 1);   $article->setTitle(Awesome New Story);      No                           ...
Model  /** @Entity @HasLifecycleCallbacks */  class Article {        // ...        /** @PreUpdate */        public functio...
What else can I do?
prePersist    postLoadpostPersist   loadClassMetadatapreUpdate     preFlushpostUpdate    onFlushpreRemove     postFlushpos...
prePersist    postLoadpostPersist   loadClassMetadatapreUpdate     preFlushpostUpdate    onFlushpreRemove     postFlushpos...
Protip:Only fires if youre dirty
new MudWrestling();
Controller   $article = $em->find(Article, 1);   $article->setTitle(Awesome New Story);   $em->flush();   // 2012-05-29T10...
Controller   $article = $em->find(Article, 1);   $article->setTitle(Awesome New Story);   $em->flush();             Awesom...
Controller   $article = $em->find(Article, 1);   $article->setTitle(Fantabulous Updated Story);   $em->flush();     Awesom...
Controller   $article = $em->find(Article, 1);   $article->setTitle(Awesome New Project.uniqid());   $em->flush();
So, heres the thing.
The Thing
Events are cool.
Callbacks?
Eeeeeeeeeeeeeeeeeeh.
•   Limited to model dependencies•   Do you really need that function?•   Protected function? Silent error•   Repeated Cod...
Listeners
Listener   class UpdateTimeListener {           public function preUpdate($event) {           }   }
Bootstrap   $em->getEventManager()->addEventListener(        array(DoctrineORMEvents::preUpdate),        new UpdateTimeLis...
Invert
Invert
Bootstrap   $em->getEventManager()->addEventListener(        array(DoctrineORMEvents::preUpdate),        new UpdateTimeLis...
Bootstrap   $em->getEventManager()->addEventSubscriber(        new UpdateTimeListener()   );
Listener   class UpdateTimeListener {           public function preUpdate($event) {           }           public function ...
Listener   use DoctrineCommonEventSubscriber;   class UpdateTimeListener implements EventSubscriber {           public fun...
Functionally?   No differenceDesign?         Subscriber
Bootstrap   $em->getEventManager()->addEventSubscriber(        new ChangeMailListener()   );
Bootstrap   $em->getEventManager()->addEventSubscriber(        new ChangeMailListener($mailer)   );
Listener   class UpdateTimeListener implements EventSubscriber {           public function getSubscribedEvents() {        ...
Listener   public function preUpdate($event) {           $em = $event->getEntityManager();           $model = $event->getE...
Controller   $article = $em->find(Article, 1);   $article->setTitle(Awesome New Story);   $em->flush();   echo $article->g...
Whoopity-doo
Theory Land   interface LastUpdatedInterface {        public function setUpdatedAt(Datetime $date);        public function...
Listener   public function preUpdate($event) {           $em = $event->getEntityManager();           $model = $event->getE...
Listener   public function preUpdate($event) {           $em = $event->getEntityManager();           $model = $event->getE...
Listener   public function preUpdate($event) {           $em = $event->getEntityManager();           $model = $event->getE...
The POWAH
Flushing And Multiple Events
OnFlush
TPS RequestAfter every update to an article, I wantan email of the changes sent to me.Also, bring me more lettuce.
Listener   class ChangeMailListener implements EventSubscriber {           protected $mailMessage;           public functi...
Listener   public function onFlush($event) {           $uow = $event->getEntityManager()->getUnitOfWork();           forea...
array(1) {    ["title"]=>        array(2) {          [0]=> string(16) "Boring Old Title"          [1]=> string(16) "Great ...
Listener   public function onFlush($event) {           $uow = $event->getEntityManager()->getUnitOfWork();           forea...
Listener   public function onFlush($event) {           $uow = $event->getEntityManager()->getUnitOfWork();           forea...
Thats really all there is to it.
Listener   public function formatAllPrettyInMail($model, $changes) {           if (!$model instanceof Article) {          ...
Advice:Treat your listeners like controllers
Keep it thin
Crazy Town
TPS RequestWrite the change messages to thedatabase instead of emailing them.
Model  /** @Entity */  class ChangeLog {        /** @Id @GeneratedValue        *   @Column(type="integer") */        prote...
Listener   class ChangeLogger implements EventSubscriber {           public function getSubscribedEvents() {              ...
Listener::onFlush   public function onFlush($event) {        $em = $event->getEntityManager();        $uow = $em->getUnitO...
Shiny
Yes, I really used PHPMyAdmin there
Shiny
Also, wow, it worked!
UnitOfWork API                 $uow->getScheduledEntityInsertions();                 $uow->getScheduledEntityUpdates();   ...
UnitOfWork API                 $uow->scheduleForInsert();                 $uow->scheduleForUpdate();                 $uow-...
UnitOfWork API                 And many more...
postLoad
Model  /** @Entity */  class Article {        //...        /** @Column(type="integer") */        protected $viewCount;  }
+MySQL       Redis
TPS RequestMove view counts out of the databaseinto a faster system.And dont 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() {         ...
Controller   $article = $em->find(Article, 1);   echo $article->getViewCount();
Many, many other uses.
Class Metadata
Model  /** @Entity */  class Article {        /** @Id @GeneratedValue        *   @Column(type="integer") */        protect...
Model  /** @Entity */  class Article {        /** @Id @GeneratedValue        *   @Column(type="integer") */        protect...
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->quer...
Tip of the Iceberg
???      $metadata->getFieldMapping(title);array(8) {  fieldName     =>   "title"  type          =>   "string"  length    ...
???      $metadata->getFieldMapping(title);      $metadata->getFieldNames();      $metadata->getReflectionClass();      $m...
Simple Example
Symple Example
Form in Symfony2   class ArticleType extends AbstractType {        public function buildForm($builder, array $options) {  ...
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) {  ...
Symfony Doctrine Bridge  switch ($metadata->getTypeOfField($property)) {       case string:            return new TypeGues...
Cool, huh?
Cool, huh?
???      $metadata->getAssociationMapping(comments);array(15) {  fieldName        =>   "comments"  mappedBy         =>   "...
???      $metadata->getAssociationMapping(comments);      $metadata->getAssociationMappings();      $metadata->isSingleVal...
Form in Symfony2   class ArticleType extends AbstractType {        public function buildForm($builder, array $options) {  ...
Oh yeah.
You can set all of this stuff.
100~ public functions
But is there a good reason?Unless youre writing a driver, probably not.
Listener   class MyListener {           public function loadClassMetadata($event) {               echo $event->getClassMet...
Wheres the manatee?
Youre the manatee.
Epilogue & Service Layers
ModelController  View
ModelService Layer Controller    View
Be smart.
Be smart.
Be simple
Dont overuse this.
Test
Test test test
test test test test test test test test test testtest test test test test test test test test testtest test test test test...
RTFM.
Go forth and rock.
Youre the manatee.
Thanks to:@boekkooi#doctrine (freenode)
Wikipedia            OwnMoment (sxc.hu)
https://joind.in/6251
Extending Doctrine 2 for your Domain Model
Extending Doctrine 2 for your Domain Model
Extending Doctrine 2 for your Domain Model
Extending Doctrine 2 for your Domain Model
Extending Doctrine 2 for your Domain Model
Extending Doctrine 2 for your Domain Model
Extending Doctrine 2 for your Domain Model
Extending Doctrine 2 for your Domain Model
Extending Doctrine 2 for your Domain Model
Extending Doctrine 2 for your Domain Model
Extending Doctrine 2 for your Domain Model
Extending Doctrine 2 for your Domain Model
Upcoming SlideShare
Loading in...5
×

Extending Doctrine 2 for your Domain Model

12,283

Published on

UPDATE: My thoughts have changed a bit since I originally presented this talk. It's could still be a useful intro to some of Doctrine's intermediate features (although the docs continue to improve. However, I would recommend you take a look at a new talk I've done since then "Models & Service Layers; Hemoglobin & Hobgoblins" which I think does a better job of actually talking about architecture and domain models than this talk does. Thanks! http://www.slideshare.net/rosstuck/models-and-service-layers-hemoglobin-and-hobgoblins

As presented at the Dutch PHP Conference 2012.

Sure, it can save models and do relations but Doctrine 2 can do a whole lot more. This talk introduces features like Events and Filters that can keep your code clean and organized. If you like the idea of automatically updating your solr index or adding audit logs without changing any existing code, this is the talk for you.

The emphasis is on practical application of the lesser known but powerful features. The talk is geared toward people who are curious what Doctrine 2 can offer over a standard database layer or have used it previously.

2 Comments
46 Likes
Statistics
Notes
  • @HakanAkta All of the images seem to be in place, the arrow on slide #114 was part of a joke in the presentation where it was supposed to point to the speaker (me). Thanks for pointing it out though!
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • I think the some of the images in the slides are remote and I think they have deleted. For example slide #114 only has an arrow.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total Views
12,283
On Slideshare
0
From Embeds
0
Number of Embeds
7
Actions
Shares
0
Downloads
135
Comments
2
Likes
46
Embeds 0
No embeds

No notes for slide

Extending Doctrine 2 for your Domain Model

  1. 1. Extending Doctrine 2For Your Domain Model Ross Tuck June 9th, DPC
  2. 2. Who Am I?
  3. 3. Ross Tuck
  4. 4. Team Lead at IbuildingsCodemonkeyREST nutHat guyToken Foreigner America, &@* ! Yeah
  5. 5. @rosstuck#dashinglyHandsome
  6. 6. Quick But Necessary
  7. 7. Doctrine 101
  8. 8. Doctrine 102
  9. 9. Doctrine 102.5
  10. 10. Model /** @Entity */ class Article { /** @Id @GeneratedValue * @Column(type="integer") */ protected $id; /** @Column(type="string") */ protected $title; /** @Column(type="text") */ protected $content; }
  11. 11. Controller $article = $em->find(Article, 1); $article->setTitle(Awesome New Story); $em->flush();
  12. 12. Filters
  13. 13. TPS RequestWe need a way to approve commentsbefore they appear to all users.
  14. 14. Controller $comments = $em->getRepository(Comment)->findAll(); foreach($comments as $comment) { echo $comment->getContent()."n"; }Output:Doug isnt humanDoug is the best
  15. 15. Controller $comments = $em->getRepository(Comment)->findAll(); foreach($comments as $comment) { echo $comment->getContent()."n"; }
  16. 16. Controller $comments = $em->createQuery(SELECT c FROM Comment c); foreach($comments->getResult() as $comment) { echo $comment->getContent()."n"; }
  17. 17. Controller $comments = $em->find(Article, 1)->getComments(); foreach($comments as $comment) { echo $comment->getContent()."n"; } ! ce s in your code Approx 100 pla
  18. 18. FiltersNew in 2.2
  19. 19. Filter use DoctrineORMQueryFilterSQLFilter; class CommentFilter extends SQLFilter { public function addFilterConstraint($entityInfo, $alias) { if ($entityInfo->name !== Comment) { return ""; } return $alias.".approved = 1"; } }
  20. 20. Bootstrap y Ha ndy ke $em->getConfiguration() ->addFilter(approved_comments, CommentFilter); Class name
  21. 21. Bootstrap $em->getConfiguration() ->addFilter(approved_comments, CommentFilter); $em->getFilters()->enable(approved_comments);
  22. 22. Bootstrap if ($this->isNormalUser()) { $em->getConfiguration() ->addFilter(approved_comments, CommentFilter); $em->getFilters()->enable(approved_comments); }
  23. 23. Controller $comments = $em->getRepository(Comment)->findAll(); foreach($comments as $comment) { echo $comment->getContent()."n"; } al UserOutput: A s NormDoug is the best
  24. 24. Controller $comments = $em->getRepository(Comment)->findAll(); foreach($comments as $comment) { echo $comment->getContent()."n"; } inOutput: As the AdmDoug isnt humanDoug is the best
  25. 25. Parameters
  26. 26. Bootstrap $filter = $em->getFilters()->getFilter(approved_comments); $filter->setParameter(level, $this->getUserLevel());
  27. 27. 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"; } }
  28. 28. 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; } }
  29. 29. 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; } }
  30. 30. Limitations
  31. 31. Events
  32. 32. Inser tprePersist postLoadpostPersist loadClassMetadatapreUpdate preFlushpostUpdate onFlushpreRemove postFlushpostRemove onClear
  33. 33. Callbacks ListenersOn the model External objects
  34. 34. Lifecycle Callbacks
  35. 35. Model /** @Entity */ class Article { /** @Id @GeneratedValue * @Column(type="integer") */ protected $id; /** @Column(type="string") */ protected $title; /** @Column(type="text") */ protected $content; }
  36. 36. Controller $article = $em->find(Article, 1); $article->setTitle(Awesome New Story); $em->flush();
  37. 37. TPS RequestArticles must record the last date theywere modified in any way.
  38. 38. Controller $article = $em->find(Article, 1); $article->setTitle(Awesome New Story); $article->setUpdatedAt(new DateTime(now)); + 100 other files $em->flush(); + testing + missed bugs
  39. 39. Model /** @Entity */ class Article { /** @Id @GeneratedValue * @Column(type="integer") */ protected $id; /** @Column(type="string") */ protected $title; }
  40. 40. Model /** @Entity */ class Article { /** @Id @GeneratedValue * @Column(type="integer") */ protected $id; /** @Column(type="string") */ protected $title; /** @Column(type="datetime") */ protected $updatedAt; }
  41. 41. Model /** @Entity */ class Article { // ... public function markAsUpdated() { $this->updatedAt = new Datetime(now); } }
  42. 42. Model /** @Entity @HasLifecycleCallbacks */ class Article { // ... Event mapping /** @PreUpdate */ public function markAsUpdated() { $this->updatedAt = new Datetime(now); } }
  43. 43. 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
  44. 44. Model /** @Entity @HasLifecycleCallbacks */ class Article { // ... /** @PreUpdate */ public function markAsUpdated() { $this->updatedAt = new Datetime(now); } }
  45. 45. What else can I do?
  46. 46. prePersist postLoadpostPersist loadClassMetadatapreUpdate preFlushpostUpdate onFlushpreRemove postFlushpostRemove onClear
  47. 47. prePersist postLoadpostPersist loadClassMetadatapreUpdate preFlushpostUpdate onFlushpreRemove postFlushpostRemove onClear
  48. 48. Protip:Only fires if youre dirty
  49. 49. new MudWrestling();
  50. 50. 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
  51. 51. Controller $article = $em->find(Article, 1); $article->setTitle(Awesome New Story); $em->flush(); Awesome New Story === Awesome New Story No update
  52. 52. Controller $article = $em->find(Article, 1); $article->setTitle(Fantabulous Updated Story); $em->flush(); Awesome New Story !== Fantabulous Updated Story Update, yeah!
  53. 53. Controller $article = $em->find(Article, 1); $article->setTitle(Awesome New Project.uniqid()); $em->flush();
  54. 54. So, heres the thing.
  55. 55. The Thing
  56. 56. Events are cool.
  57. 57. Callbacks?
  58. 58. Eeeeeeeeeeeeeeeeeeh.
  59. 59. • Limited to model dependencies• Do you really need that function?• Protected function? Silent error• Repeated Code• Performance implications
  60. 60. Listeners
  61. 61. Listener class UpdateTimeListener { public function preUpdate($event) { } }
  62. 62. Bootstrap $em->getEventManager()->addEventListener( array(DoctrineORMEvents::preUpdate), new UpdateTimeListener() );
  63. 63. Invert
  64. 64. Invert
  65. 65. Bootstrap $em->getEventManager()->addEventListener( array(DoctrineORMEvents::preUpdate), new UpdateTimeListener() );
  66. 66. Bootstrap $em->getEventManager()->addEventSubscriber( new UpdateTimeListener() );
  67. 67. Listener class UpdateTimeListener { public function preUpdate($event) { } public function getSubscribedEvents() { return array(Events::preUpdate); } }
  68. 68. Listener use DoctrineCommonEventSubscriber; class UpdateTimeListener implements EventSubscriber { public function preUpdate($event) { } public function getSubscribedEvents() { return array(Events::preUpdate); } }
  69. 69. Functionally? No differenceDesign? Subscriber
  70. 70. Bootstrap $em->getEventManager()->addEventSubscriber( new ChangeMailListener() );
  71. 71. Bootstrap $em->getEventManager()->addEventSubscriber( new ChangeMailListener($mailer) );
  72. 72. Listener class UpdateTimeListener implements EventSubscriber { public function getSubscribedEvents() { return array(Events::preUpdate); } public function preUpdate($event) { } }
  73. 73. 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 ); }
  74. 74. 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
  75. 75. Whoopity-doo
  76. 76. Theory Land interface LastUpdatedInterface { public function setUpdatedAt(Datetime $date); public function getUpdatedAt(); }
  77. 77. 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 ); }
  78. 78. 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 ); }
  79. 79. 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 ); }
  80. 80. The POWAH
  81. 81. Flushing And Multiple Events
  82. 82. OnFlush
  83. 83. TPS RequestAfter every update to an article, I wantan email of the changes sent to me.Also, bring me more lettuce.
  84. 84. Listener class ChangeMailListener implements EventSubscriber { protected $mailMessage; public function getSubscribedEvents() { return array(Events::onFlush, Events::postFlush); } }
  85. 85. Listener public function onFlush($event) { $uow = $event->getEntityManager()->getUnitOfWork(); foreach($uow->getScheduledEntityUpdates() as $model) { $changeset = $uow->getEntityChangeSet($model); } }
  86. 86. array(1) { ["title"]=> array(2) { [0]=> string(16) "Boring Old Title" [1]=> string(16) "Great New Title!" }}
  87. 87. Listener public function onFlush($event) { $uow = $event->getEntityManager()->getUnitOfWork(); foreach($uow->getScheduledEntityUpdates() as $model) { $changeset = $uow->getEntityChangeSet($model); $this->formatAllPrettyInMail($model, $changeset); } }
  88. 88. 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); }
  89. 89. Thats really all there is to it.
  90. 90. 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; }
  91. 91. Advice:Treat your listeners like controllers
  92. 92. Keep it thin
  93. 93. Crazy Town
  94. 94. TPS RequestWrite the change messages to thedatabase instead of emailing them.
  95. 95. Model /** @Entity */ class ChangeLog { /** @Id @GeneratedValue * @Column(type="integer") */ protected $id; /** @Column(type="text") */ protected $description; }
  96. 96. Listener class ChangeLogger implements EventSubscriber { public function getSubscribedEvents() { return array(Events::onFlush); } public function onFlush($event) { } }
  97. 97. 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 ); }
  98. 98. Shiny
  99. 99. Yes, I really used PHPMyAdmin there
  100. 100. Shiny
  101. 101. Also, wow, it worked!
  102. 102. UnitOfWork API $uow->getScheduledEntityInsertions(); $uow->getScheduledEntityUpdates(); $uow->getScheduledEntityDeletions(); $uow->getScheduledCollectionUpdates(); $uow->getScheduledCollectionDeletions();
  103. 103. UnitOfWork API $uow->scheduleForInsert(); $uow->scheduleForUpdate(); $uow->scheduleExtraUpdate(); $uow->scheduleForDelete();
  104. 104. UnitOfWork API And many more...
  105. 105. postLoad
  106. 106. Model /** @Entity */ class Article { //... /** @Column(type="integer") */ protected $viewCount; }
  107. 107. +MySQL Redis
  108. 108. TPS RequestMove view counts out of the databaseinto a faster system.And dont break everything.
  109. 109. Controller $article = $em->find(Article, 1); echo $article->getViewCount();
  110. 110. Model /** @Entity */ class Article { //... /** @Column(type="integer") */ protected $viewCount; }
  111. 111. 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); } }
  112. 112. Controller $article = $em->find(Article, 1); echo $article->getViewCount();
  113. 113. Many, many other uses.
  114. 114. Class Metadata
  115. 115. 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; }
  116. 116. Model /** @Entity */ class Article { /** @Id @GeneratedValue * @Column(type="integer") */ protected $id; /** @Column(type="string") */ protected $title; /** @Column(type="datetime") */ protected $updatedAt; }
  117. 117. DoctrineORMMappingClassMetadata
  118. 118. ??? $metadata = $em->getClassMetadata(Article);
  119. 119. ??? $metadata = $em->getClassMetadata(Article); echo $metadata->getTableName();Output:Article
  120. 120. ??? $metadata = $em->getClassMetadata(Article); echo $metadata->getTableName();Output:articles
  121. 121. Doctrine 2.3 will bring NamingStrategy
  122. 122. ??? $conn = $em->getConnection(); $tableName = $metadata->getQuotedTableName($conn); $results = $conn->query(SELECT * FROM .$tableName);
  123. 123. Tip of the Iceberg
  124. 124. ??? $metadata->getFieldMapping(title);array(8) { fieldName => "title" type => "string" length => NULL precision => 0 scale => 0 nullable => false unique => false columnName => "title"}
  125. 125. ??? $metadata->getFieldMapping(title); $metadata->getFieldNames(); $metadata->getReflectionClass(); $metadata->getReflectionProperty(title); $metadata->getColumnName(title); $metadata->getTypeOfField(title);
  126. 126. Simple Example
  127. 127. Symple Example
  128. 128. Form in Symfony2 class ArticleType extends AbstractType { public function buildForm($builder, array $options) { $builder ->add(title) ->add(content); } }
  129. 129. Controller in Symfony2 $entity = new Article(); $form = $this->createForm(new ArticleType(), $entity);
  130. 130. Form in Symfony2 class ArticleType extends AbstractType { public function buildForm($builder, array $options) { $builder ->add(title) ->add(content); } }
  131. 131. 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, ...); }
  132. 132. Cool, huh?
  133. 133. Cool, huh?
  134. 134. ??? $metadata->getAssociationMapping(comments);array(15) { fieldName => "comments" mappedBy => "article" targetEntity => "Comment" cascade => array(0) {} fetch => 2 type => 4 isOwningSide => false sourceEntity => "Article" ...
  135. 135. ??? $metadata->getAssociationMapping(comments); $metadata->getAssociationMappings(); $metadata->isSingleValuedAssociation(comments); $metadata->isCollectionValuedAssociation(comments); $metadata->getAssociationTargetClass(comments);
  136. 136. Form in Symfony2 class ArticleType extends AbstractType { public function buildForm($builder, array $options) { $builder ->add(title) ->add(content) ->add(comments); } }
  137. 137. Oh yeah.
  138. 138. You can set all of this stuff.
  139. 139. 100~ public functions
  140. 140. But is there a good reason?Unless youre writing a driver, probably not.
  141. 141. Listener class MyListener { public function loadClassMetadata($event) { echo $event->getClassMetadata()->getName(); } }
  142. 142. Wheres the manatee?
  143. 143. Youre the manatee.
  144. 144. Epilogue & Service Layers
  145. 145. ModelController View
  146. 146. ModelService Layer Controller View
  147. 147. Be smart.
  148. 148. Be smart.
  149. 149. Be simple
  150. 150. Dont overuse this.
  151. 151. Test
  152. 152. Test test test
  153. 153. test test test test test test test test test testtest test test test test test test test test testtest test test test test test test test test testtest test test test test test test test test testtest test test test test test test test test testtest test test test test test test test test testtest test test test test test test test test testtest test test test test test test test test testtest test test test test test test test test testtest test test test test test test test test testtest test test test test test test test test test
  154. 154. RTFM.
  155. 155. Go forth and rock.
  156. 156. Youre the manatee.
  157. 157. Thanks to:@boekkooi#doctrine (freenode)
  158. 158. Wikipedia OwnMoment (sxc.hu)
  159. 159. https://joind.in/6251
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×