Doctrine ORM
What's This Then?
with Margaret Staples
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1703640/doctrine-logo.gif)
Hello, Nice to Meet You
I'm Margaret
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1636922/drawing-elephant.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1636924/light-blue-daisy.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1636923/fucia-daisy.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1636930/white-daisy.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1636926/red-daisy.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1636925/dark-blue-daisy.png)
Hello, Nice to Meet You
I'm Margaret
I have been working with Doctrine
mostly as part of the Symfony2 framework
for about 4 years
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1703640/doctrine-logo.gif)
Hello, Nice to Meet You
I'm Margaret
I'm developing a Social Strategy City-Builder RPG game of awesomeness.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1636585/brune.gif)
brunegame.com
Doctrine ORM
Topics
1. Overview
2. Installation
3. Creating Structure
4. Basic Interactions
5. Complex Queries
6. Custom Repositories
7. Lifecycle Events
8. Questions
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1703640/doctrine-logo.gif)
Doctrine ORM
Overview
"Object Relational Mapping"
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1860017/map-29903_640.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1860019/magnetic-29618_640.png)
Doctrine ORM
Overview
"Object Relational Mapping"
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1860017/map-29903_640.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1860019/magnetic-29618_640.png)
How your app will think about stored information.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1860055/treasure-161753_640.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1860065/pirate-treasure-148526_640.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1860065/pirate-treasure-148526_640.png)
Doctrine ORM
Overview
"Object Relational Mapping"
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1860017/map-29903_640.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1860019/magnetic-29618_640.png)
How your app will think about stored information.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1860055/treasure-161753_640.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1860065/pirate-treasure-148526_640.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1860065/pirate-treasure-148526_640.png)
Reasons with a database on behalf of your app.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
Doctrine ORM
Overview
"Object Relational Mapping"
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
How your app will think about stored information.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
Reasons with a database on behalf of your app.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
Part of Symfony Standard Edition
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1860462/pixel-heart.gif)
Installation
Local Webserver
Linux + Apache + MySQL + PHP
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1794254/linux-tux.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1794342/Tatung-einstein-computer-linux-paste.png)
Installation
Composer
Dependency Manager for PHP
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1703671/logo-composer-transparent2.png)
getcomposer.org
Installation
Option #1: Install Symfony Standard Edition
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1860462/pixel-heart.gif)
> php composer.phar
create-project
symfony/framework-standard-edition
/path/to/webroot/NewProject
Installation
Option #1: Install Symfony Standard Edition
Install the demo bundle?
database_driver (pdo_mysql)?
database_host (127.0.0.1)?
database_port (null)?
database_name (symfony)?
database_user (root)?
database_password (null)?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1860462/pixel-heart.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
Installation
Option #2: Install Doctrine Alone
> php composer.phar
require
doctrine/orm:*
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1703671/logo-composer-transparent2.png)
Include Composer's Autoloader (relative to project root).
require_once "vendor/autoload.php";
Installation
Option #2: Install Doctrine Alone
Setup your Entity Manager: bootstrap.php
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
$paths = ["/path/to/entity-files"];
$isDevMode = false;
// the connection configuration
$dbParams = array(
'driver' => 'pdo_mysql',
'user' => 'root',
'password' => '',
'dbname' => 'foo',
);
$config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode);
$entityManager = EntityManager::create($dbParams, $config);
Installation
Option #2: Install Doctrine Alone
Setup the command line tool: cli-config.php
use Doctrine\ORM\Tools\Console\ConsoleRunner;
require_once 'bootstrap.php';
$entityManager = GetEntityManager();
return ConsoleRunner::createHelperSet($entityManager);
Installation
Option #2: Install Doctrine Alone
doctrine-orm.readthedocs.org
Creating Structure
Entity
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
Describes the Structure of a database table.
Holds methods for interacting with data from records in that table.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1703640/doctrine-logo.gif)
Creating Structure
Entity: Annotations: Simple
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="item")
*/
class Item
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
Creating Structure
Entity: Annotations: Simple
/**
* @ORM\Column(type="string", length=64)
*/
protected $name;
/**
* @ORM\Column(type="decimal", scale=2)
*/
protected $value;
/**
* @ORM\Column(type="integer", nullable=true)
*/
protected $available;
/**
* @ORM\Column(type="array")
*/
protected $attributes;
Creating Structure
Entity: Annotations Associations: One to One
// Join one Character to one AdventureClass
// (Unidirectional)
class Character
{
/**
* @OneToOne(targetEntity="AdventureClass")
* @JoinColumn(name="class_id", referencedColumnName="id")
**/
private $adventure_class;
}
Creating Structure
Entity: Annotations Associations: One to One
// Join one Character to one Mount
// (Bidirectional 1 of 2)
class Character
{
/**
* @OneToOne(targetEntity="Mount", mappedBy="character")
**/
private $mount;
}
Creating Structure
Entity: Annotations Associations: One to One
// Join one Character to one Mount
// (Bidirectional 2 of 2)
class Mount
{
/**
* @OneToOne(targetEntity="Character", inversedBy="mount")
* @JoinColumn(name="character_id", referencedColumnName="id")
**/
private $character;
}
Creating Structure
Entity: Annotations Associations: One to One
// Join one Character to one Character
// (self referencing)
class Character
{
/**
* @OneToOne(targetEntity="Character")
* @JoinColumn(name="sovereign_id", referencedColumnName="id")
**/
private $sovereign;
}
Creating Structure
Entity: Annotations Associations: Many to One
// Join potentially many Character to one Region
// (Unidirectional)
class Character
{
/**
* @ManyToOne(targetEntity="Region")
* @JoinColumn(name="region_id", referencedColumnName="id")
**/
private $region;
}
Creating Structure
Entity: Annotations Associations: One to Many
// Join one User to potentially many Characters
// (Bidirectional 1 of 2)
class User
{
/**
* @OneToMany(targetEntity="Character", mappedBy="user")
**/
private $characters;
public function __construct() {
$this->characters = new ArrayCollection();
}
}
Creating Structure
Entity: Annotations Associations: One to Many
// Join one User to potentially many Characters
// (Bidirectional 2 of 2)
class Character
{
/**
* @ManyToOne(targetEntity="User", inversedBy="characters")
* @JoinColumn(name="user_id", referencedColumnName="id")
**/
private $user;
}
Creating Structure
Entity: Console Commands
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
Update Database Structure based on Entity Annotations
> php app/console doctrine:schema:update --force
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1636936/symfony-small.png)
Creating Structure
Entity: Console Commands
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
Generate Getters / Setters for Entity properties
> php app/console doctrine:generate:entities
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1636936/symfony-small.png)
Creating Structure
Entity: Console Commands
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
Update Database Structure based on Entity Annotations
> php vendor/bin/doctrine orm:schema-tool:update --force
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1703640/doctrine-logo.gif)
Creating Structure
Entity: Console Commands
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1851187/heart.png)
Generate Getters / Setters for Entity properties
> php vendor/bin/doctrine orm:generate-entities
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1703640/doctrine-logo.gif)
Creating Structure
Entity: Generated Getter
class User
{
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1860462/pixel-heart.gif)
Creating Structure
Entity: Generated Setter
class User
{
/**
* Set name
*
* @param string $name
*/
public function setName($name)
{
$this->name = $name;
}
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1860462/pixel-heart.gif)
Creating Structure
Entity: Modified Setter
class Item
{
/**
* Set type
*
* @param string $type
*/
public function setType($type)
{
if (!in_array($type,$types) {
$type = $default_type;
}
$this->type = $type;
}
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1860462/pixel-heart.gif)
Basic Interactions
Access the Entity Manager
// From a Controller
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
}
// From a Command
public function myCommand()
{
$em = $this->getContainer()->get('doctrine')->getManager('default');
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1636936/symfony-small.png)
Basic Interactions
Access the Entity Manager
// From a Service
// Resources/config/services.yml
my_service:
class: Bundle\Namespace\Services
arguments:
entityManager: "@doctrine.orm.entity_manager"
// Services/MyService.php
class MyService
{
public function __construct(EntityManager $entityManager)
{
$this->em = $entityManager;
}
public function someServiceFunction()
{
$em = $this->em;
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1636936/symfony-small.png)
Basic Interactions
Access the Entity Manager
// bootstrap.php
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
require_once "vendor/autoload.php";
$isDevMode = true;
$config = Setup::createAnnotationMetadataConfiguration([__DIR__."/src"], $isDevMode);
$conn = array(
'driver' => 'pdo_sqlite',
'path' => __DIR__ . '/db.sqlite',
);
$em = EntityManager::create($conn, $config);
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1703640/doctrine-logo.gif)
Basic Interactions
Create a New Record
$item = new Item();
$item->setName("Sword");
$item->setType("Weapon");
$item->setAmount(1);
$em->persist($item);
$em->flush();
Include a "use" statement to create or type hint entities
Example: use MyProjectPath\Entity\Item;
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1862348/knight.png)
Basic Interactions
Using Entities in Controllers
$item_repo = $em->getRepository('Item');
// Find all items: returns array of item records or an empty array
$all_items = $item_repo->findAll();
// Find item by id
$item = $item_repo->findOneById($item_id);
// Find items matching parameters
$some_items = $item_repo->findBy(['type' => 'Shield', 'amount' => 10]);
In Symfony2 use the bundle name prefix for repository calls.
Example: getRepository('MyProjectBundle:Item')
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1709571/sword-and-shield.png)
Basic Interactions
Using Entities in Controllers
// One item
$name = $item->getName();
$item->setAmount($item->getAmount() + 5);
$em->persist($item);
$em->flush();
// Many items
foreach ($items as $item){
$name = $item->getName();
if (in_array($name,$cancelled) {
$em->remove($item);
}
}
$em->flush();
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1709571/sword-and-shield.png)
Complex Queries
Doctrine Query Language
$prefer = "Fire";
$types = [ 'Weapon', 'Armor', 'Spell' ];
$query = $em->createQueryBuilder()
->select('c.slot')
->from('MyProject:Item', 'c')
->where('c.name LIKE :prefer')
->andWhere('c.destroyed IS NULL')
->andWhere($qb->expr()->in('c.type',':types'))
->setParameters(['prefer'=>$prefer,'types' => $types])
->distinct()
->orderBy("c.value","DESC")
->getQuery();
$slots = $query->getResult();
foreach ($slots as $each){
$slot_name = $each['slot'];
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1862471/wizard.png)
Complex Queries
Doctrine Query Language
$prefer = "Fire";
$types = [ 'Weapon', 'Armor', 'Spell' ];
$query = $em->createQueryBuilder()
->select('c.slot')
->from('MyProject:Item', 'c')
...
->getQuery();
$slots = $query->getResult();
foreach ($slots as $each){
$slot_name = $each['slot'];
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1862471/wizard.png)
Complex Queries
Doctrine Query Language
$prefer = "Fire";
$types = [ 'Weapon', 'Armor', 'Spell' ];
...
->where('c.name LIKE :prefer')
->andWhere('c.destroyed IS NULL')
->andWhere($qb->expr()->in('c.type',':types'))
->setParameters(['prefer'=>"%$prefer%",'types' => $types])
->distinct()
->orderBy("c.value","DESC")
...
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1862471/wizard.png)
Custom Repos
Add a Custom Repository to an Entity
/**
* @ORM\Entity
* @ORM\Table(name="character")
* @ORM\Entity(repositoryClass="MyProject\Repository\CharacterRepository")
*/
class Character
{
...
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1862491/elephpants.gif)
Custom Repos
Create the Repository Declared in the Entity
namespace MyProject\Repository;
use Doctrine\ORM\EntityRepository;
class CharacterRepository extends EntityRepository
{
// custom functions go here
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1862491/elephpants.gif)
Custom Repos
Custom Repository Functions
class AdventureClassRepository extends EntityRepository
{
public function getClassIdsByClassType($type)
{
$em = $this->getEntityManager();
$query = $em->createQueryBuilder()
->select('c.id')
->from('MyProject:AdventureClass', 'c')
->where('c.type = :type')
->setParameters(array('type'=>$type))
->getQuery();
return $query->getResult();
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1864105/archer.png)
Custom Repos
class CharacterRepository extends EntityRepository
{
public function getTopTenByClassType($type)
{
$em = $this->getEntityManager();
$class_repo = $em->getRepository('MyProject:AdventureClass');
$class_ids = $class_repo->getClassIdsByClassType($type);
Custom Repository Functions
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1864105/archer.png)
Custom Repos
public function getTopTenByClassType($type)
{
...
$query = $em->createQueryBuilder()
->select('c')
->from('MyProject:Character', 'c')
->andWhere('c.inactive IS NULL')
->andWhere($qb->expr()->in('c.class_id',':class_ids'))
->setParameters(array('class_ids' => $class_ids))
->orderBy("c.level","DESC")
->setMaxResults(10)
->getQuery();
return $query->getResult();
Custom Repository Functions
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1864105/archer.png)
Custom Repos
$type = "Ranger";
$char_repo = $em->getRepository('Character');
$top_rangers = $char_repo->getTopTenByClassType($type);
Access Custom Repository Functions
Custom repository functions can be accessed anywhere you have the Entity Manager:
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1864105/archer.png)
Lifecycle Callbacks
/**
* @ORM\Entity()
* @ORM\HasLifecycleCallbacks()
*/
class Character
{
Create Automatic Changes
Callbacks can be set to trigger on entity insert, update, delete, etc
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1864111/clock.gif)
Lifecycle Callbacks
...
/**
* @ORM\Column(type="datetime")
*/
protected $updated_on;
/**
* @ORM\PreUpdate
*/
public function setUpdatedOnValue()
{
$this->updated_on = new \DateTime();
}
Create Automatic Changes
Callbacks can be set to trigger on entity insert, update, delete, etc
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1864111/clock.gif)
Lifecycle Callbacks
...
/**
* @ORM\Column(type="datetime")
*/
protected $updated_on;
/**
* @ORM\PreUpdate
*/
public function setUpdatedOnValue()
{
$this->updated_on = new \DateTime();
}
Create Automatic Changes
Callbacks can be set to trigger on entity insert, update, delete, etc
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1864111/clock.gif)
Lifecycle Callbacks
Create Automatic Changes
Callbacks can be set to trigger on entity insert, update, delete, etc
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1864111/clock.gif)
http://docs.doctrine-project.org/
projects/doctrine-orm/
en/latest/reference/
events.html#lifecycle-events
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1709201/flowery-elephant.png)
Questions?
Doctrine ORM
http://slides.com/margaretstaples/deck
https://joind.in/15632
Questions?
Margaret Staples
@dead_lugosi on twitter
deadlugosi on freenode
brunegame.com
![](https://s3.amazonaws.com/media-p.slid.es/uploads/362724/images/1703640/doctrine-logo.gif)
Doctrine
By Margaret Staples
Doctrine
- 3,284