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. What is new in
    Doctrine?
    Benjamin Eberlei
    & Alexander Mols
    Symfony Live Berlin 2012

    View Slide

  2. Benjamin
    Benjamin Eberlei
    SimpleThings GmbH
    Doctrine Core Developer
    github.com/beberlei
    twitter.com/beberlei

    View Slide

  3. Alexander
    Alexander Mols
    Technical Lead Developer @ VONQ
    Doctrine Core Developer
    github.com/asm89
    twitter.com/iam_asm89

    View Slide

  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

    View Slide

  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

    View Slide

  6. Filter API
    http://www.flickr.com/photos/giantsqurl/4983106796/

    View Slide

  7. Filter API
    1 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 }

    View Slide

  8. Filter API
    1 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');

    View Slide

  9. Filter API
    1 # entity manager configuration
    2 filters:
    3 locale:
    4 class: Acme\MyLocaleFilter
    5 softdelete:
    6 class: Acme\MySoftdeleteFilter
    7 enabled: true

    View Slide

  10. Complex SQL
    Types
    http://www.flickr.com/photos/garrettc/2172850095/

    View Slide

  11. Complex SQL Types
    1 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 }

    View Slide

  12. Flush
    Optimizations
    http://www.flickr.com/photos/elycefeliz/5717273714/

    View Slide

  13. 1 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));

    View Slide

  14. Paginator
    http://www.flickr.com/photos/horiavarlan/4332381194/

    View Slide

  15. Paginator

    Merger of three libraries:
    – Doctrine extensions
    – Pagerfanta
    – KnpLabs pager

    Used in various bundles now

    View Slide

  16. 1 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 }

    View Slide

  17. DQL
    Improvements

    View Slide

  18. DQL Improvements
    1 2 $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 }

    View Slide

  19. DQL Improvements
    1 2 $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;

    View Slide

  20. DQL Improvements

    Bit comparisons

    New CASE expression

    New IDENTITY function

    Sort by associations

    View Slide

  21. Master-Slave
    Connection

    View Slide

  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

    View Slide

  23. Target Entity
    Resolving

    View Slide

  24. Target Entity Resolving
    1 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

    View Slide

  25. View Slide

  26. Arbitrary Joins

    View Slide

  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

    View Slide

  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

    View Slide

  29. Collection Criteria

    View Slide

  30. Collection Criteria
    Filter associations without DQL:
    1 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);

    View Slide

  31. Collection Criteria
    Filter associations without DQL:
    1 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);

    View Slide

  32. Collection Criteria
    Filter associations without DQL:
    1 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);

    View Slide

  33. Collection Criteria
    Filter associations without DQL:
    1 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);

    View Slide

  34. Collection Criteria
    Filter associations without DQL:
    1 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);

    View Slide

  35. Collection Criteria
    Filter associations without DQL:
    1 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);

    View Slide

  36. Collection Criteria

    Uses SQL when collection is not yet loaded

    Works on any Selectable
    – PersistentCollection (associations)
    – ArrayCollection
    – EntityReposity

    View Slide

  37. Naming Strategy

    View Slide

  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

    View Slide

  39. Naming Strategy
    1 2
    3 class Article
    4 {
    5 /** @ORM\Column() */
    6 private $title;
    7
    8 /** @ORM\Column(name="summary") */
    9 private $summary;
    10 }
    1 2
    3 $namingStrategy = new MyNamingStrategy();
    4 $configuration->setNamingStrategy($namingStrategy);

    View Slide

  40. Custom ID
    Generators
    http://www.flickr.com/photos/mheinzel/5492534374/

    View Slide

  41. Custom ID Generators
    1 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 }

    View Slide

  42. Custom ID Generators
    1 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 }

    View Slide

  43. DoctrineBundle

    View Slide

  44. View Slide

  45. DoctrineBundle

    View Slide

  46. Also in 2.3

    View Slide

  47. Also in 2.3

    Possibility to override metadata in subclasses:
    – Associations
    – Properties

    @NamedNativeQuery

    @SqlResultSetMapping(s)

    View Slide

  48. View Slide

  49. NEW() Operator

    View Slide

  50. NEW() Operator
    1 2
    3 class AddressDTO
    4 {
    5 public function __construct($city, $street)
    6 {
    7 }
    8 }
    9
    10 $dql = <<11 SELECT new AddressDTO(a.city, a.street)
    12 FROM User u JOIN u.address a WHERE u.id = ?1
    13 DQL;

    View Slide

  51. ResultSetMapping
    Builder

    View Slide

  52. ResultSetMapping Builder
    1 2
    3 $rsm = $entityManager->getRepository('User')
    4 ->createResultSetMappingBuilder('u');
    5 $rsm->addJoinedEntityFromClassMetadata('Group', 'g');
    6
    7 $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();

    View Slide

  53. EventListeners

    View Slide

  54. EventListeners
    1 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 }

    View Slide

  55. EventListeners
    1 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 }

    View Slide

  56. Questions?
    Feedback @
    http://joind.in/talk/view/7567
    Benjamin Eberlei Alexander Mols

    View Slide