Doctrine 2 Internals
Benjamin Eberlei
FrOSCon 2010



<?php
class Order
{
private $id;
private $created;
private $customer;
private $items;
public function __construct(Customer $cust)
{
$this->customer = $cust;
$this->created = new DateTime("now");
$this->items = new ArrayCollection();
}
}
<?php
/** @Entity @Table(name="order") */
class Order
{
/** @Id @Column(type="integer")
* @GeneratedValue */
private $id;
/** @Column(type="datetime") */
private $created;
/** @ManyToOne(targetEntity="Customer", ..) */
private $customer;
/** @OneToMany(targetEntity="OrderItem", ..) */
private $items;
}
ClassMetadata instance for each entityClassMetadata is cached (APC, Memcache, ...)<?php
class ClassMetadataInfo
{
public $fieldMappings;
public $associationMappings;
public $reflClass;
public $reflFields;
public $identifier;
public $idGenerator;
// .. 10-20 other variables
}
<?php
$product = $entityManager->find("Product", $pid);
$customer = new Customer("kontakt@beberlei.de");
$order = new Order($customer);
$item = new OrderItem($product, $amount);
$order->addItem($item);
$entityManager->persist($customer);
$entityManager->persist($order);
$entityManager->flush();
<?php
// once per request
$reflClass = new ReflectionClass("Order");
$reflField = $reflClass->getProperty("id");
$reflField->setAccessible(true);
// once per object hydration+field
$reflField->setValue($order, $row['id']);
// once per object persistance+field
$id = $reflField->getValue($order);
<?php
public function newInstance()
{
if ($this->_prototype === null) {
$this->_prototype = unserialize(
sprintf(
'O:%d:"%s":0:{}',
strlen($this->name),
$this->name
)
);
}
return clone $this->_prototype;
}
ArrayCollection wrapped by PersistentCollection
<?php
$order = $em->find("Order", $myOrderId);
// `Order::$customer` is a proxy
$me = $order->getCustomer();
// lazy load fired
echo "Customer: " . $me->getEmail();
<?php
class CustomerProxy extends Customer implements Proxy
{
private function _load()
{
// lazy loading code
}
public function getEmail()
{
$this->_load();
return parent::getEmail();
}
// .. other public methods of Customer
}
<?php
$order = $em->find("Order", $myOrderId);
// `Order::$items` is PersistentCollection
$items = $order->getItems();
$sum = 0;
// loop fires lazy load of all related Items
foreach ($items AS $item) {
// product lazy load
$price = $item->getProduct()->getPrice();
$sum += $item->getAmount() * $price;
}

<?php
class Reference
{
/** @Id @ManyToOne(targetEntity="Article") */
private $source;
/** @Id @ManyToOne(targetEntity="Article") */
private $destination;
/** @Column(type="string") */
private $title;
/** @Column(type="datetime") */
private $created;
}
<?php
class Product
{
/**
* @ElementCollection(table="product_details")
*/
private $details = array();
}
CREATE TABLE product_details (product_id INT,
col_key VARCHAR(255), col_value VARCHAR(255),
PRIMARY KEY (product_id, key));
<?php
class User
{
private $id;
private $email;
/** @Embedded(class="Address") */
private $address;
}
class Address
{
private $street;
private $city;
private $country;
}

$id => $instance
PersistentCollection
EntityManager::persist($entity)
EntityManager::remove($entity)
EntityManager::flush()

Each EBNF Grammer Rule translates to Method
SelectStatement ::= SelectClause FromClause
[WhereClause] [GroupByClause]
[HavingClause] [OrderByClause]
<?php
class Parser
{
public function SelectStatement()
{
$selectStatement = new AST\SelectStatement(
$this->SelectClause(), $this->FromClause()
);
$selectStatement->whereClause =
$this->_lexer->isNextToken(Lexer::T_WHERE) ?
$this->WhereClause() : null;
//...
}
}
SELECT u FROM User u
SELECT u
FROM User u
WHERE u.age > 20 AND u.country = ?1
UPDATE User u
SET u.status = 'inactive'
WHERE u.lastLogin < ?1
SELECT u FROM User u JOIN u.address a
SELECT u, a FROM User u JOIN u.address a
SELECT u.email, COUNT(g.id)
FROM User u
JOIN u.groups g
GROUP BY u.email
Managed Entities x Number of Field Mappings
Public faster than Protected and Private
<?php
class ClassMetadata
{
public $fieldMappings;
protected $associationMappings;
private $identifier;
}
echo serialize(new ClassMetadata());
Yields:
O:13:"ClassMetadata":3:{
s:13:"fieldMappings";N;
s:22:"*associationMappings";N;
s:25:"ClassMetadataidentifier";N;}