with Margaret Staples
1. Overview
2. Installation
3. Creating Structure
4. Basic Interactions
5. Complex Queries
6. Custom Repositories
7. Lifecycle Events
8. Questions
"Object Relational Mapping"
"Object Relational Mapping"
How your app will think about stored information.
"Object Relational Mapping"
How your app will think about stored information.
Reasons with a database on behalf of your app.
"Object Relational Mapping"
How your app will think about stored information.
Reasons with a database on behalf of your app.
Part of Symfony Standard Edition
Linux + Apache + MySQL + PHP
Dependency Manager for PHP
> php composer.phar
create-project
symfony/framework-standard-edition
/path/to/webroot/NewProject
> php composer.phar
require
doctrine/orm:*
Include Composer's Autoloader (relative to project root).
require_once "vendor/autoload.php";
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);
Setup the command line tool: cli-config.php
use Doctrine\ORM\Tools\Console\ConsoleRunner;
require_once 'bootstrap.php';
$entityManager = GetEntityManager();
return ConsoleRunner::createHelperSet($entityManager);
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;
/**
* @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;
// Join one Character to one AdventureClass
// (Unidirectional)
class Character
{
/**
* @OneToOne(targetEntity="AdventureClass")
* @JoinColumn(name="class_id", referencedColumnName="id")
**/
private $adventure_class;
}
// Join one Character to one Mount
// (Bidirectional 1 of 2)
class Character
{
/**
* @OneToOne(targetEntity="Mount", mappedBy="character")
**/
private $mount;
}
// 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;
}
// Join one Character to one Character
// (self referencing)
class Character
{
/**
* @OneToOne(targetEntity="Character")
* @JoinColumn(name="sovereign_id", referencedColumnName="id")
**/
private $sovereign;
}
// Join potentially many Character to one Region
// (Unidirectional)
class Character
{
/**
* @ManyToOne(targetEntity="Region")
* @JoinColumn(name="region_id", referencedColumnName="id")
**/
private $region;
}
// 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();
}
}
// 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;
}
Update Database Structure based on Entity Annotations
> php app/console doctrine:schema:update --force
Generate Getters / Setters for Entity properties
> php app/console doctrine:generate:entities
Update Database Structure based on Entity Annotations
> php vendor/bin/doctrine orm:schema-tool:update --force
Generate Getters / Setters for Entity properties
> php vendor/bin/doctrine orm:generate-entities
class User
{
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
}
class User
{
/**
* Set name
*
* @param string $name
*/
public function setName($name)
{
$this->name = $name;
}
}
class Item
{
/**
* Set type
*
* @param string $type
*/
public function setType($type)
{
if (!in_array($type,$types) {
$type = $default_type;
}
$this->type = $type;
}
}
// From a Controller
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
}
// From a Command
public function myCommand()
{
$em = $this->getContainer()->get('doctrine')->getManager('default');
}
// 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;
// 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);
$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;
$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')
// 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();
$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'];
}
$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'];
}
$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")
...
/**
* @ORM\Entity
* @ORM\Table(name="character")
* @ORM\Entity(repositoryClass="MyProject\Repository\CharacterRepository")
*/
class Character
{
...
namespace MyProject\Repository;
use Doctrine\ORM\EntityRepository;
class CharacterRepository extends EntityRepository
{
// custom functions go here
}
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();
}
class CharacterRepository extends EntityRepository
{
public function getTopTenByClassType($type)
{
$em = $this->getEntityManager();
$class_repo = $em->getRepository('MyProject:AdventureClass');
$class_ids = $class_repo->getClassIdsByClassType($type);
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();
$type = "Ranger";
$char_repo = $em->getRepository('Character');
$top_rangers = $char_repo->getTopTenByClassType($type);
Custom repository functions can be accessed anywhere you have the Entity Manager:
/**
* @ORM\Entity()
* @ORM\HasLifecycleCallbacks()
*/
class Character
{
Callbacks can be set to trigger on entity insert, update, delete, etc
...
/**
* @ORM\Column(type="datetime")
*/
protected $updated_on;
/**
* @ORM\PreUpdate
*/
public function setUpdatedOnValue()
{
$this->updated_on = new \DateTime();
}
Callbacks can be set to trigger on entity insert, update, delete, etc
...
/**
* @ORM\Column(type="datetime")
*/
protected $updated_on;
/**
* @ORM\PreUpdate
*/
public function setUpdatedOnValue()
{
$this->updated_on = new \DateTime();
}
Callbacks can be set to trigger on entity insert, update, delete, etc
Callbacks can be set to trigger on entity insert, update, delete, etc
Margaret Staples
@dead_lugosi on twitter
deadlugosi on freenode