1. Doctrator
Pablo Díez
Symfony Live 2011 - Paris
viernes 4 de marzo de 2011
2. Pablo Díez
Creator of Mondongo
ODM for MongoDB and PHP
http://mondongo.es (in English :)
Creator of Mondator
Class generator for PHP
Creator of Doctrator
http://twitter.com/pablodip
http://github.com/pablodip
viernes 4 de marzo de 2011
11. How does Doctrine2 work?
“Doctrine2 provides transparent persistence for PHP objects.”
http://www.doctrine-project.org/docs/orm/2.0/en/reference/introduction.html
viernes 4 de marzo de 2011
12. That is, persist PHP objects without restrictions of a base class,
properties, methods.
namespace Model;
class User
{
public $id;
public $username;
public $email;
}
viernes 4 de marzo de 2011
13. You only have to tell Doctrine2 (map) what you want to persist.
viernes 4 de marzo de 2011
14. You only have to tell Doctrine2 (map) what you want to persist.
With Docblock Annotations
/**
* @Entity
*/
class User
{
/**
* @Id
* @Column(type="integer")
*/
public $id;
/**
* @Column(length=50)
*/
public $username;
/**
* @Column(length=100)
*/
public $email;
}
viernes 4 de marzo de 2011
15. You only have to tell Doctrine2 (map) what you want to persist.
With YAML
EntitiesUser:
type: entity
fields:
id: { type: integer, id: true }
username: { type: string(50) }
email: { type: string(50) }
viernes 4 de marzo de 2011
16. You only have to tell Doctrine2 (map) what you want to persist.
With XML
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="ModelUser" table="user">
<id name="id" type="integer" column="id">
<generator strategy="AUTO"/>
</id>
<field name="username" type="string" length="50" />
<field name="email" type="string" length="100" />
</entity>
</doctrine-mapping>
viernes 4 de marzo de 2011
17. You only have to tell Doctrine2 (map) what you want to persist.
With PHP
$metadata->mapField(array(
'id' => true,
'fieldName' => 'id',
'type' => 'integer'
));
$metadata->mapField(array(
'fieldName' => 'username',
'type' => 'string'
));
$metadata->mapField(array(
'fieldName' => 'email',
'type' => 'string'
));
viernes 4 de marzo de 2011
18. You only have to tell Doctrine2 (map) what you want to persist.
Then you are able to persist those objects.
viernes 4 de marzo de 2011
19. Then you are able to persist those objects.
$user = new User();
$user->username = 'pablodip';
$user->password = 'pa$$word';
$user->email = 'pablodip@gmail.com';
$entityManager->persist($user);
$entityManager->flush();
viernes 4 de marzo de 2011
20. A Doctrine2 good practice is to use non public
properties in the entities.
class User
{
protected $id;
protected $username;
protected $password;
protected $email;
}
viernes 4 de marzo de 2011
21. You have to create methods to access to the properties.
Setters & Getters
public function setId($id)
{
$this->id = $id;
}
public function getId()
{
return $this->id;
}
public function setUsername($username)
{
$this->username = $username;
}
public function getUsername()
{
return $this->username;
}
public function setPassword($password)
{
$this->password = $password;
viernes 4 de marzo de 2011
22. What do you need to work with this simple table?
user
id integer
username string
password string
email string
viernes 4 de marzo de 2011
23. namespace Model;
class User
{
}
Class
user
id integer
username string
password string
email string
viernes 4 de marzo de 2011
24. namespace Model;
protected $id; class User
protected $username; {
protected $password; }
protected $email;
Class
Properties
user
id integer
username string
password string
email string
viernes 4 de marzo de 2011
25. namespace Model;
protected $id; class User
protected $username; {
protected $password; }
protected $email;
Class
Properties
user
id integer
username string
public function setId($id)
{
}
$this->id = $id;
password string
public function getId()
{
return $this->id;
email string
}
public function setUsername($username)
{
$this->username = $username; Setters/Getters
}
public function getUsername()
{
return $this->username;
}
public function setPassword($password)
{
$this->password = $password;
viernes 4 de marzo de 2011
26. namespace Model;
protected $id; class User
protected $username; {
protected $password; }
protected $email;
Class
Mapping
Properties
user
id integer
/**
* @Entity
username string */
public function setId($id)
{ /**
}
$this->id = $id;
password string * @Id
* @Column(type="integer")
*/
public function getId()
{
return $this->id;
email string /**
* @Column(length=50)
}
*/
public function setUsername($username)
{
$this->username = $username; Setters/Getters /**
* @Column(length=100)
} */
public function getUsername()
{
return $this->username;
}
public function setPassword($password)
{
$this->password = $password;
viernes 4 de marzo de 2011
27. namespace Model;
/**
* @Entity
*/
class User
{
/**
* @Id
* @Column(type="integer")
*/
protected $id;
/**
* @Column(length="50")
*/
protected $username;
/**
* @Column(length="40")
*/
protected $password;
/**
user * @Column(length="100")
*/
protected $email;
id integer public function setId($id)
{
$this->id = $id;
}
username string public function getId()
{
return $this->id;
password string }
public function setUsername($username)
{
email string }
$this->username = $username;
public function getUsername()
{
return $this->username;
}
public function setPassword($password)
{
$this->password = $password;
}
public function getPassword()
{
return $this->password;
}
public function setEmail($email)
{
$this->email = $email;
}
public function getEmail()
{
return $this->email;
}
}
viernes 4 de marzo de 2011
28. namespace Model;
/**
* @Entity
*/
class User
{
/**
* @Id
* @Column(type="integer")
*/
protected $id;
/**
* @Column(length="50")
*/
protected $username;
/**
* @Column(length="40")
*/
protected $password;
/**
user * @Column(length="100")
*/
protected $email;
id integer public function setId($id)
{
$this->id = $id;
LORC
}
username string public function getId()
{
return $this->id;
password string }
public function setUsername($username)
{
email string }
$this->username = $username;
public function getUsername()
{
return $this->username;
}
public function setPassword($password)
{
$this->password = $password;
}
public function getPassword()
{
return $this->password;
}
public function setEmail($email)
{
$this->email = $email;
}
public function getEmail()
{
return $this->email;
}
}
viernes 4 de marzo de 2011
29. LORC
Lines Of Repetitive Code
viernes 4 de marzo de 2011
30. LORC
Lines Of Repetitive Code
... and we still don’t have any features! :)
viernes 4 de marzo de 2011
31. How many LORC do we need in a real database?
viernes 4 de marzo de 2011
32. How many LORC do we need in a real database?
...
viernes 4 de marzo de 2011
43. namespace Model;
class User
{
protected $username;
public function setUsername($username)
{
$this->username = $username;
}
public function getUsername()
{
return $this->username;
}
}
viernes 4 de marzo de 2011
44. namespace Model; Definition
class User
{ Properties
protected $username;
public function setUsername($username)
{
$this->username = $username;
}
public function getUsername()
{
return $this->username;
}
}
Methods
viernes 4 de marzo de 2011
46. namespace Model;
class User
{
}
Full Class Name
$definition = new Definition('ModelUser');
viernes 4 de marzo de 2011
47. protected $username;
Visibility Name
$property = new Property('protected', 'username');
$definition->addProperty($property);
viernes 4 de marzo de 2011
48. public function setUsername($username)
{
$this->username = $username;
}
Visibility Name Arguments Code
$method = new Method('public', 'setUsername', '$username', <<<EOF
$this->username = $username;
EOF
);
$definition->addMethod($method);
viernes 4 de marzo de 2011
49. You can define any PHP class.
viernes 4 de marzo de 2011
50. Parent class
$definition->setParentClass('ModelBaseUser');
Interfaces
$definition->addInterface('ArrayAccess');
Abstract
$definition->setIsAbstract(true);
viernes 4 de marzo de 2011
51. Default value
$property->setValue($defaultValue);
Static
$property->setIsStatic(true);
viernes 4 de marzo de 2011
52. Abstract
$method->setIsAbstract(true);
Static
$method->setIsStatic(true);
viernes 4 de marzo de 2011
54. $definition->setDocComment(<<<EOF
/**
* User Class.
*/
EOF
);
$method->setDocComment(<<<EOF
/**
* Set the username.
*
* @param string $username The username.
*/
EOF
);
viernes 4 de marzo de 2011
55. Then you can export them with the Dumper.
use MondongoMondatorDumper;
$dumper = new Dumper($definition);
$classCode = $dumper->dump();
echo $classCode;
viernes 4 de marzo de 2011
56. /**
* User entity.
*/
class User
{
protected $username;
/**
* Set the username.
*
* @param string $username The username.
*/
public function setUsername($username)
{
$this->username = $username;
}
/**
* Returns the username.
*
* @return string The username.
*/
public function getUsername()
{
return $this->username;
}
}
viernes 4 de marzo de 2011
57. And save them in files.
file_put_contents($file, $codeClass);
viernes 4 de marzo de 2011
59. Mondator Extensions
Mondator uses extensions to generate similar classes
in a powerful and flexible way.
viernes 4 de marzo de 2011
60. The Mondator Extensions process the config classes
to define what classes will be generated.
viernes 4 de marzo de 2011
61. ModelUser:
columns:
id: { id: auto, type: integer }
username: { type: string, length: 50 }
password: { type: string, length: 40 }
email: { type: string, length: 100 }
The Mondator Extensions process the config classes
to define what classes will be generated.
MondongoMondatorDefinitionDefinition
viernes 4 de marzo de 2011
62. ModelUser:
columns:
id: { id: auto, type: integer }
username: { type: string, length: 50 }
password: { type: string, length: 40 }
email: { type: string, length: 100 }
use MondongoMondatorExtension;
class Doctrator extends Extension
{
protected function doClassProcess()
{
$this->class;
$this->configClass;
$this->definitions;
}
}
viernes 4 de marzo de 2011
63. ModelUser:
columns:
id: { id: auto, type: integer }
username: { type: string, length: 50 }
password: { type: string, length: 40 }
email: { type: string, length: 100 }
use MondongoMondatorExtension;
class Doctrator extends Extension
{
protected function doClassProcess()
{
$this->class;
$this->configClass;
$this->definitions;
}
}
viernes 4 de marzo de 2011
64. ModelUser:
columns:
id: { id: auto, type: integer }
username: { type: string, length: 50 }
password: { type: string, length: 40 }
email: { type: string, length: 100 }
use MondongoMondatorExtension;
class Doctrator extends Extension
{
protected function doClassProcess()
{
$this->class;
$this->configClass;
$this->definitions;
}
}
viernes 4 de marzo de 2011
65. ModelUser:
columns:
id: { id: auto, type: integer }
username: { type: string, length: 50 }
password: { type: string, length: 40 }
email: { type: string, length: 100 }
use MondongoMondatorExtension;
class Doctrator extends Extension
{
protected function doClassProcess()
{
$this->class;
$this->configClass;
$this->definitions;
}
} Definitions to generate
viernes 4 de marzo de 2011
66. An extension can generate any definition.
viernes 4 de marzo de 2011
67. use MondongoMondatorExtension;
use MondongoMondatorDefinitionDefinition;
class Doctrator extends Extension
{
protected function doClassProcess()
{
$definition = new Definition($this->class);
$this->definitions['entity'] = $definition;
$definition = new Definition($this->class.'Repository');
$this->definitions['repository'] = $definition;
}
}
viernes 4 de marzo de 2011
68. foreach ($this->configClass['columns'] as $name => $column) {
$property = new Property('protected', $name);
$this->definitions['entity']->addProperty($property);
}
viernes 4 de marzo de 2011
69. foreach ($this->configClass['columns'] as $name => $column) {
$setterName = 'set'.Inflector::camelize($name);
$setter = new Method('public', $setterName, '$value', <<<EOF
$this->$name = $value;
EOF
);
$this->definitions['entity']->addMethod($setter);
}
viernes 4 de marzo de 2011
73. class Doctrator extends Extension
{
protected function setUp()
{
$this->addOptions(array(
'columns' => true,
'array_access' => true,
));
}
protected function doClassProcess()
{
if ($this->getOption('columns')) {
$this->processColumns();
}
if ($this->getOption('array_access')) {
$this->processArrayAccess();
}
}
}
viernes 4 de marzo de 2011
74. You can process the extensions that you want.
viernes 4 de marzo de 2011
75. $mondator = new MondongoMondatorMondator();
$mondator->setConfigClasses($configClasses);
$mondator->setExtensions(array(
new DoctratorExtensionCore($options),
new DoctratorExtensionArrayAccess(),
));
$mondator->process();
$article['title'] = 'Doctrator';
echo $article['title']; // Doctrator
viernes 4 de marzo de 2011
76. $mondator = new MondongoMondatorMondator();
$mondator->setConfigClasses($configClasses);
$mondator->setExtensions(array(
new DoctratorExtensionCore($options),
//new DoctratorExtensionArrayAccess(),
));
$mondator->process();
$article['title'] = 'Doctrator';
echo $article['title']; // Doctrator
viernes 4 de marzo de 2011
77. An extension can change the config class to extend
another extension.
viernes 4 de marzo de 2011
78. class Doctrator extends Extension
{
protected function doClassProcess()
{
foreach ($this->configClass['columns'] as $name => $column) {
// ...
}
}
}
class DateColumn extends Extension
{
protected function doConfigClassProcess()
{
$this->configClass['columns']['date'] = array(
'type' => 'date',
)
}
}
viernes 4 de marzo de 2011
79. You can even use extensions in the config classes.
viernes 4 de marzo de 2011
80. ModelArticle:
columns:
id: { id: auto, type: integer }
title: { type: string, length: 100 }
behaviors:
-
class: DoctratorBehaviorTimestampable
options: { }
viernes 4 de marzo de 2011
81. And you can combine all these things to do what you
want.
viernes 4 de marzo de 2011
82. Generated code is not necessarily magic code.
viernes 4 de marzo de 2011
84. Doctrator Extensions
Core
ArrayAccess PropertyOverloading ActiveRecord Behaviors
viernes 4 de marzo de 2011
85. Core
Generates and maps objects with Doctrine2.
viernes 4 de marzo de 2011
86. Doctrator uses base classes to separate generated
code from your code.
viernes 4 de marzo de 2011
87. ModelUser
namespace Model;
class User extends ModelBaseUser
{
// your code
}
namespace ModelBase;
class User
{
// generated code
}
viernes 4 de marzo de 2011
91. $category = new ModelCategory();
$category->setName('Class Generator');
$entityManager->persist($category);
$article = new ModelArticle();
$article->setTitle('Doctrator');
$article->setDate(new DateTime('now'));
$article->setCategory($category);
$entityManager->persist($article);
$entityManager->flush();
viernes 4 de marzo de 2011
92. Core
Useful methods.
viernes 4 de marzo de 2011
93. Set & Get by string
$article->set('title', 'Doctrator');
echo $article->get('title'); // Doctrator
viernes 4 de marzo de 2011
94. fromArray & toArray
$article->fromArray(array(
'title' => 'Doctrator',
'date' => new DateTime('now')
));
$array = $article->toArray();
viernes 4 de marzo de 2011
95. ArrayAccess
Implements the ArrayAccess interface in the entities.
viernes 4 de marzo de 2011
96. $article = new ModelArticle();
$article['title'] = 'Doctrator';
echo $article['title']; // Doctrator
viernes 4 de marzo de 2011
97. PropertyOverloading
Allows you access to entity data like properties.
viernes 4 de marzo de 2011
98. $article = new ModelArticle();
$article->title = 'Doctrator';
echo $article->title; // Doctrator
viernes 4 de marzo de 2011
99. ActiveRecord
Implements the ActiveRecord pattern in your entities.
viernes 4 de marzo de 2011
100. $article = new ModelArticle();
$article->setTitle('Doctrator');
$article->save();
$article->refresh();
$article->delete();
viernes 4 de marzo de 2011
102. print_r($article);
ModelArticle Object
(
[id:protected] => 1
[title:protected] => Doctrator
[content:protected] => Rocks!
)
Doctrator entities are clean even with ActiveRecord!
viernes 4 de marzo de 2011
103. $em = ModelArticle::entityManager();
$articleRepository = ModelArticle::repository();
$queryBuilder = ModelArticle::queryBuilder();
viernes 4 de marzo de 2011
104. $articles = $entityManager->getRepository('Model
Article')->findAll();
$article = $entityManager->getRepository('Model
Article')->find($id);
$articles = ModelArticle::repository()->findAll();
$article = ModelArticle::repository()->find($id);
viernes 4 de marzo de 2011
106. Behaviors
Reuse features.
viernes 4 de marzo de 2011
107. A behavior is simply a Mondator extension.
viernes 4 de marzo de 2011
108. A behavior can
Have options
Change config classes:
• Columns
• Associations
• Indexes
• Events
• ...
Add new generated classes
Add properties and methods
• Entities
• Repositories
• ...
viernes 4 de marzo de 2011
109. Timestampable
Saves the created and updated date.
created TRUE
created_column created_at
updated TRUE
updated_column updated_at
viernes 4 de marzo de 2011
110. ModelArticle:
columns:
id: { id: auto, type: integer }
title: { type: name, length: 100 }
behaviors:
- DoctratorBehaviorTimestampable
viernes 4 de marzo de 2011
111. $article = new ModelArticle();
$article->setTitle('Doctrator');
$article->save();
echo $article->getCreatedAt(); // now
echo $article->getUpdatedAt(); // null
$article->setContent('Rocks!');
$article->save();
echo $article->getCreatedAt(); // before
echo $article->getUpdatedAt(); // now
viernes 4 de marzo de 2011
112. Ipable
Saves the created and updated ip.
created TRUE
created_column created_from
updated TRUE
updated_column updated_from
viernes 4 de marzo de 2011
113. Hashable
Ipable
Saves a unique hash in each entity.
column hash
viernes 4 de marzo de 2011
114. ModelArticle:
columns:
id: { id: auto, type: integer }
title: { type: name, length: 100 }
behaviors:
- DoctratorBehaviorHashable
viernes 4 de marzo de 2011
115. $article = new Article();
$article->setTitle('Doctrator');
$entityManager->persist();
$entityManager->flush();
echo $article->getHash();
// da39a3ee5e6b4b0d3255bfef95601890afd80709
viernes 4 de marzo de 2011
116. Timestampable
Sluggable
Saves a slug from a field.
from_column *
slug_column slug
unique TRUE
update FALSE
viernes 4 de marzo de 2011
117. ModelArticle:
columns:
id: { id: auto, type: integer }
title: { type: name, length: 100 }
behaviors:
-
class: DoctratorBehaviorSluggable
options: { from_column: title }
viernes 4 de marzo de 2011
118. $article = new ModelArticle();
$article->setTitle('Doctrator Rocks!');
$article->save();
echo $article->getSlug(); // doctrator-rocks
viernes 4 de marzo de 2011
119. Sortable
Allows you to sort your entities.
column position
new_position bottom
viernes 4 de marzo de 2011
120. $articles = array();
for ($i = 0; $i <= 10; $i++) {
$articles[$i] = $a = new ModelArticle();
$a->setTitle('Article '.$i);
$a->save();
}
echo $articles[3]->getPosition(); // 3
echo $articles[6]->getPosition(); // 6
viernes 4 de marzo de 2011
121. // some methods
$articles[1]->isFirst();
$articles[1]->isLast();
$articles[1]->getNext();
$articles[1]->getPrevious();
$articles[1]->swapWith($articles[2]);
$articles[1]->moveUp();
$articles[1]->moveDown();
$repository->getMinPosition();
$repository->getMaxPosition();
viernes 4 de marzo de 2011
122. Taggable
Sortable
Allows you to save tags in the entities.
viernes 4 de marzo de 2011
123. $article = new ModelArticle();
$article->setTitle('My Title');
$article->save();
// methods
$article->addTags('foobar, barfoo');
$article->removeTags('foobar');
$article->removeAllTags(); // saved and not saved
$article->getSavedTags();
$article->getTags(); // saved and not saved
$article->setTags(array('foo', 'bar'));
$article->saveTags();
$repository->getTags();
$repository->getTagsWithCount();
viernes 4 de marzo de 2011
124. Translatable
Taggable
Sortable
Allows you to translate entity columns.
columns *
viernes 4 de marzo de 2011
126. $article = new ModelArticle();
$article->setDate(new DateTime());
// en
$article->translation('en')->setTitle('My Title');
$article->translation('en')->setContent('My Content');
// es
$article->translation('es')->setTitle('Mi Título');
$article->translation('es')->setContent('Mi Contenido');
$article->save();
viernes 4 de marzo de 2011
135. Questions?
http://mondongo.es (English :)
You can contact me for Mondongo, Doctrator, consulting, development
pablodip@gmail.com
viernes 4 de marzo de 2011