What is new in Doctrine?

D07a7a143b14fc8309f9abb78d569344?s=47 Alexander
November 23, 2012

What is new in Doctrine?

Doctrine follows a six monthly release schedule and adds a considerable amount of new features in new release. This talk discusses some of the more recent features and how they help you build applications with Symfony2.

D07a7a143b14fc8309f9abb78d569344?s=128

Alexander

November 23, 2012
Tweet

Transcript

  1. What is new in Doctrine? Benjamin Eberlei & Alexander Mols

    Symfony Live Berlin 2012
  2. Benjamin Benjamin Eberlei SimpleThings GmbH Doctrine Core Developer github.com/beberlei twitter.com/beberlei

  3. Alexander Alexander Mols Technical Lead Developer @ VONQ Doctrine Core

    Developer github.com/asm89 twitter.com/iam_asm89
  4. The big picture • 6 month release schedule • 2.4

    on the horizon • Increased velocity since 2.0 • Growing team • Stabilizing and improving minor details
  5. New features • 2.2 and 2.3 added tons of features

    • Hard to keep up with rate of change • Documentation fail on our part
  6. Filter API http://www.flickr.com/photos/giantsqurl/4983106796/

  7. Filter API 1 <?php 2 use Doctrine\ORM\Mapping\ClassMetaData; 3 use Doctrine\ORM\Query\Filter\SQLFilter;

    4 5 class MyLocaleFilter extends SQLFilter 6 { 7 public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias) 8 { 9 if (!in_array("LocaleAware", $targetEntity->reflClass->getInterfaceNames())) { 10 return ""; 11 } 12 13 return $targetTableAlias . '.locale = ' . $this->getParameter('locale'); 14 } 15 }
  8. Filter API 1 <?php 2 // enable and set parameter

    3 $filter = $entityManager->getFilters()->enable('locale'); 4 $filter->setParameter('locale', 'en'); 5 6 // disable a filter 7 $filter = $entityManager->getFilters()->disable('softdelete');
  9. Filter API 1 # entity manager configuration 2 filters: 3

    locale: 4 class: Acme\MyLocaleFilter 5 softdelete: 6 class: Acme\MySoftdeleteFilter 7 enabled: true
  10. Complex SQL Types http://www.flickr.com/photos/garrettc/2172850095/

  11. Complex SQL Types 1 <?php 2 3 class IpV4AddressType extends

    Type 4 { 5 public function canRequireSQLConversion() 6 { 7 return true; 8 } 9 10 public function convertToPHPValueSQL($sqlExpr, $platform) 11 { 12 return "INET_NTOA('" .$sqlExpr. "')"; 13 } 14 15 public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) 16 { 17 return 'INET_ATON('.$sqlExpr.')'; 18 } 19 }
  12. Flush Optimizations http://www.flickr.com/photos/elycefeliz/5717273714/

  13. 1 <?php 2 $user = $entityManager->find("User", 1); 3 $group =

    $user->getGroup(); 4 5 // do something with $user and $group 6 7 // single flush 8 $entityManager->flush($user); 9 10 // set flush 11 $entityManager->flush(array($user, $group));
  14. Paginator http://www.flickr.com/photos/horiavarlan/4332381194/

  15. Paginator • Merger of three libraries: – Doctrine extensions –

    Pagerfanta – KnpLabs pager • Used in various bundles now
  16. 1 <?php 2 use Doctrine\ORM\Tools\Pagination\Paginator; 3 4 $dql = "SELECT

    p, c FROM BlogPost p JOIN p.comments c"; 5 $query = $entityManager->createQuery($dql) 6 ->setFirstResult(0) 7 ->setMaxResults(100); 8 9 $paginator = new Paginator($query); 10 11 echo 'Found ' . count($paginator) . ' posts!' . "\n"; 12 foreach ($paginator as $post) { 13 echo $post->getHeadline() . "\n"; 14 }
  17. DQL Improvements

  18. DQL Improvements 1 <?php 2 $dql = <<<DQL 3 SELECT

    4 p AS post, 5 COUNT(p.comments) AS comment_count 6 FROM 7 Post p 8 DQL; 9 10 $results = $entityManager->createQuery($dql)->getResult(); 11 12 foreach ($results as $row) { 13 $post = $row['post']; 14 $count = $row['comment_count']; 15 }
  19. DQL Improvements 1 <?php 2 $dql = <<<DQL 3 SELECT

    4 p, 5 COUNT(p.comments) AS HIDDEN comment_count 6 FROM 7 Post p 8 ORDER BY 9 comment_count 10 DQL;
  20. DQL Improvements • Bit comparisons • New CASE expression •

    New IDENTITY function • Sort by associations
  21. Master-Slave Connection

  22. Master-Slave Connection • Define many slaves and one master •

    All reads go to a random slave • Until one write statement is issued • Then every read goes to master • Selectively change back to slave
  23. Target Entity Resolving

  24. Target Entity Resolving 1 <?php 2 3 /** 4 *

    @ORM\Entity 5 */ 6 class Post 7 { 8 /** 9 * @ORM\ManyToOne(targetEntity="Symfony\...\UserInterface") 10 */ 11 private $user; 12 } 1 # doctrine configuration 2 orm: 3 resolve_target_entities: 4 Symfony\Component\Security\Core\User\UserInterface: MyUserClass
  25. None
  26. Arbitrary Joins

  27. Arbitrary Joins 1 SELECT 2 u.name, a.title 3 FROM 4

    User u 5 JOIN 6 Article a 7 WITH 8 a.user = u.id 1 SELECT 2 u.name, a.title 3 FROM 4 User u 5 JOIN 6 u.article a
  28. Arbitrary Joins 1 SELECT 2 u.name, a.title 3 FROM 4

    User u 5 JOIN 6 Article a 7 WITH 8 a.user = u.id 1 SELECT 2 u.name, a.title 3 FROM 4 User u 5 JOIN 6 u.article a
  29. Collection Criteria

  30. Collection Criteria Filter associations without DQL: 1 <?php 2 use

    Doctrine\Common\Collections\Criteria; 3 4 $userCollection = $group->getUsers(); 5 6 $criteria = Criteria::create() 7 ->where(Criteria::expr()->eq("birthday", "11-23")) 8 ->orderBy(array("username" => "ASC")) 9 ->setFirstResult(0) 10 ->setMaxResults(20) 11 ; 12 13 $birthdayUsers = $userCollection->matching($criteria);
  31. Collection Criteria Filter associations without DQL: 1 <?php 2 use

    Doctrine\Common\Collections\Criteria; 3 4 $userCollection = $group->getUsers(); 5 6 $criteria = Criteria::create() 7 ->where(Criteria::expr()->eq("birthday", "11-23")) 8 ->orderBy(array("username" => "ASC")) 9 ->setFirstResult(0) 10 ->setMaxResults(20) 11 ; 12 13 $birthdayUsers = $userCollection->matching($criteria);
  32. Collection Criteria Filter associations without DQL: 1 <?php 2 use

    Doctrine\Common\Collections\Criteria; 3 4 $userCollection = $group->getUsers(); 5 6 $criteria = Criteria::create() 7 ->where(Criteria::expr()->eq("birthday", "11-23")) 8 ->orderBy(array("username" => "ASC")) 9 ->setFirstResult(0) 10 ->setMaxResults(20) 11 ; 12 13 $birthdayUsers = $userCollection->matching($criteria);
  33. Collection Criteria Filter associations without DQL: 1 <?php 2 use

    Doctrine\Common\Collections\Criteria; 3 4 $userCollection = $group->getUsers(); 5 6 $criteria = Criteria::create() 7 ->where(Criteria::expr()->eq("birthday", "11-23")) 8 ->orderBy(array("username" => "ASC")) 9 ->setFirstResult(0) 10 ->setMaxResults(20) 11 ; 12 13 $birthdayUsers = $userCollection->matching($criteria);
  34. Collection Criteria Filter associations without DQL: 1 <?php 2 use

    Doctrine\Common\Collections\Criteria; 3 4 $userCollection = $group->getUsers(); 5 6 $criteria = Criteria::create() 7 ->where(Criteria::expr()->eq("birthday", "11-23")) 8 ->orderBy(array("username" => "ASC")) 9 ->setFirstResult(0) 10 ->setMaxResults(20) 11 ; 12 13 $birthdayUsers = $userCollection->matching($criteria);
  35. Collection Criteria Filter associations without DQL: 1 <?php 2 use

    Doctrine\Common\Collections\Criteria; 3 4 $userCollection = $group->getUsers(); 5 6 $criteria = Criteria::create() 7 ->where(Criteria::expr()->eq("birthday", "11-23")) 8 ->orderBy(array("username" => "ASC")) 9 ->setFirstResult(0) 10 ->setMaxResults(20) 11 ; 12 13 $birthdayUsers = $userCollection->matching($criteria);
  36. Collection Criteria • Uses SQL when collection is not yet

    loaded • Works on any Selectable – PersistentCollection (associations) – ArrayCollection – EntityReposity
  37. Naming Strategy

  38. Naming Strategy • Customize name generation for tables, columns •

    Two strategies shipped in core: – DefaultNamingStrategy – UnderscoreNamingStrategy • Only works for “unnamed” columns! • Legacy database schema's
  39. Naming Strategy 1 <?php 2 3 class Article 4 {

    5 /** @ORM\Column() */ 6 private $title; 7 8 /** @ORM\Column(name="summary") */ 9 private $summary; 10 } 1 <?php 2 3 $namingStrategy = new MyNamingStrategy(); 4 $configuration->setNamingStrategy($namingStrategy);
  40. Custom ID Generators http://www.flickr.com/photos/mheinzel/5492534374/

  41. Custom ID Generators 1 <?php 2 3 /** 4 *

    @ORM\Entity 5 */ 6 class User 7 { 8 /** 9 * @ORM\Column(type="integer") 10 * @ORM\Id 11 * @ORM\GeneratedValue("CUSTOM") 12 * @ORM\CustomIdGenerator(class="SnowflakeIdGenerator") 13 */ 14 protected $id; 15 }
  42. Custom ID Generators 1 <?php 2 3 use Doctrine\ORM\EntityManager; 4

    use Doctrine\ORM\Id\AbstractIdGenerator; 5 6 class SnowflakeIdGenerator extends AbstractIdGenerator 7 { 8 public function generate(EntityManager $em, $entity) 9 { 10 return snowflake_get_id(); // made up API 11 } 12 }
  43. DoctrineBundle

  44. None
  45. DoctrineBundle

  46. Also in 2.3

  47. Also in 2.3 • Possibility to override metadata in subclasses:

    – Associations – Properties • @NamedNativeQuery • @SqlResultSetMapping(s)
  48. None
  49. NEW() Operator

  50. NEW() Operator 1 <?php 2 3 class AddressDTO 4 {

    5 public function __construct($city, $street) 6 { 7 } 8 } 9 10 $dql = <<<DQL 11 SELECT new AddressDTO(a.city, a.street) 12 FROM User u JOIN u.address a WHERE u.id = ?1 13 DQL;
  51. ResultSetMapping Builder

  52. ResultSetMapping Builder 1 <?php 2 3 $rsm = $entityManager->getRepository('User') 4

    ->createResultSetMappingBuilder('u'); 5 $rsm->addJoinedEntityFromClassMetadata('Group', 'g'); 6 7 $sql = <<<SQL 8 SELECT $rsm FROM users u 9 LEFT JOIN groups g ON u.group_id = g.id 10 SQL; 11 12 $users = $entityManager->createNativeQuery($sql, $rsm) 13 ->getResult();
  53. EventListeners

  54. EventListeners 1 <?php 2 /** 3 * @ORM\EntityListeners({"ContractListener"}) 4 *

    @ORM\HasLifecycleCallbacks 5 * @ORM\Entity 6 */ 7 class Contract 8 { 9 /** @ORM\PostLoad */ 10 public function postLoadHandler(LifecycleEventArgs $event) 11 { 12 // do something 13 } 14 }
  55. EventListeners 1 <?php 2 3 class ContractListener 4 { 5

    /** @ORM\PrePersist */ 6 public function prePersistHandler(Contract $contract) 7 { 8 } 9 10 /** 11 * @ORM\PostPersist 12 */ 13 public function postPersistHandler(LifecycleEventArgs $args) 14 { 15 } 16 }
  56. Questions? Feedback @ http://joind.in/talk/view/7567 Benjamin Eberlei Alexander Mols