PageRenderTime 50ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/doc/blameable.md

http://github.com/l3pp4rd/DoctrineExtensions
Markdown | 645 lines | 514 code | 131 blank | 0 comment | 0 complexity | 5948c7c3b23f5ec4045a06eed4ec87bd MD5 | raw file
Possible License(s): LGPL-2.0
  1. # Blameable behavior extension for Doctrine 2
  2. **Blameable** behavior will automate the update of username or user reference fields
  3. on your Entities or Documents. It works through annotations and can update
  4. fields on creation, update, property subset update, or even on specific property value change.
  5. This is very similar to Timestampable but sets a string or user object for a user association.
  6. If you map the blame onto a string field, this extension will try to assign the user name.
  7. If you map the blame onto a association field, this extension will try to assign the user
  8. object to it.
  9. Note that you need to set the user on the BlameableListener (unless you use the
  10. Symfony2 extension which does automatically assign the current security context
  11. user).
  12. Features:
  13. - Automatic predefined user field update on creation, update, property subset update, and even on record property changes
  14. - ORM and ODM support using same listener
  15. - Specific annotations for properties, and no interface required
  16. - Can react to specific property or relation changes to specific value
  17. - Can be nested with other behaviors
  18. - Annotation, Yaml and Xml mapping support for extensions
  19. **Symfony:**
  20. - **Blameable** is available as [Bundle](http://github.com/stof/StofDoctrineExtensionsBundle)
  21. for **Symfony2**, together with all other extensions
  22. This article will cover the basic installation and functionality of **Blameable** behavior
  23. Content:
  24. - [Including](#including-extension) the extension
  25. - Entity [example](#entity-mapping)
  26. - Document [example](#document-mapping)
  27. - [Yaml](#yaml-mapping) mapping example
  28. - [Xml](#xml-mapping) mapping example
  29. - Advanced usage [examples](#advanced-examples)
  30. - Using [Traits](#traits)
  31. <a name="including-extension"></a>
  32. ## Setup and autoloading
  33. Read the [documentation](http://github.com/l3pp4rd/DoctrineExtensions/blob/master/doc/annotations.md#em-setup)
  34. or check the [example code](http://github.com/l3pp4rd/DoctrineExtensions/tree/master/example)
  35. on how to setup and use the extensions in most optimized way.
  36. <a name="entity-mapping"></a>
  37. ## Blameable Entity example:
  38. ### Blameable annotations:
  39. - **@Gedmo\Mapping\Annotation\Blameable** this annotation tells that this column is blameable
  40. by default it updates this column on update. If column is not a string field or an association
  41. it will trigger an exception.
  42. Available configuration options:
  43. - **on** - is main option and can be **create, update, change** this tells when it
  44. should be updated
  45. - **field** - only valid if **on="change"** is specified, tracks property or a list of properties for changes
  46. - **value** - only valid if **on="change"** is specified and the tracked field is a single field (not an array), if the tracked field has this **value**
  47. then it updates the blame
  48. **Note:** that Blameable interface is not necessary, except in cases there
  49. you need to identify entity as being Blameable. The metadata is loaded only once then
  50. cache is activated
  51. Column is a string field:
  52. ``` php
  53. <?php
  54. namespace Entity;
  55. use Gedmo\Mapping\Annotation as Gedmo;
  56. use Doctrine\ORM\Mapping as ORM;
  57. /**
  58. * @ORM\Entity
  59. */
  60. class Article
  61. {
  62. /** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
  63. private $id;
  64. /**
  65. * @ORM\Column(type="string", length=128)
  66. */
  67. private $title;
  68. /**
  69. * @ORM\Column(name="body", type="string")
  70. */
  71. private $body;
  72. /**
  73. * @var string $createdBy
  74. *
  75. * @Gedmo\Blameable(on="create")
  76. * @ORM\Column(type="string")
  77. */
  78. private $createdBy;
  79. /**
  80. * @var string $updatedBy
  81. *
  82. * @Gedmo\Blameable(on="update")
  83. * @ORM\Column(type="string")
  84. */
  85. private $updatedBy;
  86. /**
  87. * @var datetime $contentChangedBy
  88. *
  89. * @ORM\Column(name="content_changed_by", type="string", nullable=true)
  90. * @Gedmo\Timestampable(on="change", field={"title", "body"})
  91. */
  92. private $contentChangedBy;
  93. public function getId()
  94. {
  95. return $this->id;
  96. }
  97. public function setTitle($title)
  98. {
  99. $this->title = $title;
  100. }
  101. public function getTitle()
  102. {
  103. return $this->title;
  104. }
  105. public function setBody($body)
  106. {
  107. $this->body = $body;
  108. }
  109. public function getBody()
  110. {
  111. return $this->body;
  112. }
  113. public function getCreatedBy()
  114. {
  115. return $this->createdBy;
  116. }
  117. public function getUpdatedBy()
  118. {
  119. return $this->updatedBy;
  120. }
  121. public function getContentChangedBy()
  122. {
  123. return $this->contentChangedBy;
  124. }
  125. }
  126. ```
  127. Column is an association:
  128. ``` php
  129. <?php
  130. namespace Entity;
  131. use Gedmo\Mapping\Annotation as Gedmo;
  132. use Doctrine\ORM\Mapping as ORM;
  133. /**
  134. * @ORM\Entity
  135. */
  136. class Article
  137. {
  138. /** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
  139. private $id;
  140. /**
  141. * @ORM\Column(type="string", length=128)
  142. */
  143. private $title;
  144. /**
  145. * @ODM\String
  146. */
  147. private $body;
  148. /**
  149. * @var User $createdBy
  150. *
  151. * @Gedmo\Blameable(on="create")
  152. * @ORM\ManyToOne(targetEntity="Path\To\Entity\User")
  153. * @ORM\JoinColumn(name="created_by", referencedColumnName="id")
  154. */
  155. private $createdBy;
  156. /**
  157. * @var User $updatedBy
  158. *
  159. * @Gedmo\Blameable(on="update")
  160. * @ORM\ManyToOne(targetEntity="Path\To\Entity\User")
  161. * @ORM\JoinColumn(name="updated_by", referencedColumnName="id")
  162. */
  163. private $updatedBy;
  164. /**
  165. * @var User $contentChangedBy
  166. *
  167. * @Gedmo\Timestampable(on="change", fields={"title", "body"})
  168. * @ORM\ManyToOne(targetEntity="Path\To\Entity\User")
  169. * @ORM\JoinColumn(name="content_changed_by", referencedColumnName="id")
  170. */
  171. private $contentChangedBy;
  172. public function getId()
  173. {
  174. return $this->id;
  175. }
  176. public function setTitle($title)
  177. {
  178. $this->title = $title;
  179. }
  180. public function getTitle()
  181. {
  182. return $this->title;
  183. }
  184. public function setBody($body)
  185. {
  186. $this->body = $body;
  187. }
  188. public function getBody()
  189. {
  190. return $this->body;
  191. }
  192. public function getCreatedBy()
  193. {
  194. return $this->createdBy;
  195. }
  196. public function getUpdatedBy()
  197. {
  198. return $this->updatedBy;
  199. }
  200. public function getContentChangedBy()
  201. {
  202. return $this->contentChangedBy;
  203. }
  204. }
  205. ```
  206. <a name="document-mapping"></a>
  207. ## Blameable Document example:
  208. ``` php
  209. <?php
  210. namespace Document;
  211. use Gedmo\Mapping\Annotation as Gedmo;
  212. use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
  213. /**
  214. * @ODM\Document(collection="articles")
  215. */
  216. class Article
  217. {
  218. /** @ODM\Id */
  219. private $id;
  220. /**
  221. * @ODM\String
  222. */
  223. private $title;
  224. /**
  225. * @var string $createdBy
  226. *
  227. * @ODM\String
  228. * @Gedmo\Blameable(on="create")
  229. */
  230. private $createdBy;
  231. /**
  232. * @var string $updatedBy
  233. *
  234. * @ODM\String
  235. * @Gedmo\Blameable
  236. */
  237. private $updatedBy;
  238. public function getId()
  239. {
  240. return $this->id;
  241. }
  242. public function setTitle($title)
  243. {
  244. $this->title = $title;
  245. }
  246. public function getTitle()
  247. {
  248. return $this->title;
  249. }
  250. public function getCreatedBy()
  251. {
  252. return $this->createdBy;
  253. }
  254. public function getUpdatedBy()
  255. {
  256. return $this->updatedBy;
  257. }
  258. }
  259. ```
  260. Now on update and creation these annotated fields will be automatically updated
  261. <a name="yaml-mapping"></a>
  262. ## Yaml mapping example:
  263. Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml**
  264. ```
  265. ---
  266. Entity\Article:
  267. type: entity
  268. table: articles
  269. id:
  270. id:
  271. type: integer
  272. generator:
  273. strategy: AUTO
  274. fields:
  275. title:
  276. type: string
  277. length: 64
  278. createdBy:
  279. type: string
  280. gedmo:
  281. blameable:
  282. on: create
  283. updatedBy:
  284. type: string
  285. gedmo:
  286. blameable:
  287. on: update
  288. ```
  289. <a name="xml-mapping"></a>
  290. ## Xml mapping example
  291. ``` xml
  292. <?xml version="1.0" encoding="UTF-8"?>
  293. <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
  294. xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping">
  295. <entity name="Mapping\Fixture\Xml\Blameable" table="blameables">
  296. <id name="id" type="integer" column="id">
  297. <generator strategy="AUTO"/>
  298. </id>
  299. <field name="createdBy" type="string">
  300. <gedmo:blameable on="create"/>
  301. </field>
  302. <field name="updatedBy" type="string">
  303. <gedmo:blameable on="update"/>
  304. </field>
  305. <field name="publishedBy" type="string" nullable="true">
  306. <gedmo:blameable on="change" field="status.title" value="Published"/>
  307. </field>
  308. <many-to-one field="status" target-entity="Status">
  309. <join-column name="status_id" referenced-column-name="id"/>
  310. </many-to-one>
  311. </entity>
  312. </doctrine-mapping>
  313. ```
  314. <a name="advanced-examples"></a>
  315. ## Advanced examples:
  316. ### Using dependency of property changes
  317. Add another entity which would represent Article Type:
  318. ``` php
  319. <?php
  320. namespace Entity;
  321. use Doctrine\ORM\Mapping as ORM;
  322. /**
  323. * @ORM\Entity
  324. */
  325. class Type
  326. {
  327. /** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
  328. private $id;
  329. /**
  330. * @ORM\Column(type="string", length=128)
  331. */
  332. private $title;
  333. /**
  334. * @ORM\OneToMany(targetEntity="Article", mappedBy="type")
  335. */
  336. private $articles;
  337. public function getId()
  338. {
  339. return $this->id;
  340. }
  341. public function setTitle($title)
  342. {
  343. $this->title = $title;
  344. }
  345. public function getTitle()
  346. {
  347. return $this->title;
  348. }
  349. }
  350. ```
  351. Now update the Article Entity to reflect publishedBy on Type change:
  352. ``` php
  353. <?php
  354. namespace Entity;
  355. use Gedmo\Mapping\Annotation as Gedmo;
  356. use Doctrine\ORM\Mapping as ORM;
  357. /**
  358. * @ORM\Entity
  359. */
  360. class Article
  361. {
  362. /** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
  363. private $id;
  364. /**
  365. * @ORM\Column(type="string", length=128)
  366. */
  367. private $title;
  368. /**
  369. * @var string $createdBy
  370. *
  371. * @Gedmo\Blameable(on="create")
  372. * @ORM\Column(type="string")
  373. */
  374. private $createdBy;
  375. /**
  376. * @var string $updatedBy
  377. *
  378. * @Gedmo\Blameable(on="update")
  379. * @ORM\Column(type="string")
  380. */
  381. private $updatedBy;
  382. /**
  383. * @ORM\ManyToOne(targetEntity="Type", inversedBy="articles")
  384. */
  385. private $type;
  386. /**
  387. * @var string $publishedBy
  388. *
  389. * @ORM\Column(type="string", nullable=true)
  390. * @Gedmo\Blameable(on="change", field="type.title", value="Published")
  391. */
  392. private $publishedBy;
  393. public function setType($type)
  394. {
  395. $this->type = $type;
  396. }
  397. public function getId()
  398. {
  399. return $this->id;
  400. }
  401. public function setTitle($title)
  402. {
  403. $this->title = $title;
  404. }
  405. public function getTitle()
  406. {
  407. return $this->title;
  408. }
  409. public function getCreatedBy()
  410. {
  411. return $this->createdBy;
  412. }
  413. public function getUpdatedBy()
  414. {
  415. return $this->updatedBy;
  416. }
  417. public function getPublishedBy()
  418. {
  419. return $this->publishedBy;
  420. }
  421. }
  422. ```
  423. Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml**
  424. ```
  425. ---
  426. Entity\Article:
  427. type: entity
  428. table: articles
  429. id:
  430. id:
  431. type: integer
  432. generator:
  433. strategy: AUTO
  434. fields:
  435. title:
  436. type: string
  437. length: 64
  438. createdBy:
  439. type: string
  440. gedmo:
  441. blameable:
  442. on: create
  443. updatedBy:
  444. type: string
  445. gedmo:
  446. blameable:
  447. on: update
  448. publishedBy:
  449. type: string
  450. gedmo:
  451. blameable:
  452. on: change
  453. field: type.title
  454. value: Published
  455. manyToOne:
  456. type:
  457. targetEntity: Entity\Type
  458. inversedBy: articles
  459. ```
  460. Now few operations to get it all done:
  461. ``` php
  462. <?php
  463. $article = new Article;
  464. $article->setTitle('My Article');
  465. $em->persist($article);
  466. $em->flush();
  467. // article: $createdBy, $updatedBy were set
  468. $type = new Type;
  469. $type->setTitle('Published');
  470. $article = $em->getRepository('Entity\Article')->findByTitle('My Article');
  471. $article->setType($type);
  472. $em->persist($article);
  473. $em->persist($type);
  474. $em->flush();
  475. // article: $publishedBy, $updatedBy were set
  476. $article->getPublishedBy(); // the user that published this article
  477. ```
  478. Easy like that, any suggestions on improvements are very welcome
  479. <a name="traits"></a>
  480. ## Traits
  481. You can use blameable traits for quick **createdBy** **updatedBy** string definitions
  482. when using annotation mapping.
  483. There is also a trait without annotations for easy integration purposes.
  484. **Note:** this feature is only available since php **5.4.0**. And you are not required
  485. to use the Traits provided by extensions.
  486. ``` php
  487. <?php
  488. namespace Blameable\Fixture;
  489. use Gedmo\Blameable\Traits\BlameableEntity;
  490. use Doctrine\ORM\Mapping as ORM;
  491. /**
  492. * @ORM\Entity
  493. */
  494. class UsingTrait
  495. {
  496. /**
  497. * Hook blameable behavior
  498. * updates createdBy, updatedBy fields
  499. */
  500. use BlameableEntity;
  501. /**
  502. * @ORM\Id
  503. * @ORM\GeneratedValue
  504. * @ORM\Column(type="integer")
  505. */
  506. private $id;
  507. /**
  508. * @ORM\Column(length=128)
  509. */
  510. private $title;
  511. }
  512. ```
  513. The Traits are very simplistic - if you use different field names it is recommended to simply create your
  514. own Traits specific to your project. The ones provided by this bundle can be used as example.