mysql_connect('localhost', 'user', 'password');
mysql_select_db('test');
//simple example
mysql_query('INSERT INTO greetings (content) VALUES ("' . mysql_real_escape_string($_POST['greeting']) . '")');
//comments example
mysql_query('INSERT INTO users (username) VALUES ("' . mysql_real_escape_string($_POST['username']) . '")');
mysql_query('INSERT INTO comments (user_id, content) VALUES (' . mysql_insert_id() . ', "' . mysql_real_escape_string($_POST['content']) . '")');
try {
$dbh = new PDO('mysql:host=localhost;dbname=test', 'user', 'password');
//simple example
$sth = $dbh->prepare('INSERT INTO greetings (content) VALUES (:content);
$sth->execute(array(':content' => $_POST['content']));
//comments example
$sth = $dbh->prepare('INSERT INTO users (username) VALUES (:username)');
$sth->execute(array(':username' => $_POST['username']));
$sth = $dbh->prepare('INSERT INTO comments (user_id, content) VALUES (user_id, :content)');
$sth->execute(array(':user_id' => $dbh->lastInsertId(), ':content' => $_POST['content']);
} catch (PDOException $e) {
echo $e->getMessage();
}
An Active Record implementation
class Greeting extends Doctrine_Record
{
public function setTableDefinition()
{
$this->setTableName('greetings');
$this->hasColumn(
'content',
'string',
255,
array(
'type' => 'string',
'length' => '255'
)
);
}
}
//simple example
$greeting = new Greeting();
$greeting->content = $_POST['content'];
$greeting->save();
class User extends Doctrine_Record
{
public function setTableDefinition()
{
$this->setTableName('users');
$this->hasColumn(
'username',
'string',
255,
array(
'type' => 'text',
)
);
}
public function setUp()
{
$this->hasMany(
'Comments as Comments',
array(
'refClass' => 'Comment',
'local' => 'id',
'foreign' => 'user_id'
)
);
}
}
class Comment extends Doctrine_Record
{
public function setTableDefinition()
{
$this->setTableName('comments');
$this->hasColumn(
'content',
'string',
255,
array(
'type' => 'string',
'length' => '255'
)
);
$this->hasColumn(
'user_id',
'string',
255,
array(
'type' => 'integer',
)
);
}
public function setUp()
{
$this->hasOne(
'User',
array(
'refClass' => 'User',
'local' => 'user_id',
'foreign' => 'id'
)
);
}
}
//comments example
$user = new User();
$user->username = $_POST['username'];
$comment = new Comment();
$comment->content = $_POST['content'];
$user->Comments[] = $comment;
$comment->User = $user;
$user->save();
mkdir doctrine-demo
cd doctrine-demo
mkdir library
mkdir library/Entity
touch library/Entity/Greeting.php
mkdir library/EntityProxy
chmod +w library/EntityProxy
echo "{}" > composer.json
curl -s https://getcomposer.org/installer | php
./composer.phar require doctrine/orm:2.*
touch bootstrap.php
namespace Entity;
class Greeting
{
/** @var int */
private $id;
/** @var string */
private $content;
}
namespace Entity;
class Greeting
{
/** @var int */
private $id;
/** @var string */
private $content;
public function __construct($content) {
$this->setContent($content);
}
/**
* @return int
*/
public function getId() {
return $this->id;
}
/**
* @return string
*/
public function getContent() {
return $this->content;
}
/**
* @param string $content
*/
public function setContent($content) {
$this->content = (string) $content;
}
}
namespace Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class Greeting
{
/**
* @ORM\Id()
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
* @var int
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
* @var string
*/
private $content;
public function __construct($content) {
$this->setContent($content);
}
/**
* @return int
*/
public function getId() {
return $this->id;
}
/**
* @return string
*/
public function getContent() {
return $this->content;
}
/**
* @param string $content
*/
public function setContent($content) {
$this->content = (string) $content;
}
}
<?php
// bootstrap.php
use Doctrine\ORM\Tools\Setup,
Doctrine\ORM\EntityManager,
Doctrine\ORM\Configuration,
Doctrine\Common\Cache\ArrayCache as Cache,
Doctrine\Common\Annotations\AnnotationRegistry,
Doctrine\Common\ClassLoader;
//autoloading
require_once __DIR__ . '/vendor/autoload.php';
$loader = new ClassLoader('Entity', __DIR__ . '/library');
$loader->register();
$loader = new ClassLoader('EntityProxy', __DIR__ . '/library');
$loader->register();
//configuration
$config = new Configuration();
$cache = new Cache();
$config->setQueryCacheImpl($cache);
$config->setProxyDir(__DIR__ . '/library/EntityProxy');
$config->setProxyNamespace('EntityProxy');
$config->setAutoGenerateProxyClasses(true);
//mapping (example uses annotations, could be any of XML/YAML or plain PHP)
AnnotationRegistry::registerFile(__DIR__ . '/library/doctrine-orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php');
$driver = new Doctrine\ORM\Mapping\Driver\AnnotationDriver(
new Doctrine\Common\Annotations\AnnotationReader(),
array(__DIR__ . '/library/Entity')
);
$config->setMetadataDriverImpl($driver);
$config->setMetadataCacheImpl($cache);
//getting the EntityManager
$em = EntityManager::create(
array(
'driver' => 'pdo_sqlite',
'path' => 'database.sqlite'
),
$config
);
<?php
// bootstrap.php
use Doctrine\ORM\Tools\Setup,
Doctrine\ORM\EntityManager,
Doctrine\ORM\Configuration,
Doctrine\Common\Cache\ArrayCache as Cache,
Doctrine\Common\Annotations\AnnotationRegistry,
Doctrine\Common\ClassLoader;
//autoloading
require_once __DIR__ . '/vendor/autoload.php';
$loader = new ClassLoader('Entity', __DIR__ . '/library');
$loader->register();
$loader = new ClassLoader('EntityProxy', __DIR__ . '/library');
$loader->register();
//configuration
$config = new Configuration();
$cache = new Cache();
$config->setQueryCacheImpl($cache);
$config->setProxyDir(__DIR__ . '/library/EntityProxy');
$config->setProxyNamespace('EntityProxy');
$config->setAutoGenerateProxyClasses(true);
//mapping (example uses annotations, could be any of XML/YAML or plain PHP)
AnnotationRegistry::registerFile(__DIR__ . '/library/doctrine-orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php');
$driver = new Doctrine\ORM\Mapping\Driver\AnnotationDriver(
new Doctrine\Common\Annotations\AnnotationReader(),
array(__DIR__ . '/library/Entity')
);
$config->setMetadataDriverImpl($driver);
$config->setMetadataCacheImpl($cache);
//getting the EntityManager
$em = EntityManager::create(
array(
'driver' => 'pdo_sqlite',
'path' => 'database.sqlite'
),
$config
);
<?php
//doctrine-cli.php
use Symfony\Component\Console\Helper\HelperSet,
Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper,
Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper,
Doctrine\ORM\Tools\Console\ConsoleRunner;
require_once __DIR__ . '/bootstrap.php';
$helperSet = new HelperSet(array(
'em' => new EntityManagerHelper($em),
'conn' => new ConnectionHelper($em->getConnection())
));
ConsoleRunner::run($helperSet);
$ php doctrine-cli.php orm:schema-tool:create
ATTENTION: This operation should not be executed in a production environment.
Creating database schema...
Database schema created successfully!
Generated database as seen by SQLite browser
//examples/1.php
use Entity\Greeting;
require_once __DIR__ . '/../bootstrap.php';
//Creating our greeting
$greeting = new Greeting('Hello World!');
//Registering $greeting with the EntityManager
$em->persist($greeting);
//Flushing all changes to database
$em->flush();
echo 'OK!';
//examples/2.php
require_once __DIR__ . '/../bootstrap.php';
//Finding Greeting with id = 1
$greeting = $em->find('Entity\Greeting', 1);
if($greeting) {
//The EntityManager has already provided us an object of type Entity\Greeting!
echo 'Found a greeting (instance of ' . get_class($greeting)
. ') with content ' . $greeting->getContent();
}else{
echo 'Couldn\'t find Greeting with id=1';
}
//examples/3.php
require_once __DIR__ . '/../bootstrap.php';
//Finding Greeting with id = 1
$greeting = $em->find('Entity\Greeting', 1);
if($greeting) {
echo $greeting->getContent() . PHP_EOL;
echo 'Changing the contents of found Greeting to "Hello Test!"' . PHP_EOL;
//Using Entity\Greeting to set a new content for the $greeting!
$greeting->setContent('Hello Test!');
//Flushing changes to database (triggers SQL updates)
$em->flush();
echo 'Now try loading 2.php again!' . PHP_EOL;
}else{
echo 'Couldn\'t find Greeting with id=1';
}
//examples/4.php
require_once __DIR__ . '/../bootstrap.php';
//A repository is like a "Table" containing our entities of a specified type
$repository = $em->getRepository('Entity\Greeting');
//Finding all Entity\Greeting with content = "Hello World!"
$worldGreetings = $repository->findBy(array('content' => 'Hello World!'));
//Finding all Entity\Greeting with content = "Hello Test!"
$testGreetings = $repository->findBy(array('content' => 'Hello Test!'));
//Displaying results
echo 'Found ' . count($worldGreetings) . ' "Hello World!" greetings:' . PHP_EOL;
foreach($worldGreetings as $worldGreeting) {
echo ' - ' . $worldGreeting->getId() . PHP_EOL;
}
echo 'Found ' . count($testGreetings) . ' "Hello Test!" greetings:' . PHP_EOL;
foreach($testGreetings as $testGreeting) {
echo ' - ' . $testGreeting->getId() . PHP_EOL;
}
//examples/5.php
require_once __DIR__ . '/../bootstrap.php';
//Creating a DQL query that selects all greetings with id >= 5 and id <= 10
$greetings = $em
->createQuery('SELECT g FROM Entity\Greeting g WHERE g.id >= 5 AND g.id <= 10')
->getResult();
//Displaying results
echo 'Found ' . count($greetings) . ' Entity\Greeting:' . PHP_EOL;
foreach($greetings as $greeting) {
echo ' - ' . $greeting->getId() . ' => ' . $greeting->getContent() . PHP_EOL;
}
//examples/6.php
require_once __DIR__ . '/../bootstrap.php';
//Finding the last inserted greeting
$greetings = $em
->createQuery('SELECT g FROM Entity\Greeting g ORDER BY g.id DESC')
->setMaxResults(1) //we want only one result
->getResult();
if(!empty($greetings)) {
$greeting = reset($greetings);
echo 'Found greeting with id "' . $greeting->getId()
. '" and content "' . $greeting->getContent() . '"' . PHP_EOL;
$em->remove($greeting);
//Triggers delete
$em->flush();
echo 'Greeting deleted!' . PHP_EOL;
} else {
echo 'Could not find any Greeting' . PHP_EOL;
}
ONE Entity\Post
HAS MANY Entity\Comment
ONE Entity\Comment
HAS ONE Entity\Post
Entity\Post::comments
is a OneToMany relation to Entity\Comment, mapped by Entity\Comment#post
Entity\Comment::post
is a ManyToOne relation to Entity\Post, inversed by Entity\Post#comments
OneToMany and ManyToMany relations are represented in Doctrine by instances of the Doctrine\Common\Collections\Collection interface.
Relationship between entities may be inversed (bidirectional) or unidirectional.
There is always an owning side of the relation.
A bidirectional relationship has both an owning side and an inverse side.
A unidirectional relationship has only an owning side.
The owning side of the relation is the one checked by Doctrine to determine changes to the relation graph.
Some rules apply to relations, you can read them at
http://www.doctrine-project.org/docs/orm/2.0/en/reference/association-mapping.html
Please be sure to have read them before getting to work with Doctrine.
/** @ORM\Entity */
class User
{
/** @ORM\Id() @ORM\Column(type="integer") @ORM\GeneratedValue(strategy="AUTO") @var int */
private $id;
/** @ORM\Column(type="string", length=255) @var string */
private $login;
/**
* @ORM\OneToMany(targetEntity="Entity\Comment", mappedBy="user")
* @var Collection
*/
private $comments;
public function __construct($login) {
//Initializing collection. Doctrine recognizes Collections, not arrays!
$this->comments = new ArrayCollection();
$this->setLogin($login);
}
//Getters and setters
/** @return Collection */
public function getComments() {
return $this->comments;
}
/** @param Comment $comment */
public function addComment(Comment $comment) {
$this->comments->add($comment);
$comment->setUser($this);
}
}
/** @ORM\Entity */
class Comment
{
/** @ORM\Id() @ORM\Column(type="integer") @ORM\GeneratedValue(strategy="AUTO") @var int */
private $id;
/** @ORM\Column(type="string", length=255) @var string */
private $content;
/**
* @ORM\ManyToOne(targetEntity="Entity\User", inversedBy="comments")
* @var User|null
*/
private $user;
public function __construct($content) {
$this->setContent($content);
}
//Setters, getters
/** @return User|null */
public function getUser() {
return $this->user;
}
/** @param User $user */
public function setUser(User $user) {
if($user === null || $user instanceof User) {
$this->user = $user;
} else {
throw new InvalidArgumentException('$user must be instance of Entity\User or null!');
}
}
}
//examples/7.php
use Entity\User,
Entity\Comment;
require_once __DIR__ . '/../bootstrap.php';
//Creating our user
$user = new User('Marco Pivetta');
$em->persist($user);
$comment = new Comment('This is a sample post!');
$em->persist($comment);
$user->addComment($comment);
//Flushing all changes to database
$em->flush();
echo 'OK!';
//examples/8.php
require_once __DIR__ . '/../bootstrap.php';
//Finding previously persisted user
$user = $em->find('Entity\User', 1);
if($user) {
echo 'Found an Entity\User: ' . PHP_EOL
. $user->getId() . ' => ' . $user->getLogin() . '(' . get_class($user) . ')' . PHP_EOL
. 'and ' . $user->getComments()->count() . ' Entity\Comment attached to it: ' . PHP_EOL;
foreach($user->getComments() as $comment) {
echo ' ' . $comment->getId() . ' => ' . $comment->getContent()
. ' (' . get_class($comment) . ')' . PHP_EOL;
}
} else {
echo 'Could not find Entity\User with id=1';
}
//examples/9.php
use Entity\Comment;
require_once __DIR__ . '/../bootstrap.php';
//Finding previously persisted user
$user = $em->find('Entity\User', 1);
if($user) {
echo 'Found an Entity\User: ' . PHP_EOL
. $user->getId() . ' => ' . $user->getLogin() . '(' . get_class($user) . ')' . PHP_EOL
. 'and ' . $user->getComments()->count() . ' Entity\Comment attached to it: ' . PHP_EOL;
echo 'Adding a Comment to the user';
$comment = new Comment('Comment generated at ' . time());
$em->persist($comment);
$user->addComment($comment);
$em->flush();
echo 'Comment has been attached to the user, try 8.php!';
} else {
echo 'Could not find Entity\User with id=1';
}
//examples/10.php
require_once __DIR__ . '/../bootstrap.php';
$user = $em->find('Entity\User', 1);
if($user) {
echo 'Found an Entity\User: ' . PHP_EOL
. $user->getId() . ' => ' . $user->getLogin() . '(' . get_class($user) . ')' . PHP_EOL
. 'and ' . $user->getComments()->count() . ' Entity\Comment attached to it: ' . PHP_EOL;
if($comment = $user->getComments()->first()) {
echo 'Removing the first attached comment!' . PHP_EOL;
echo 'Removing comment with id=' . $comment->getId() . PHP_EOL;
$em->remove($comment);
$em->flush();
} else {
echo 'Could not find any comments to remove...' . PHP_EOL;
}
} else {
echo 'Could not find Entity\User with id=1';
}
//examples/11.php
require_once __DIR__ . '/../bootstrap.php';
echo 'Searching all users with a comment with id > 5: ' . PHP_EOL;
$users = $em
->createQuery('SELECT u FROM Entity\User u JOIN u.comments c WHERE c.id > :id')
->setParameter('id', 5)
->getResult();
echo 'Found ' . count($users) . ':' . PHP_EOL;
foreach($users as $user) {
echo ' ' . $user->getId() . ' => ' . $user->getLogin() . ' (' . get_class($user) . ')' . PHP_EOL;
}
This tutorial should be extended with following examples and features:
This tutorial should be extended with following examples and features:
You can find, fork, edit and help me with these slides at
https://github.com/Ocramius/Doctrine2ORMSlidesTutorial
You can find the Doctrine project pages at
http://www.doctrine-project.org/
All what you have seen is possible only because of a good community helping in this project, so please be part of it and share!
Marco Pivetta
@Ocramius
ocramius@gmail.com