PageRenderTime 28ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Driver/Annotation.php

https://bitbucket.org/hanutimes/hanutimes
PHP | 247 lines | 162 code | 23 blank | 62 comment | 44 complexity | 7f9f2d994f5ef8da3b1b04ab1b1780e2 MD5 | raw file
  1. <?php
  2. namespace Gedmo\Tree\Mapping\Driver;
  3. use Gedmo\Mapping\Driver\AbstractAnnotationDriver,
  4. Gedmo\Exception\InvalidMappingException,
  5. Gedmo\Tree\Mapping\Validator;
  6. /**
  7. * This is an annotation mapping driver for Tree
  8. * behavioral extension. Used for extraction of extended
  9. * metadata from Annotations specificaly for Tree
  10. * extension.
  11. *
  12. * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
  13. * @author <rocco@roccosportal.com>
  14. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  15. */
  16. class Annotation extends AbstractAnnotationDriver
  17. {
  18. /**
  19. * Annotation to define the tree type
  20. */
  21. const TREE = 'Gedmo\\Mapping\\Annotation\\Tree';
  22. /**
  23. * Annotation to mark field as one which will store left value
  24. */
  25. const LEFT = 'Gedmo\\Mapping\\Annotation\\TreeLeft';
  26. /**
  27. * Annotation to mark field as one which will store right value
  28. */
  29. const RIGHT = 'Gedmo\\Mapping\\Annotation\\TreeRight';
  30. /**
  31. * Annotation to mark relative parent field
  32. */
  33. const PARENT = 'Gedmo\\Mapping\\Annotation\\TreeParent';
  34. /**
  35. * Annotation to mark node level
  36. */
  37. const LEVEL = 'Gedmo\\Mapping\\Annotation\\TreeLevel';
  38. /**
  39. * Annotation to mark field as tree root
  40. */
  41. const ROOT = 'Gedmo\\Mapping\\Annotation\\TreeRoot';
  42. /**
  43. * Annotation to specify closure tree class
  44. */
  45. const CLOSURE = 'Gedmo\\Mapping\\Annotation\\TreeClosure';
  46. /**
  47. * Annotation to specify path class
  48. */
  49. const PATH = 'Gedmo\\Mapping\\Annotation\\TreePath';
  50. /**
  51. * Annotation to specify path source class
  52. */
  53. const PATH_SOURCE = 'Gedmo\\Mapping\\Annotation\\TreePathSource';
  54. /**
  55. * Annotation to specify path hash class
  56. */
  57. const PATH_HASH = 'Gedmo\\Mapping\\Annotation\\TreePathHash';
  58. /**
  59. * Annotation to mark the field to be used to hold the lock time
  60. */
  61. const LOCK_TIME = 'Gedmo\\Mapping\\Annotation\\TreeLockTime';
  62. /**
  63. * List of tree strategies available
  64. *
  65. * @var array
  66. */
  67. protected $strategies = array(
  68. 'nested',
  69. 'closure',
  70. 'materializedPath'
  71. );
  72. /**
  73. * {@inheritDoc}
  74. */
  75. public function readExtendedMetadata($meta, array &$config)
  76. {
  77. $validator = new Validator();
  78. $class = $this->getMetaReflectionClass($meta);
  79. // class annotations
  80. if ($annot = $this->reader->getClassAnnotation($class, self::TREE)) {
  81. if (!in_array($annot->type, $this->strategies)) {
  82. throw new InvalidMappingException("Tree type: {$annot->type} is not available.");
  83. }
  84. $config['strategy'] = $annot->type;
  85. $config['activate_locking'] = $annot->activateLocking;
  86. $config['locking_timeout'] = (int) $annot->lockingTimeout;
  87. if ($config['locking_timeout'] < 1) {
  88. throw new InvalidMappingException("Tree Locking Timeout must be at least of 1 second.");
  89. }
  90. }
  91. if ($annot = $this->reader->getClassAnnotation($class, self::CLOSURE)) {
  92. if (!class_exists($annot->class)) {
  93. throw new InvalidMappingException("Tree closure class: {$annot->class} does not exist.");
  94. }
  95. $config['closure'] = $annot->class;
  96. }
  97. // property annotations
  98. foreach ($class->getProperties() as $property) {
  99. if ($meta->isMappedSuperclass && !$property->isPrivate() ||
  100. $meta->isInheritedField($property->name) ||
  101. isset($meta->associationMappings[$property->name]['inherited'])
  102. ) {
  103. continue;
  104. }
  105. // left
  106. if ($this->reader->getPropertyAnnotation($property, self::LEFT)) {
  107. $field = $property->getName();
  108. if (!$meta->hasField($field)) {
  109. throw new InvalidMappingException("Unable to find 'left' - [{$field}] as mapped property in entity - {$meta->name}");
  110. }
  111. if (!$validator->isValidField($meta, $field)) {
  112. throw new InvalidMappingException("Tree left field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}");
  113. }
  114. $config['left'] = $field;
  115. }
  116. // right
  117. if ($this->reader->getPropertyAnnotation($property, self::RIGHT)) {
  118. $field = $property->getName();
  119. if (!$meta->hasField($field)) {
  120. throw new InvalidMappingException("Unable to find 'right' - [{$field}] as mapped property in entity - {$meta->name}");
  121. }
  122. if (!$validator->isValidField($meta, $field)) {
  123. throw new InvalidMappingException("Tree right field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}");
  124. }
  125. $config['right'] = $field;
  126. }
  127. // ancestor/parent
  128. if ($this->reader->getPropertyAnnotation($property, self::PARENT)) {
  129. $field = $property->getName();
  130. if (!$meta->isSingleValuedAssociation($field)) {
  131. throw new InvalidMappingException("Unable to find ancestor/parent child relation through ancestor field - [{$field}] in class - {$meta->name}");
  132. }
  133. $config['parent'] = $field;
  134. }
  135. // root
  136. if ($this->reader->getPropertyAnnotation($property, self::ROOT)) {
  137. $field = $property->getName();
  138. if (!$meta->hasField($field)) {
  139. throw new InvalidMappingException("Unable to find 'root' - [{$field}] as mapped property in entity - {$meta->name}");
  140. }
  141. if (!$validator->isValidFieldForRoot($meta, $field)) {
  142. throw new InvalidMappingException("Tree root field - [{$field}] type is not valid and must be any of the 'integer' types or 'string' in class - {$meta->name}");
  143. }
  144. $config['root'] = $field;
  145. }
  146. // level
  147. if ($this->reader->getPropertyAnnotation($property, self::LEVEL)) {
  148. $field = $property->getName();
  149. if (!$meta->hasField($field)) {
  150. throw new InvalidMappingException("Unable to find 'level' - [{$field}] as mapped property in entity - {$meta->name}");
  151. }
  152. if (!$validator->isValidField($meta, $field)) {
  153. throw new InvalidMappingException("Tree level field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}");
  154. }
  155. $config['level'] = $field;
  156. }
  157. // path
  158. if ($pathAnnotation = $this->reader->getPropertyAnnotation($property, self::PATH)) {
  159. $field = $property->getName();
  160. if (!$meta->hasField($field)) {
  161. throw new InvalidMappingException("Unable to find 'path' - [{$field}] as mapped property in entity - {$meta->name}");
  162. }
  163. if (!$validator->isValidFieldForPath($meta, $field)) {
  164. throw new InvalidMappingException("Tree Path field - [{$field}] type is not valid. It must be string or text in class - {$meta->name}");
  165. }
  166. if (strlen($pathAnnotation->separator) > 1) {
  167. throw new InvalidMappingException("Tree Path field - [{$field}] Separator {$pathAnnotation->separator} is invalid. It must be only one character long.");
  168. }
  169. $config['path'] = $field;
  170. $config['path_separator'] = $pathAnnotation->separator;
  171. $config['path_append_id'] = $pathAnnotation->appendId;
  172. $config['path_starts_with_separator'] = $pathAnnotation->startsWithSeparator;
  173. $config['path_ends_with_separator'] = $pathAnnotation->endsWithSeparator;
  174. }
  175. // path source
  176. if ($this->reader->getPropertyAnnotation($property, self::PATH_SOURCE)) {
  177. $field = $property->getName();
  178. if (!$meta->hasField($field)) {
  179. throw new InvalidMappingException("Unable to find 'path_source' - [{$field}] as mapped property in entity - {$meta->name}");
  180. }
  181. if (!$validator->isValidFieldForPathSource($meta, $field)) {
  182. throw new InvalidMappingException("Tree PathSource field - [{$field}] type is not valid. It can be any of the integer variants, double, float or string in class - {$meta->name}");
  183. }
  184. $config['path_source'] = $field;
  185. }
  186. // path hash
  187. if ($this->reader->getPropertyAnnotation($property, self::PATH_HASH)) {
  188. $field = $property->getName();
  189. if (!$meta->hasField($field)) {
  190. throw new InvalidMappingException("Unable to find 'path_hash' - [{$field}] as mapped property in entity - {$meta->name}");
  191. }
  192. if (!$validator->isValidFieldForPathHash($meta, $field)) {
  193. throw new InvalidMappingException("Tree PathHash field - [{$field}] type is not valid. It can be any of the integer variants, double, float or string in class - {$meta->name}");
  194. }
  195. $config['path_hash'] = $field;
  196. }
  197. // lock time
  198. if ($this->reader->getPropertyAnnotation($property, self::LOCK_TIME)) {
  199. $field = $property->getName();
  200. if (!$meta->hasField($field)) {
  201. throw new InvalidMappingException("Unable to find 'lock_time' - [{$field}] as mapped property in entity - {$meta->name}");
  202. }
  203. if (!$validator->isValidFieldForLockTime($meta, $field)) {
  204. throw new InvalidMappingException("Tree PathSource field - [{$field}] type is not valid. It must be \"date\" in class - {$meta->name}");
  205. }
  206. $config['lock_time'] = $field;
  207. }
  208. }
  209. if (isset($config['activate_locking']) && $config['activate_locking'] && !isset($config['lock_time'])) {
  210. throw new InvalidMappingException("You need to map a date field as the tree lock time field to activate locking support.");
  211. }
  212. if (!$meta->isMappedSuperclass && $config) {
  213. if (isset($config['strategy'])) {
  214. if (is_array($meta->identifier) && count($meta->identifier) > 1) {
  215. throw new InvalidMappingException("Tree does not support composite identifiers in class - {$meta->name}");
  216. }
  217. $method = 'validate' . ucfirst($config['strategy']) . 'TreeMetadata';
  218. $validator->$method($meta, $config);
  219. } else {
  220. throw new InvalidMappingException("Cannot find Tree type for class: {$meta->name}");
  221. }
  222. }
  223. }
  224. }