Doctrator Symfony Live 2011 San Francisco

  • 5,100 views
Uploaded on

 

More in: Technology , Business
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
5,100
On Slideshare
0
From Embeds
0
Number of Embeds
4

Actions

Shares
Downloads
0
Comments
0
Likes
10

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Doctrator Pablo DíezSymfony Live 2011 - San Francisco
  • 2. Pablo DíezCreator of Mondongo ODM for MongoDB and PHP http://mondongo.es (in English :)Creator of Mondator Class generator for PHPCreator of Doctratorhttp://twitter.com/pablodiphttp://github.com/pablodip
  • 3. What is Doctrator?
  • 4. Doctrator = Doctrine2 + Mondator
  • 5. Agile Development
  • 6. Agile Development ActiveRecord
  • 7. Agile Development ActiveRecord optional
  • 8. Agile Development ActiveRecord Behaviors
  • 9. Agile Development ActiveRecord BehaviorsDoctrator saves you a lot of time! ;)
  • 10. How does Doctrine2 work?
  • 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
  • 12. That is, persist PHP objects without restrictions of a base class, properties, methods. namespace Model; class User { public $id; public $username; public $email; }
  • 13. You only have to tell Doctrine2 (map) what you want to persist.
  • 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; }
  • 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) }
  • 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>
  • 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 ));
  • 18. You only have to tell Doctrine2 (map) what you want to persist. Then you are able to persist those objects.
  • 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();
  • 20. A Doctrine2 best practice is to use non public properties in the entities. class User { protected $id; protected $username; protected $password; protected $email; }
  • 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;
  • 22. What do you need to work with this simple table? user id integer username string password string email string
  • 23. namespace Model; class User { } Class user id integerusername stringpassword string email string
  • 24. namespace Model;protected $id; class Userprotected $username; {protected $password; }protected $email; Class Properties user id integer username string password string email string
  • 25. namespace Model; protected $id; class User protected $username; { protected $password; } protected $email; Class Properties user id integer username stringpublic function setId($id){} $this->id = $id; password stringpublic 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;
  • 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;
  • 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; } }
  • 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; } }
  • 29. LORCLines Of Repetitive Code
  • 30. LORC Lines Of Repetitive Code... and we still don’t have any features! :)
  • 31. How many LORC do we need in a real database?
  • 32. How many LORC do we need in a real database? ...
  • 33. Doctrator’s principle is to avoid writing LORC
  • 34. What does Doctrator do?
  • 35. What does Doctrator do?Doctrator generates classes and maps them with Doctrine2
  • 36. Doctrator generates classes and maps them with Doctrine2 You only have to tell it the configuration of the classes.
  • 37. In PHParray( ModelUser => array( columns => array( id => array(id => auto, type => integer), username => array(type => string, length => 50), password => array(type => string, length => 40), email => array(type => string, length => 100), ), ),);
  • 38. In YAMLModelUser: columns: id: { id: auto, type: integer } username: { type: string, length: 50 } password: { type: string, length: 40 } email: { type: string, length: 100 }
  • 39. ModelUser: columns: id: { id: auto, type: integer } username: { type: string, length: 50 } password: { type: string, length: 40 } email: { type: string, length: 100 }This generates your object. This maps your object. class User { protected $id; protected $username; /** protected $password; * @Entity protected $email; */ public function setId($id) { $this->id = $id; } /** public function getId() * @Id { return $this->id; * @Column(type="integer") } */ public function setUsername($username) { $this->username = $username; } /** public function getUsername() * @Column(length=50) { return $this->username; */ } public function setPassword($password) { /** } $this->password = $password; * @Column(length=100) public function getPassword() */ { return $this->password; } public function setEmail($email)
  • 40. ModelUser: columns: id: { id: auto, type: integer } username: { type: string, length: 50 } password: { type: string, length: 40 } email: { type: string, length: 100 } You can start to work.$user = new ModelUser();$user->setUsername(pagafantas);$user->setEmail(pagafantas@gmail.com);$entityManager->persist($user);$entityManager->flush();
  • 41. How does Doctrator work?
  • 42. How does Doctrator work?Doctrator uses Mondator to generate the classes for you.
  • 43. Mondator defines PHP classes.
  • 44. namespace Model;class User{ protected $username; public function setUsername($username) { $this->username = $username; } public function getUsername() { return $this->username; }}
  • 45. namespace Model; Definitionclass User{ Properties protected $username; public function setUsername($username) { $this->username = $username; } public function getUsername() { return $this->username; }} Methods
  • 46. use MondongoMondatorDefinitionDefinition;use MondongoMondatorDefinitionProperty;use MondongoMondatorDefinitionMethod;
  • 47. namespace Model;class User{} Full Class Name$definition = new Definition(ModelUser);
  • 48. protected $username; Visibility Name$property = new Property(protected, username);$definition->addProperty($property);
  • 49. 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);
  • 50. You can define any PHP class.
  • 51. Parent class$definition->setParentClass(ModelBaseUser); Interfaces$definition->addInterface(ArrayAccess); Abstract$definition->setIsAbstract(true);
  • 52. Default value$property->setValue($defaultValue); Static$property->setIsStatic(true);
  • 53. Abstract$method->setIsAbstract(true); Static$method->setIsStatic(true);
  • 54. Even with comments.
  • 55. $definition->setDocComment(<<<EOF/** * User Class. */EOF);$method->setDocComment(<<<EOF /** * Set the username. * * @param string $username The username. */EOF);
  • 56. Then you can export them with the Dumper. use MondongoMondatorDumper; $dumper = new Dumper($definition); $classCode = $dumper->dump(); echo $classCode;
  • 57. /** * 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; }}
  • 58. And save them in files.file_put_contents($file, $codeClass);
  • 59. Mondator Extensions
  • 60. Mondator ExtensionsMondator uses extensions to generate similar classes in a powerful and flexible way.
  • 61. The Mondator Extensions process the config classes to define what classes will be generated.
  • 62. 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
  • 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; }}
  • 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; }}
  • 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; }}
  • 66. 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
  • 67. An extension can generate any definition.
  • 68. 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; }}
  • 69. foreach ($this->configClass[columns] as $name => $column) { $property = new Property(protected, $name); $this->definitions[entity]->addProperty($property);}
  • 70. 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);}
  • 71. Different extensions can modify the same definition.
  • 72. class Doctrator extends Extension{ protected function doClassProcess() { $definition = new Definition($this->class); $this->definitions[entity] = $defintion; $this->processColumns(); }}class ArrayAccess extends Extension{ protected function doClassProcess() { $this->definitions[entity]->addInterface(ArrayAccess); $method = new Method(public, offsetGet, $name, $code); $this->definitions[entity]->addMethod($method); // ... }}
  • 73. An extension can have options.
  • 74. 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(); } }}
  • 75. You can process the extensions that you want.
  • 76. $mondator = new MondongoMondatorMondator();$mondator->setConfigClasses($configClasses);$mondator->setExtensions(array( new DoctratorExtensionCore($options),));$mondator->process();
  • 77. $mondator = new MondongoMondatorMondator();$mondator->setConfigClasses($configClasses);$mondator->setExtensions(array( new DoctratorExtensionCore($options), new DoctratorExtensionArrayAccess(),));$mondator->process();
  • 78. $mondator = new MondongoMondatorMondator();$mondator->setConfigClasses($configClasses);$mondator->setExtensions(array( new DoctratorExtensionCore($options), new DoctratorExtensionArrayAccess(),));$mondator->process();$article[title] = Doctrator;echo $article[title]; // Doctrator
  • 79. $mondator = new MondongoMondatorMondator();$mondator->setConfigClasses($configClasses);$mondator->setExtensions(array( new DoctratorExtensionCore($options), //new DoctratorExtensionArrayAccess(),));$mondator->process();$article[title] = Doctrator;echo $article[title]; // Doctrator
  • 80. An extension can change the config class to extend another extension.
  • 81. 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, ) }}
  • 82. You can even use extensions in the config classes.
  • 83. ModelArticle: columns: id: { id: auto, type: integer } title: { type: string, length: 100 } behaviors: - class: DoctratorBehaviorTimestampable options: { }
  • 84. And you can combine all these things to do what you want.
  • 85. Generated code is not necessarily magic code.
  • 86. Doctrator’s generated code is really simple, non- magical code.
  • 87. Code even with PHPDoc.
  • 88. You can use IDE Autocompletion.
  • 89. Doctrator Extensions
  • 90. Doctrator Extensions CoreArrayAccess PropertyOverloading ActiveRecord Behaviors
  • 91. CoreGenerates and maps objects with Doctrine2.
  • 92. Doctrator uses base classes to separate generated code from your code.
  • 93. ModelUsernamespace Model;class User extends ModelBaseUser{ // your code}namespace ModelBase;class User{ // generated code}
  • 94. ModelArticle: table_name: articles columns: id: { id: auto, type: integer } title: { type: string, length: 100 } slug: { type: string, length: 100 } content: { type: string } is_active: { type: boolean, default: true } date: { type: date } many_to_one: category: { class: ModelCategory, inversed: articles } indexes: slug: { columns: [slug], unique: true } date: { columns: [is_active, date] } events: preUpdate: [updateDate]
  • 95. AssociationsModelArticle: table_name: articles one_to_one one_to_many columns: many_to_one id: { id: auto,many_to_many type: integer } title: { type: string, length: 100 } slug: { type: string, length: 100 } name content: { type: string class } mapped is_active: { type: boolean, default: true } date: { type: date }inversed many_to_one: category: { class: ModelCategory, inversed: articles } indexes: slug: { columns: [slug], unique: true } date: { columns: [is_active, date] } events: preUpdate: [updateDate]
  • 96. ModelArticle: Events table_name: articles columns: prePersist id: { id: auto, type: integer } postPersist title: { type: string, length: 100 } preUpdate slug: postUpdate { type: string, length: 100 } preRemove content: { type: string } postRemove is_active: { type: boolean, default: true } postLoad date: { type: date } many_to_one: category: { class: ModelCategory, inversed: articles } indexes: slug: { columns: [slug], unique: true } date: { columns: [is_active, date] } events: preUpdate: [updateDate]
  • 97. $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();
  • 98. CoreUseful methods.
  • 99. Set & Get by string$article->set(title, Doctrator);echo $article->get(title); // Doctrator
  • 100. fromArray & toArray$article->fromArray(array( title => Doctrator, date => new DateTime(now)));$array = $article->toArray();
  • 101. ArrayAccessImplements the ArrayAccess interface in the entities.
  • 102. $article = new ModelArticle();$article[title] = Doctrator;echo $article[title]; // Doctrator
  • 103. PropertyOverloadingAllows you access to entity data like properties.
  • 104. $article = new ModelArticle();$article->title = Doctrator;echo $article->title; // Doctrator
  • 105. ActiveRecordImplements the ActiveRecord pattern in your entities.
  • 106. $article = new ModelArticle();$article->setTitle(Doctrator);$article->save();$article->refresh();$article->delete();
  • 107. print_r($article);
  • 108. print_r($article); ModelArticle Object ( [id:protected] => 1 [title:protected] => Doctrator [content:protected] => Rocks! )Doctrator entities are clean even with ActiveRecord!
  • 109. Doctrator uses a global object to save the EntityManager use DoctratorEntityManagerContainer; EntityManagerContainer::set($entityManager);
  • 110. $em = ModelArticle::entityManager();$articleRepository = ModelArticle::repository();$queryBuilder = ModelArticle::queryBuilder();
  • 111. $articles = $entityManager->getRepository(ModelArticle)->findAll();$article = $entityManager->getRepository(ModelArticle)->find($id);$articles = ModelArticle::repository()->findAll();$article = ModelArticle::repository()->find($id);
  • 112. Behaviors
  • 113. BehaviorsReuse features.
  • 114. A behavior is simply a Mondator extension.
  • 115. A behavior canHave optionsChange config classes: • Columns • Associations • Indexes • Events • ...Add new generated classesAdd properties and methods • Entities • Repositories • ...
  • 116. TimestampableSaves the created and updated date. created TRUE created_column created_at updated TRUE updated_column updated_at
  • 117. ModelArticle: columns: id: { id: auto, type: integer } title: { type: name, length: 100 } behaviors: - DoctratorBehaviorTimestampable
  • 118. $article = new ModelArticle();$article->setTitle(Doctrator);$article->save();echo $article->getCreatedAt(); // nowecho $article->getUpdatedAt(); // null$article->setContent(Rocks!);$article->save();echo $article->getCreatedAt(); // beforeecho $article->getUpdatedAt(); // now
  • 119. IpableSaves the created and updated ip. created TRUE created_column created_from updated TRUE updated_column updated_from
  • 120. Hashable IpableSaves a unique hash in each entity. column hash
  • 121. ModelArticle: columns: id: { id: auto, type: integer } title: { type: name, length: 100 } behaviors: - DoctratorBehaviorHashable
  • 122. $article = new Article();$article->setTitle(Doctrator);$entityManager->persist();$entityManager->flush();echo $article->getHash();// da39a3ee5e6b4b0d3255bfef95601890afd80709
  • 123. Timestampable SluggableSaves a slug from a field. from_column * slug_column slug unique TRUE update FALSE
  • 124. ModelArticle: columns: id: { id: auto, type: integer } title: { type: name, length: 100 } behaviors: - class: DoctratorBehaviorSluggable options: { from_column: title }
  • 125. $article = new ModelArticle();$article->setTitle(Doctrator Rocks!);$article->save();echo $article->getSlug(); // doctrator-rocks
  • 126. SortableAllows you to sort your entities. column position new_position bottom
  • 127. $articles = array();for ($i = 0; $i <= 10; $i++) { $articles[$i] = $a = new ModelArticle(); $a->setTitle(Article .$i); $a->save();}echo $articles[3]->getPosition(); // 3echo $articles[6]->getPosition(); // 6
  • 128. // 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();
  • 129. Taggable SortableAllows you to save tags in the entities.
  • 130. $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();
  • 131. Translatable Taggable SortableAllows you to translate entity columns. columns *
  • 132. ModelArticle: columns: id: { id: auto, type: integer } title: { type: string, length: 100 } content: { type: string } date: { type: date } behaviors: - class: DoctratorBehaviorTranslatable options: { columns: [title, content] }
  • 133. $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();
  • 134. Doctrator in Symfony2
  • 135. DoctratorBundle
  • 136. doctrator.config: extensions: array_access: false property_overloading: false active_record: true behaviors: true validation: true
  • 137. doctrator.config: extensions: array_access: false property_overloading: false active_record: true behaviors: true validation: true my_extension_id: true
  • 138. Config Classes app/config/doctrator/*.yml*Bundle/Resources/doctrator/*.yml
  • 139. Standard NamespaceModelArticle: columns: id: { id: auto, type: integer } title: { type: string, length: 100 } content: { type: string }ModelDoctratorUserBundleUser: columns: id: { id: auto, type: integer } username: { type: string, length: 20 }
  • 140. ModelArticle: validation: - MyArticleClassValidator: ~ columns: id: { id: auto, type: integer } title: { type: string, length: 100 } content: { type: string, validation: [MaxLength: 2000] } Validation integrated
  • 141. php app/console doctrator:generate
  • 142. Questions? http://mondongo.es (English :)You can contact me for Mondongo, Doctrator, consulting, development pablodip@gmail.com