DisconnectedMetadataFactory.php 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. <?php
  2. namespace Doctrine\Bundle\DoctrineBundle\Mapping;
  3. use Doctrine\ORM\Mapping\ClassMetadata;
  4. use Doctrine\ORM\Mapping\MappingException;
  5. use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory;
  6. use Doctrine\Persistence\ManagerRegistry;
  7. use ReflectionClass;
  8. use RuntimeException;
  9. use Symfony\Component\HttpKernel\Bundle\BundleInterface;
  10. use function array_pop;
  11. use function class_exists;
  12. use function dirname;
  13. use function explode;
  14. use function implode;
  15. use function sprintf;
  16. use function str_replace;
  17. use function strpos;
  18. /**
  19. * This class provides methods to access Doctrine entity class metadata for a
  20. * given bundle, namespace or entity class, for generation purposes
  21. */
  22. class DisconnectedMetadataFactory
  23. {
  24. /** @var ManagerRegistry */
  25. private $registry;
  26. /**
  27. * @param ManagerRegistry $registry A ManagerRegistry instance
  28. */
  29. public function __construct(ManagerRegistry $registry)
  30. {
  31. $this->registry = $registry;
  32. }
  33. /**
  34. * Gets the metadata of all classes of a bundle.
  35. *
  36. * @param BundleInterface $bundle A BundleInterface instance
  37. *
  38. * @return ClassMetadataCollection A ClassMetadataCollection instance
  39. *
  40. * @throws RuntimeException When bundle does not contain mapped entities.
  41. */
  42. public function getBundleMetadata(BundleInterface $bundle)
  43. {
  44. $namespace = $bundle->getNamespace();
  45. $metadata = $this->getMetadataForNamespace($namespace);
  46. if (! $metadata->getMetadata()) {
  47. throw new RuntimeException(sprintf('Bundle "%s" does not contain any mapped entities.', $bundle->getName()));
  48. }
  49. $path = $this->getBasePathForClass($bundle->getName(), $bundle->getNamespace(), $bundle->getPath());
  50. $metadata->setPath($path);
  51. $metadata->setNamespace($bundle->getNamespace());
  52. return $metadata;
  53. }
  54. /**
  55. * Gets the metadata of a class.
  56. *
  57. * @param string $class A class name
  58. * @param string $path The path where the class is stored (if known)
  59. *
  60. * @return ClassMetadataCollection A ClassMetadataCollection instance
  61. *
  62. * @throws MappingException When class is not valid entity or mapped superclass.
  63. */
  64. public function getClassMetadata($class, $path = null)
  65. {
  66. $metadata = $this->getMetadataForClass($class);
  67. if (! $metadata->getMetadata()) {
  68. throw MappingException::classIsNotAValidEntityOrMappedSuperClass($class);
  69. }
  70. $this->findNamespaceAndPathForMetadata($metadata, $path);
  71. return $metadata;
  72. }
  73. /**
  74. * Gets the metadata of all classes of a namespace.
  75. *
  76. * @param string $namespace A namespace name
  77. * @param string $path The path where the class is stored (if known)
  78. *
  79. * @return ClassMetadataCollection A ClassMetadataCollection instance
  80. *
  81. * @throws RuntimeException When namespace not contain mapped entities.
  82. */
  83. public function getNamespaceMetadata($namespace, $path = null)
  84. {
  85. $metadata = $this->getMetadataForNamespace($namespace);
  86. if (! $metadata->getMetadata()) {
  87. throw new RuntimeException(sprintf('Namespace "%s" does not contain any mapped entities.', $namespace));
  88. }
  89. $this->findNamespaceAndPathForMetadata($metadata, $path);
  90. return $metadata;
  91. }
  92. /**
  93. * Find and configure path and namespace for the metadata collection.
  94. *
  95. * @param string|null $path
  96. *
  97. * @throws RuntimeException When unable to determine the path.
  98. */
  99. public function findNamespaceAndPathForMetadata(ClassMetadataCollection $metadata, $path = null)
  100. {
  101. $all = $metadata->getMetadata();
  102. if (class_exists($all[0]->name)) {
  103. $r = new ReflectionClass($all[0]->name);
  104. $path = $this->getBasePathForClass($r->getName(), $r->getNamespaceName(), dirname($r->getFilename()));
  105. $ns = $r->getNamespaceName();
  106. } elseif ($path) {
  107. // Get namespace by removing the last component of the FQCN
  108. $nsParts = explode('\\', $all[0]->name);
  109. array_pop($nsParts);
  110. $ns = implode('\\', $nsParts);
  111. } else {
  112. throw new RuntimeException(sprintf('Unable to determine where to save the "%s" class (use the --path option).', $all[0]->name));
  113. }
  114. $metadata->setPath($path);
  115. $metadata->setNamespace($ns);
  116. }
  117. /**
  118. * Get a base path for a class
  119. *
  120. * @throws RuntimeException When base path not found.
  121. */
  122. private function getBasePathForClass(string $name, string $namespace, string $path): string
  123. {
  124. $namespace = str_replace('\\', '/', $namespace);
  125. $search = str_replace('\\', '/', $path);
  126. $destination = str_replace('/' . $namespace, '', $search, $c);
  127. if ($c !== 1) {
  128. throw new RuntimeException(sprintf('Can\'t find base path for "%s" (path: "%s", destination: "%s").', $name, $path, $destination));
  129. }
  130. return $destination;
  131. }
  132. private function getMetadataForNamespace(string $namespace): ClassMetadataCollection
  133. {
  134. $metadata = [];
  135. foreach ($this->getAllMetadata() as $m) {
  136. if (strpos($m->name, $namespace) !== 0) {
  137. continue;
  138. }
  139. $metadata[] = $m;
  140. }
  141. return new ClassMetadataCollection($metadata);
  142. }
  143. private function getMetadataForClass(string $entity): ClassMetadataCollection
  144. {
  145. foreach ($this->registry->getManagers() as $em) {
  146. $cmf = new DisconnectedClassMetadataFactory();
  147. $cmf->setEntityManager($em);
  148. if (! $cmf->isTransient($entity)) {
  149. return new ClassMetadataCollection([$cmf->getMetadataFor($entity)]);
  150. }
  151. }
  152. return new ClassMetadataCollection([]);
  153. }
  154. /**
  155. * @return ClassMetadata[]
  156. */
  157. private function getAllMetadata(): array
  158. {
  159. $metadata = [];
  160. foreach ($this->registry->getManagers() as $em) {
  161. $cmf = new DisconnectedClassMetadataFactory();
  162. $cmf->setEntityManager($em);
  163. foreach ($cmf->getAllMetadata() as $m) {
  164. $metadata[] = $m;
  165. }
  166. }
  167. return $metadata;
  168. }
  169. }