• Save
Doctrator Symfony Live 2011 San Francisco
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Doctrator Symfony Live 2011 San Francisco

on

  • 5,552 views

 

Statistics

Views

Total Views
5,552
Views on SlideShare
4,622
Embed Views
930

Actions

Likes
10
Downloads
0
Comments
0

19 Embeds 930

http://www.symfony-project.org 236
http://swik.net 200
http://gpupo.com 174
http://www.symfony.es 135
http://symfony.com 103
http://test.ical.ly 27
http://static.slidesharecdn.com 14
http://rimzy.net 12
http://www.testically.org 7
http://www.sfexception.com 6
http://twitter.com 4
http://translate.googleusercontent.com 3
http://matmati.net 3
http://www.symfony.com 1
http://www.netvibes.com 1
http://feeds.feedburner.com 1
https://twitter.com 1
http://www.phpframeworks.com 1
http://www.alertize.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Doctrator Symfony Live 2011 San Francisco Presentation 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