PageRenderTime 58ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataFactory.php

https://github.com/esimionato/mongodb-odm
PHP | 410 lines | 250 code | 42 blank | 118 comment | 32 complexity | 4133c2c01056e2aa323ac3bb4b783888 MD5 | raw file
  1. <?php
  2. /*
  3. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  4. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  5. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  6. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  7. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  8. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  9. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  10. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  11. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  13. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14. *
  15. * This software consists of voluntary contributions made by many individuals
  16. * and is licensed under the LGPL. For more information, see
  17. * <http://www.doctrine-project.org>.
  18. */
  19. namespace Doctrine\ODM\MongoDB\Mapping;
  20. use Doctrine\ODM\MongoDB\DocumentManager,
  21. Doctrine\ODM\MongoDB\Configuration,
  22. Doctrine\ODM\MongoDB\Mapping\ClassMetadata,
  23. Doctrine\ODM\MongoDB\MongoDBException,
  24. Doctrine\ODM\MongoDB\Events,
  25. Doctrine\Common\Cache\Cache,
  26. Doctrine\ODM\MongoDB\Mapping\Types\Type;
  27. /**
  28. * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
  29. * metadata mapping informations of a class which describes how a class should be mapped
  30. * to a document database.
  31. *
  32. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
  33. * @link www.doctrine-project.com
  34. * @since 1.0
  35. * @author Jonathan H. Wage <jonwage@gmail.com>
  36. * @author Roman Borschel <roman@code-factory.org>
  37. */
  38. class ClassMetadataFactory implements \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory
  39. {
  40. /** The DocumentManager instance */
  41. private $dm;
  42. /** The Configuration instance */
  43. private $config;
  44. /** The array of loaded ClassMetadata instances */
  45. private $loadedMetadata;
  46. /** The used metadata driver. */
  47. private $driver;
  48. /** The event manager instance */
  49. private $evm;
  50. /** The used cache driver. */
  51. private $cacheDriver;
  52. /** Whether factory has been lazily initialized yet */
  53. private $initialized = false;
  54. /**
  55. * Sets the DocumentManager instance for this class.
  56. *
  57. * @param EntityManager $dm The DocumentManager instance
  58. */
  59. public function setDocumentManager(DocumentManager $dm)
  60. {
  61. $this->dm = $dm;
  62. }
  63. /**
  64. * Sets the Configuration instance
  65. *
  66. * @param Configuration $config
  67. */
  68. public function setConfiguration(Configuration $config)
  69. {
  70. $this->config = $config;
  71. }
  72. /**
  73. * Lazy initialization of this stuff, especially the metadata driver,
  74. * since these are not needed at all when a metadata cache is active.
  75. */
  76. private function initialize()
  77. {
  78. $this->driver = $this->config->getMetadataDriverImpl();
  79. $this->evm = $this->dm->getEventManager();
  80. $this->initialized = true;
  81. }
  82. /**
  83. * Sets the cache driver used by the factory to cache ClassMetadata instances.
  84. *
  85. * @param Doctrine\Common\Cache\Cache $cacheDriver
  86. */
  87. public function setCacheDriver($cacheDriver)
  88. {
  89. $this->cacheDriver = $cacheDriver;
  90. }
  91. /**
  92. * Gets the cache driver used by the factory to cache ClassMetadata instances.
  93. *
  94. * @return Doctrine\Common\Cache\Cache
  95. */
  96. public function getCacheDriver()
  97. {
  98. return $this->cacheDriver;
  99. }
  100. /**
  101. * Gets the array of loaded ClassMetadata instances.
  102. *
  103. * @return array $loadedMetadata The loaded metadata.
  104. */
  105. public function getLoadedMetadata()
  106. {
  107. return $this->loadedMetadata;
  108. }
  109. /**
  110. * Forces the factory to load the metadata of all classes known to the underlying
  111. * mapping driver.
  112. *
  113. * @return array The ClassMetadata instances of all mapped classes.
  114. */
  115. public function getAllMetadata()
  116. {
  117. if ( ! $this->initialized) {
  118. $this->initialize();
  119. }
  120. $metadata = array();
  121. foreach ($this->driver->getAllClassNames() as $className) {
  122. $metadata[] = $this->getMetadataFor($className);
  123. }
  124. return $metadata;
  125. }
  126. /**
  127. * Gets the class metadata descriptor for a class.
  128. *
  129. * @param string $className The name of the class.
  130. * @return Doctrine\ODM\MongoDB\Mapping\ClassMetadata
  131. */
  132. public function getMetadataFor($className)
  133. {
  134. if ( ! isset($this->loadedMetadata[$className])) {
  135. $realClassName = $className;
  136. // Check for namespace alias
  137. if (strpos($className, ':') !== false) {
  138. list($namespaceAlias, $simpleClassName) = explode(':', $className);
  139. $realClassName = $this->config->getDocumentNamespace($namespaceAlias) . '\\' . $simpleClassName;
  140. if (isset($this->loadedMetadata[$realClassName])) {
  141. // We do not have the alias name in the map, include it
  142. $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
  143. return $this->loadedMetadata[$realClassName];
  144. }
  145. }
  146. if ($this->cacheDriver) {
  147. if (($cached = $this->cacheDriver->fetch("$realClassName\$MONGODBODMCLASSMETADATA")) !== false) {
  148. $this->loadedMetadata[$realClassName] = $cached;
  149. } else {
  150. foreach ($this->loadMetadata($realClassName) as $loadedClassName) {
  151. $this->cacheDriver->save(
  152. "$loadedClassName\$MONGODBODMCLASSMETADATA", $this->loadedMetadata[$loadedClassName], null
  153. );
  154. }
  155. }
  156. } else {
  157. $this->loadMetadata($realClassName);
  158. }
  159. if ($className != $realClassName) {
  160. // We do not have the alias name in the map, include it
  161. $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
  162. }
  163. }
  164. return $this->loadedMetadata[$className];
  165. }
  166. /**
  167. * Loads the metadata of the class in question and all it's ancestors whose metadata
  168. * is still not loaded.
  169. *
  170. * @param string $name The name of the class for which the metadata should get loaded.
  171. * @param array $tables The metadata collection to which the loaded metadata is added.
  172. */
  173. private function loadMetadata($className)
  174. {
  175. if ( ! $this->initialized) {
  176. $this->initialize();
  177. }
  178. $loaded = array();
  179. $parentClasses = $this->getParentClasses($className);
  180. $parentClasses[] = $className;
  181. // Move down the hierarchy of parent classes, starting from the topmost class
  182. $parent = null;
  183. $visited = array();
  184. foreach ($parentClasses as $className) {
  185. if (isset($this->loadedMetadata[$className])) {
  186. $parent = $this->loadedMetadata[$className];
  187. if ( ! $parent->isMappedSuperclass && ! $parent->isEmbeddedDocument) {
  188. array_unshift($visited, $className);
  189. }
  190. continue;
  191. }
  192. $class = $this->newClassMetadataInstance($className);
  193. if ($parent) {
  194. if (!$parent->isMappedSuperclass) {
  195. $class->setInheritanceType($parent->inheritanceType);
  196. $class->setDiscriminatorField($parent->discriminatorField);
  197. $class->setDiscriminatorMap($parent->discriminatorMap);
  198. }
  199. $class->setIdGeneratorType($parent->generatorType);
  200. $this->addInheritedFields($class, $parent);
  201. $this->addInheritedIndexes($class, $parent);
  202. $class->setIdentifier($parent->identifier);
  203. $class->setVersioned($parent->isVersioned);
  204. $class->setVersionField($parent->versionField);
  205. $class->setLifecycleCallbacks($parent->lifecycleCallbacks);
  206. $class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
  207. $class->setFile($parent->getFile());
  208. }
  209. // Invoke driver
  210. try {
  211. $this->driver->loadMetadataForClass($className, $class);
  212. } catch(ReflectionException $e) {
  213. throw MongoDBException::reflectionFailure($className, $e);
  214. }
  215. if ( ! $class->identifier && ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument) {
  216. throw MongoDBException::identifierRequired($className);
  217. }
  218. if ($parent && ! $parent->isMappedSuperclass && ! $class->isEmbeddedDocument) {
  219. if ($parent->generatorType) {
  220. $class->setIdGeneratorType($parent->generatorType);
  221. }
  222. if ($parent->generatorOptions) {
  223. $class->setIdGeneratorOptions($parent->generatorOptions);
  224. }
  225. if ($parent->idGenerator) {
  226. $class->setIdGenerator($parent->idGenerator);
  227. }
  228. } else {
  229. $this->completeIdGeneratorMapping($class);
  230. }
  231. if ($parent && $parent->isInheritanceTypeSingleCollection()) {
  232. $class->setDatabase($parent->getDatabase());
  233. $class->setCollection($parent->getCollection());
  234. }
  235. $class->setParentClasses($visited);
  236. if ($this->evm->hasListeners(Events::loadClassMetadata)) {
  237. $eventArgs = new \Doctrine\ODM\MongoDB\Event\LoadClassMetadataEventArgs($class, $this->dm);
  238. $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
  239. }
  240. $this->loadedMetadata[$className] = $class;
  241. $parent = $class;
  242. if ( ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument) {
  243. array_unshift($visited, $className);
  244. }
  245. $loaded[] = $className;
  246. }
  247. return $loaded;
  248. }
  249. /**
  250. * Checks whether the factory has the metadata for a class loaded already.
  251. *
  252. * @param string $className
  253. * @return boolean TRUE if the metadata of the class in question is already loaded, FALSE otherwise.
  254. */
  255. public function hasMetadataFor($className)
  256. {
  257. return isset($this->loadedMetadata[$className]);
  258. }
  259. /**
  260. * Sets the metadata descriptor for a specific class.
  261. *
  262. * NOTE: This is only useful in very special cases, like when generating proxy classes.
  263. *
  264. * @param string $className
  265. * @param ClassMetadata $class
  266. */
  267. public function setMetadataFor($className, $class)
  268. {
  269. $this->loadedMetadata[$className] = $class;
  270. }
  271. /**
  272. * Creates a new ClassMetadata instance for the given class name.
  273. *
  274. * @param string $className
  275. * @return Doctrine\ODM\MongoDB\Mapping\ClassMetadata
  276. */
  277. protected function newClassMetadataInstance($className)
  278. {
  279. return new ClassMetadata($className);
  280. }
  281. /**
  282. * Get array of parent classes for the given document class
  283. *
  284. * @param string $name
  285. * @return array $parentClasses
  286. */
  287. protected function getParentClasses($name)
  288. {
  289. // Collect parent classes, ignoring transient (not-mapped) classes.
  290. $parentClasses = array();
  291. foreach (array_reverse(class_parents($name)) as $parentClass) {
  292. if ( ! $this->driver->isTransient($parentClass)) {
  293. $parentClasses[] = $parentClass;
  294. }
  295. }
  296. return $parentClasses;
  297. }
  298. private function completeIdGeneratorMapping(ClassMetadataInfo $class)
  299. {
  300. $idGenOptions = $class->generatorOptions;
  301. switch ($class->generatorType) {
  302. case ClassMetadata::GENERATOR_TYPE_AUTO:
  303. $class->setIdGenerator(new \Doctrine\ODM\MongoDB\Id\AutoGenerator($class));
  304. break;
  305. case ClassMetadata::GENERATOR_TYPE_INCREMENT:
  306. $class->setIdGenerator(new \Doctrine\ODM\MongoDB\Id\IncrementGenerator($class));
  307. break;
  308. case ClassMetadata::GENERATOR_TYPE_UUID:
  309. $uuidGenerator = new \Doctrine\ODM\MongoDB\Id\UuidGenerator($class);
  310. $uuidGenerator->setSalt(isset($idGenOptions['salt']) ? $idGenOptions['salt'] : php_uname('n'));
  311. $class->setIdGenerator($uuidGenerator);
  312. break;
  313. case ClassMetadata::GENERATOR_TYPE_ALNUM:
  314. $alnumGenerator = new \Doctrine\ODM\MongoDB\Id\AlnumGenerator($class);
  315. if(isset($idGenOptions['pad'])) {
  316. $alnumGenerator->setPad($idGenOptions['pad']);
  317. }
  318. if(isset($idGenOptions['awkwardSafe'])) {
  319. $alnumGenerator->setAwkwardSafeMode($idGenOptions['awkwardSafe']);
  320. }
  321. $class->setIdGenerator($alnumGenerator);
  322. break;
  323. case ClassMetadata::GENERATOR_TYPE_NONE;
  324. break;
  325. default:
  326. throw new MongoDBException("Unknown generator type: " . $class->generatorType);
  327. }
  328. }
  329. /**
  330. * Adds inherited fields to the subclass mapping.
  331. *
  332. * @param Doctrine\ODM\MongoDB\Mapping\ClassMetadata $subClass
  333. * @param Doctrine\ODM\MongoDB\Mapping\ClassMetadata $parentClass
  334. */
  335. private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass)
  336. {
  337. foreach ($parentClass->fieldMappings as $fieldName => $mapping) {
  338. if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
  339. $mapping['inherited'] = $parentClass->name;
  340. }
  341. if ( ! isset($mapping['declared'])) {
  342. $mapping['declared'] = $parentClass->name;
  343. }
  344. $subClass->addInheritedFieldMapping($mapping);
  345. }
  346. foreach ($parentClass->reflFields as $name => $field) {
  347. $subClass->reflFields[$name] = $field;
  348. }
  349. }
  350. /**
  351. * Adds inherited indexes to the subclass mapping.
  352. *
  353. * @param Doctrine\ODM\MongoDB\Mapping\ClassMetadata $subClass
  354. * @param Doctrine\ODM\MongoDB\Mapping\ClassMetadata $parentClass
  355. */
  356. private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass)
  357. {
  358. foreach ($parentClass->indexes as $index) {
  359. $subClass->addIndex($index['keys'], $index['options']);
  360. }
  361. }
  362. }