ORMPurger.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. <?php
  2. namespace Doctrine\Common\DataFixtures\Purger;
  3. use Doctrine\ORM\EntityManagerInterface;
  4. use Doctrine\Common\DataFixtures\Sorter\TopologicalSorter;
  5. use Doctrine\ORM\Mapping\ClassMetadata;
  6. use Doctrine\DBAL\Platforms\AbstractPlatform;
  7. use Doctrine\ORM\Mapping\ClassMetadataInfo;
  8. /**
  9. * Class responsible for purging databases of data before reloading data fixtures.
  10. *
  11. * @author Jonathan H. Wage <jonwage@gmail.com>
  12. * @author Benjamin Eberlei <kontakt@beberlei.de>
  13. */
  14. class ORMPurger implements PurgerInterface
  15. {
  16. const PURGE_MODE_DELETE = 1;
  17. const PURGE_MODE_TRUNCATE = 2;
  18. /** EntityManagerInterface instance used for persistence. */
  19. private $em;
  20. /**
  21. * If the purge should be done through DELETE or TRUNCATE statements
  22. *
  23. * @var int
  24. */
  25. private $purgeMode = self::PURGE_MODE_DELETE;
  26. /**
  27. * Table/view names to be excleded from purge
  28. *
  29. * @var string[]
  30. */
  31. private $excluded;
  32. /**
  33. * Construct new purger instance.
  34. *
  35. * @param EntityManagerInterface $em EntityManagerInterface instance used for persistence.
  36. * @param string[] $excluded array of table/view names to be excleded from purge
  37. */
  38. public function __construct(EntityManagerInterface $em = null, array $excluded = [])
  39. {
  40. $this->em = $em;
  41. $this->excluded = $excluded;
  42. }
  43. /**
  44. * Set the purge mode
  45. *
  46. * @param $mode
  47. * @return void
  48. */
  49. public function setPurgeMode($mode)
  50. {
  51. $this->purgeMode = $mode;
  52. }
  53. /**
  54. * Get the purge mode
  55. *
  56. * @return int
  57. */
  58. public function getPurgeMode()
  59. {
  60. return $this->purgeMode;
  61. }
  62. /**
  63. * Set the EntityManagerInterface instance this purger instance should use.
  64. *
  65. * @param EntityManagerInterface $em
  66. */
  67. public function setEntityManager(EntityManagerInterface $em)
  68. {
  69. $this->em = $em;
  70. }
  71. /**
  72. * Retrieve the EntityManagerInterface instance this purger instance is using.
  73. *
  74. * @return \Doctrine\ORM\EntityManagerInterface
  75. */
  76. public function getObjectManager()
  77. {
  78. return $this->em;
  79. }
  80. /** @inheritDoc */
  81. public function purge()
  82. {
  83. $classes = [];
  84. foreach ($this->em->getMetadataFactory()->getAllMetadata() as $metadata) {
  85. if (! $metadata->isMappedSuperclass && ! (isset($metadata->isEmbeddedClass) && $metadata->isEmbeddedClass)) {
  86. $classes[] = $metadata;
  87. }
  88. }
  89. $commitOrder = $this->getCommitOrder($this->em, $classes);
  90. // Get platform parameters
  91. $platform = $this->em->getConnection()->getDatabasePlatform();
  92. // Drop association tables first
  93. $orderedTables = $this->getAssociationTables($commitOrder, $platform);
  94. // Drop tables in reverse commit order
  95. for ($i = count($commitOrder) - 1; $i >= 0; --$i) {
  96. $class = $commitOrder[$i];
  97. if (
  98. (isset($class->isEmbeddedClass) && $class->isEmbeddedClass) ||
  99. $class->isMappedSuperclass ||
  100. ($class->isInheritanceTypeSingleTable() && $class->name !== $class->rootEntityName)
  101. ) {
  102. continue;
  103. }
  104. $orderedTables[] = $this->getTableName($class, $platform);
  105. }
  106. $connection = $this->em->getConnection();
  107. $filterExpr = $connection->getConfiguration()->getFilterSchemaAssetsExpression();
  108. $emptyFilterExpression = empty($filterExpr);
  109. foreach($orderedTables as $tbl) {
  110. if(($emptyFilterExpression||preg_match($filterExpr, $tbl)) && array_search($tbl, $this->excluded) === false){
  111. if ($this->purgeMode === self::PURGE_MODE_DELETE) {
  112. $connection->executeUpdate("DELETE FROM " . $tbl);
  113. } else {
  114. $connection->executeUpdate($platform->getTruncateTableSQL($tbl, true));
  115. }
  116. }
  117. }
  118. }
  119. /**
  120. * @param EntityManagerInterface $em
  121. * @param ClassMetadata[] $classes
  122. *
  123. * @return ClassMetadata[]
  124. */
  125. private function getCommitOrder(EntityManagerInterface $em, array $classes)
  126. {
  127. $sorter = new TopologicalSorter();
  128. foreach ($classes as $class) {
  129. if ( ! $sorter->hasNode($class->name)) {
  130. $sorter->addNode($class->name, $class);
  131. }
  132. // $class before its parents
  133. foreach ($class->parentClasses as $parentClass) {
  134. $parentClass = $em->getClassMetadata($parentClass);
  135. $parentClassName = $parentClass->getName();
  136. if ( ! $sorter->hasNode($parentClassName)) {
  137. $sorter->addNode($parentClassName, $parentClass);
  138. }
  139. $sorter->addDependency($class->name, $parentClassName);
  140. }
  141. foreach ($class->associationMappings as $assoc) {
  142. if ($assoc['isOwningSide']) {
  143. /* @var $targetClass ClassMetadata */
  144. $targetClass = $em->getClassMetadata($assoc['targetEntity']);
  145. $targetClassName = $targetClass->getName();
  146. if ( ! $sorter->hasNode($targetClassName)) {
  147. $sorter->addNode($targetClassName, $targetClass);
  148. }
  149. // add dependency ($targetClass before $class)
  150. $sorter->addDependency($targetClassName, $class->name);
  151. // parents of $targetClass before $class, too
  152. foreach ($targetClass->parentClasses as $parentClass) {
  153. $parentClass = $em->getClassMetadata($parentClass);
  154. $parentClassName = $parentClass->getName();
  155. if ( ! $sorter->hasNode($parentClassName)) {
  156. $sorter->addNode($parentClassName, $parentClass);
  157. }
  158. $sorter->addDependency($parentClassName, $class->name);
  159. }
  160. }
  161. }
  162. }
  163. return array_reverse($sorter->sort());
  164. }
  165. /**
  166. * @param array $classes
  167. * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
  168. * @return array
  169. */
  170. private function getAssociationTables(array $classes, AbstractPlatform $platform)
  171. {
  172. $associationTables = [];
  173. foreach ($classes as $class) {
  174. foreach ($class->associationMappings as $assoc) {
  175. if ($assoc['isOwningSide'] && $assoc['type'] == ClassMetadata::MANY_TO_MANY) {
  176. $associationTables[] = $this->getJoinTableName($assoc, $class, $platform);
  177. }
  178. }
  179. }
  180. return $associationTables;
  181. }
  182. /**
  183. *
  184. * @param \Doctrine\ORM\Mapping\ClassMetadata $class
  185. * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
  186. * @return string
  187. */
  188. private function getTableName($class, $platform)
  189. {
  190. if (isset($class->table['schema']) && !method_exists($class, 'getSchemaName')) {
  191. return $class->table['schema'].'.'.$this->em->getConfiguration()->getQuoteStrategy()->getTableName($class, $platform);
  192. }
  193. return $this->em->getConfiguration()->getQuoteStrategy()->getTableName($class, $platform);
  194. }
  195. /**
  196. *
  197. * @param array $association
  198. * @param \Doctrine\ORM\Mapping\ClassMetadata $class
  199. * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
  200. * @return string
  201. */
  202. private function getJoinTableName($assoc, $class, $platform)
  203. {
  204. if (isset($assoc['joinTable']['schema']) && !method_exists($class, 'getSchemaName')) {
  205. return $assoc['joinTable']['schema'].'.'.$this->em->getConfiguration()->getQuoteStrategy()->getJoinTableName($assoc, $class, $platform);
  206. }
  207. return $this->em->getConfiguration()->getQuoteStrategy()->getJoinTableName($assoc, $class, $platform);
  208. }
  209. }