Upgrade to Pro — share decks privately, control downloads, hide ads and more …

What is new in Doctrine?

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.

Alexander

November 23, 2012
Tweet

More Decks by Alexander

Other Decks in Programming

Transcript

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

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

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

    • Hard to keep up with rate of change • Documentation fail on our part
  4. 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 }
  5. 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');
  6. Filter API 1 # entity manager configuration 2 filters: 3

    locale: 4 class: Acme\MyLocaleFilter 5 softdelete: 6 class: Acme\MySoftdeleteFilter 7 enabled: true
  7. 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 }
  8. 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));
  9. Paginator • Merger of three libraries: – Doctrine extensions –

    Pagerfanta – KnpLabs pager • Used in various bundles now
  10. 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 }
  11. 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 }
  12. 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;
  13. DQL Improvements • Bit comparisons • New CASE expression •

    New IDENTITY function • Sort by associations
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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);
  19. 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);
  20. 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);
  21. 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);
  22. 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);
  23. 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);
  24. Collection Criteria • Uses SQL when collection is not yet

    loaded • Works on any Selectable – PersistentCollection (associations) – ArrayCollection – EntityReposity
  25. Naming Strategy • Customize name generation for tables, columns •

    Two strategies shipped in core: – DefaultNamingStrategy – UnderscoreNamingStrategy • Only works for “unnamed” columns! • Legacy database schema's
  26. 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);
  27. 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 }
  28. 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 }
  29. Also in 2.3 • Possibility to override metadata in subclasses:

    – Associations – Properties • @NamedNativeQuery • @SqlResultSetMapping(s)
  30. 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;
  31. 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();
  32. 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 }
  33. 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 }