PageRenderTime 26ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://bitbucket.org/cryofrost/portal
PHP | 267 lines | 150 code | 21 blank | 96 comment | 32 complexity | ff3025e428e6a422f0ba3cb3bf572b25 MD5 | raw file
Possible License(s): Apache-2.0, JSON, LGPL-2.1, LGPL-2.0, LGPL-3.0, BSD-3-Clause, BSD-2-Clause
  1. <?php
  2. namespace Gedmo\Tree\Mapping\Driver;
  3. use Gedmo\Mapping\Driver\AnnotationDriverInterface,
  4. Doctrine\Common\Persistence\Mapping\ClassMetadata,
  5. Gedmo\Exception\InvalidMappingException;
  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. * @package Gedmo.Tree.Mapping.Driver
  14. * @subpackage Annotation
  15. * @link http://www.gediminasm.org
  16. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  17. */
  18. class Annotation implements AnnotationDriverInterface
  19. {
  20. /**
  21. * Annotation to define the tree type
  22. */
  23. const TREE = 'Gedmo\\Mapping\\Annotation\\Tree';
  24. /**
  25. * Annotation to mark field as one which will store left value
  26. */
  27. const LEFT = 'Gedmo\\Mapping\\Annotation\\TreeLeft';
  28. /**
  29. * Annotation to mark field as one which will store right value
  30. */
  31. const RIGHT = 'Gedmo\\Mapping\\Annotation\\TreeRight';
  32. /**
  33. * Annotation to mark relative parent field
  34. */
  35. const PARENT = 'Gedmo\\Mapping\\Annotation\\TreeParent';
  36. /**
  37. * Annotation to mark node level
  38. */
  39. const LEVEL = 'Gedmo\\Mapping\\Annotation\\TreeLevel';
  40. /**
  41. * Annotation to mark field as tree root
  42. */
  43. const ROOT = 'Gedmo\\Mapping\\Annotation\\TreeRoot';
  44. /**
  45. * Annotation to specify closure tree class
  46. */
  47. const CLOSURE = 'Gedmo\\Mapping\\Annotation\\TreeClosure';
  48. /**
  49. * List of types which are valid for tree fields
  50. *
  51. * @var array
  52. */
  53. private $validTypes = array(
  54. 'integer',
  55. 'smallint',
  56. 'bigint'
  57. );
  58. /**
  59. * List of tree strategies available
  60. *
  61. * @var array
  62. */
  63. private $strategies = array(
  64. 'nested',
  65. 'closure'
  66. );
  67. /**
  68. * Annotation reader instance
  69. *
  70. * @var object
  71. */
  72. private $reader;
  73. /**
  74. * original driver if it is available
  75. */
  76. protected $_originalDriver = null;
  77. /**
  78. * {@inheritDoc}
  79. */
  80. public function setAnnotationReader($reader)
  81. {
  82. $this->reader = $reader;
  83. }
  84. /**
  85. * {@inheritDoc}
  86. */
  87. public function validateFullMetadata(ClassMetadata $meta, array $config)
  88. {
  89. if (isset($config['strategy'])) {
  90. if (is_array($meta->identifier) && count($meta->identifier) > 1) {
  91. throw new InvalidMappingException("Tree does not support composite identifiers in class - {$meta->name}");
  92. }
  93. $method = 'validate' . ucfirst($config['strategy']) . 'TreeMetadata';
  94. $this->$method($meta, $config);
  95. } elseif ($config) {
  96. throw new InvalidMappingException("Cannot find Tree type for class: {$meta->name}");
  97. }
  98. }
  99. /**
  100. * {@inheritDoc}
  101. */
  102. public function readExtendedMetadata(ClassMetadata $meta, array &$config) {
  103. $class = $meta->getReflectionClass();
  104. // class annotations
  105. if ($annot = $this->reader->getClassAnnotation($class, self::TREE)) {
  106. if (!in_array($annot->type, $this->strategies)) {
  107. throw new InvalidMappingException("Tree type: {$annot->type} is not available.");
  108. }
  109. $config['strategy'] = $annot->type;
  110. }
  111. if ($annot = $this->reader->getClassAnnotation($class, self::CLOSURE)) {
  112. if (!class_exists($annot->class)) {
  113. throw new InvalidMappingException("Tree closure class: {$annot->class} does not exist.");
  114. }
  115. $config['closure'] = $annot->class;
  116. }
  117. // property annotations
  118. foreach ($class->getProperties() as $property) {
  119. if ($meta->isMappedSuperclass && !$property->isPrivate() ||
  120. $meta->isInheritedField($property->name) ||
  121. isset($meta->associationMappings[$property->name]['inherited'])
  122. ) {
  123. continue;
  124. }
  125. // left
  126. if ($left = $this->reader->getPropertyAnnotation($property, self::LEFT)) {
  127. $field = $property->getName();
  128. if (!$meta->hasField($field)) {
  129. throw new InvalidMappingException("Unable to find 'left' - [{$field}] as mapped property in entity - {$meta->name}");
  130. }
  131. if (!$this->isValidField($meta, $field)) {
  132. throw new InvalidMappingException("Tree left field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}");
  133. }
  134. $config['left'] = $field;
  135. }
  136. // right
  137. if ($right = $this->reader->getPropertyAnnotation($property, self::RIGHT)) {
  138. $field = $property->getName();
  139. if (!$meta->hasField($field)) {
  140. throw new InvalidMappingException("Unable to find 'right' - [{$field}] as mapped property in entity - {$meta->name}");
  141. }
  142. if (!$this->isValidField($meta, $field)) {
  143. throw new InvalidMappingException("Tree right field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}");
  144. }
  145. $config['right'] = $field;
  146. }
  147. // ancestor/parent
  148. if ($parent = $this->reader->getPropertyAnnotation($property, self::PARENT)) {
  149. $field = $property->getName();
  150. if (!$meta->isSingleValuedAssociation($field)) {
  151. throw new InvalidMappingException("Unable to find ancestor/parent child relation through ancestor field - [{$field}] in class - {$meta->name}");
  152. }
  153. $config['parent'] = $field;
  154. }
  155. // root
  156. if ($root = $this->reader->getPropertyAnnotation($property, self::ROOT)) {
  157. $field = $property->getName();
  158. if (!$meta->hasField($field)) {
  159. throw new InvalidMappingException("Unable to find 'root' - [{$field}] as mapped property in entity - {$meta->name}");
  160. }
  161. if (!$this->isValidField($meta, $field)) {
  162. throw new InvalidMappingException("Tree root field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}");
  163. }
  164. $config['root'] = $field;
  165. }
  166. // level
  167. if ($parent = $this->reader->getPropertyAnnotation($property, self::LEVEL)) {
  168. $field = $property->getName();
  169. if (!$meta->hasField($field)) {
  170. throw new InvalidMappingException("Unable to find 'level' - [{$field}] as mapped property in entity - {$meta->name}");
  171. }
  172. if (!$this->isValidField($meta, $field)) {
  173. throw new InvalidMappingException("Tree level field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}");
  174. }
  175. $config['level'] = $field;
  176. }
  177. }
  178. }
  179. /**
  180. * Checks if $field type is valid
  181. *
  182. * @param ClassMetadata $meta
  183. * @param string $field
  184. * @return boolean
  185. */
  186. protected function isValidField(ClassMetadata $meta, $field)
  187. {
  188. $mapping = $meta->getFieldMapping($field);
  189. return $mapping && in_array($mapping['type'], $this->validTypes);
  190. }
  191. /**
  192. * Validates metadata for nested type tree
  193. *
  194. * @param ClassMetadata $meta
  195. * @param array $config
  196. * @throws InvalidMappingException
  197. * @return void
  198. */
  199. private function validateNestedTreeMetadata(ClassMetadata $meta, array $config)
  200. {
  201. $missingFields = array();
  202. if (!isset($config['parent'])) {
  203. $missingFields[] = 'ancestor';
  204. }
  205. if (!isset($config['left'])) {
  206. $missingFields[] = 'left';
  207. }
  208. if (!isset($config['right'])) {
  209. $missingFields[] = 'right';
  210. }
  211. if ($missingFields) {
  212. throw new InvalidMappingException("Missing properties: " . implode(', ', $missingFields) . " in class - {$meta->name}");
  213. }
  214. }
  215. /**
  216. * Validates metadata for closure type tree
  217. *
  218. * @param ClassMetadata $meta
  219. * @param array $config
  220. * @throws InvalidMappingException
  221. * @return void
  222. */
  223. private function validateClosureTreeMetadata(ClassMetadata $meta, array $config)
  224. {
  225. $missingFields = array();
  226. if (!isset($config['parent'])) {
  227. $missingFields[] = 'ancestor';
  228. }
  229. if (!isset($config['closure'])) {
  230. $missingFields[] = 'closure class';
  231. }
  232. if ($missingFields) {
  233. throw new InvalidMappingException("Missing properties: " . implode(', ', $missingFields) . " in class - {$meta->name}");
  234. }
  235. }
  236. /**
  237. * Passes in the mapping read by original driver
  238. *
  239. * @param $driver
  240. * @return void
  241. */
  242. public function setOriginalDriver($driver)
  243. {
  244. $this->_originalDriver = $driver;
  245. }
  246. }