SchemaBuilder.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\Setup\Declaration\Schema\Db;
  7. use Magento\Framework\Phrase;
  8. use Magento\Framework\Setup\Declaration\Schema\Dto\Column;
  9. use Magento\Framework\Setup\Declaration\Schema\Dto\ElementFactory;
  10. use Magento\Framework\Setup\Declaration\Schema\Dto\Schema;
  11. use Magento\Framework\Setup\Declaration\Schema\Dto\Table;
  12. use Magento\Framework\Setup\Declaration\Schema\Sharding;
  13. use Magento\Framework\Setup\Exception;
  14. /**
  15. * This type of builder is responsible for converting ENTIRE data, that comes from db
  16. * into DTO`s format, with aggregation root: Schema.
  17. *
  18. * Note: SchemaBuilder can not be used for one structural element, like column or constraint
  19. * because it should have references to other DTO objects.
  20. * In order to convert build only 1 structural element use directly it factory.
  21. *
  22. * @see Schema
  23. * @inheritdoc
  24. */
  25. class SchemaBuilder
  26. {
  27. /**
  28. * @var ElementFactory
  29. */
  30. private $elementFactory;
  31. /**
  32. * @var DbSchemaReaderInterface
  33. */
  34. private $dbSchemaReader;
  35. /**
  36. * @var Sharding
  37. */
  38. private $sharding;
  39. /**
  40. * @var array
  41. */
  42. private $tables;
  43. /**
  44. * Constructor.
  45. *
  46. * @param ElementFactory $elementFactory
  47. * @param DbSchemaReaderInterface $dbSchemaReader
  48. * @param Sharding $sharding
  49. */
  50. public function __construct(
  51. ElementFactory $elementFactory,
  52. DbSchemaReaderInterface $dbSchemaReader,
  53. Sharding $sharding
  54. ) {
  55. $this->elementFactory = $elementFactory;
  56. $this->dbSchemaReader = $dbSchemaReader;
  57. $this->sharding = $sharding;
  58. }
  59. /**
  60. * @inheritdoc
  61. */
  62. public function build(Schema $schema)
  63. {
  64. foreach ($this->sharding->getResources() as $resource) {
  65. foreach ($this->dbSchemaReader->readTables($resource) as $tableName) {
  66. $columns = [];
  67. $indexes = [];
  68. $constraints = [];
  69. $tableOptions = $this->dbSchemaReader->getTableOptions($tableName, $resource);
  70. $columnsData = $this->dbSchemaReader->readColumns($tableName, $resource);
  71. $indexesData = $this->dbSchemaReader->readIndexes($tableName, $resource);
  72. $constrainsData = $this->dbSchemaReader->readConstraints($tableName, $resource);
  73. /**
  74. * @var Table $table
  75. */
  76. $table = $this->elementFactory->create(
  77. 'table',
  78. [
  79. 'name' => $tableName,
  80. 'resource' => $resource,
  81. 'engine' => strtolower($tableOptions['engine']),
  82. 'comment' => $tableOptions['comment'] === '' ? null : $tableOptions['comment'],
  83. 'charset' => $tableOptions['charset'],
  84. 'collation' => $tableOptions['collation']
  85. ]
  86. );
  87. // Process columns
  88. foreach ($columnsData as $columnData) {
  89. $columnData['table'] = $table;
  90. $column = $this->elementFactory->create($columnData['type'], $columnData);
  91. $columns[$column->getName()] = $column;
  92. }
  93. $table->addColumns($columns);
  94. //Process indexes
  95. foreach ($indexesData as $indexData) {
  96. $indexData['table'] = $table;
  97. $indexData['columns'] = $this->resolveInternalRelations($columns, $indexData);
  98. $index = $this->elementFactory->create('index', $indexData);
  99. $indexes[$index->getName()] = $index;
  100. }
  101. //Process internal constraints
  102. foreach ($constrainsData as $constraintData) {
  103. $constraintData['table'] = $table;
  104. $constraintData['columns'] = $this->resolveInternalRelations($columns, $constraintData);
  105. $constraint = $this->elementFactory->create($constraintData['type'], $constraintData);
  106. $constraints[$constraint->getName()] = $constraint;
  107. }
  108. $table->addIndexes($indexes);
  109. $table->addConstraints($constraints);
  110. $this->tables[$table->getName()] = $table;
  111. }
  112. }
  113. $this->processReferenceKeys($this->tables, $schema);
  114. return $schema;
  115. }
  116. /**
  117. * Process references for all tables. Schema validation required.
  118. *
  119. * @param Table[] $tables
  120. * @param Schema $schema
  121. */
  122. private function processReferenceKeys(array $tables, Schema $schema)
  123. {
  124. foreach ($tables as $table) {
  125. $tableName = $table->getName();
  126. if ($schema->getTableByName($tableName) instanceof Table) {
  127. continue;
  128. }
  129. $referencesData = $this->dbSchemaReader->readReferences($tableName, $table->getResource());
  130. $references = [];
  131. foreach ($referencesData as $referenceData) {
  132. //Prepare reference data
  133. $referenceData['table'] = $table;
  134. $referenceTableName = $referenceData['referenceTable'];
  135. $referenceData['column'] = $table->getColumnByName($referenceData['column']);
  136. $referenceData['referenceTable'] = $this->tables[$referenceTableName];
  137. $referenceData['referenceColumn'] = $referenceData['referenceTable']->getColumnByName(
  138. $referenceData['referenceColumn']
  139. );
  140. $references[$referenceData['name']] = $this->elementFactory->create('foreign', $referenceData);
  141. //We need to instantiate tables in order of references tree
  142. if (isset($tables[$referenceTableName]) && $referenceTableName !== $tableName) {
  143. $this->processReferenceKeys([$referenceTableName => $tables[$referenceTableName]], $schema);
  144. unset($tables[$referenceTableName]);
  145. }
  146. }
  147. $table->addConstraints($references);
  148. $schema->addTable($table);
  149. }
  150. }
  151. /**
  152. * Retrieve column objects from names.
  153. *
  154. * @param Column[] $columns
  155. * @param array $data
  156. * @return Column[]
  157. * @throws Exception
  158. */
  159. private function resolveInternalRelations(array $columns, array $data)
  160. {
  161. if (!is_array($data['column'])) {
  162. throw new Exception(
  163. new Phrase("Cannot find columns for internal index")
  164. );
  165. }
  166. $referenceColumns = [];
  167. foreach ($data['column'] as $columnName) {
  168. if (!isset($columns[$columnName])) {
  169. $tableName = isset($data['table']) ? $data['table']->getName() : '';
  170. trigger_error(
  171. new Phrase(
  172. 'Column %1 does not exist for index/constraint %2 in table %3.',
  173. [
  174. $columnName,
  175. $data['name'],
  176. $tableName
  177. ]
  178. ),
  179. E_USER_WARNING
  180. );
  181. } else {
  182. $referenceColumns[] = $columns[$columnName];
  183. }
  184. }
  185. return $referenceColumns;
  186. }
  187. }