Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
All Data Structures Namespaces Files Functions Variables Pages
SchemaBuilder.php
Go to the documentation of this file.
1 <?php
6 declare(strict_types=1);
7 
9 
21 
40 {
44  private $tablesData = [];
45 
49  private $sharding;
50 
54  private $elementFactory;
55 
59  private $booleanUtils;
60 
64  private $validationComposite;
65 
69  private $resourceConnection;
70 
74  private $elementNameResolver;
75 
86  public function __construct(
87  ElementFactory $elementFactory,
88  BooleanUtils $booleanUtils,
89  Sharding $sharding,
90  ValidationComposite $validationComposite,
91  \Magento\Framework\App\ResourceConnection $resourceConnection,
92  ElementNameResolver $elementNameResolver
93  ) {
94  $this->sharding = $sharding;
95  $this->elementFactory = $elementFactory;
96  $this->booleanUtils = $booleanUtils;
97  $this->validationComposite = $validationComposite;
98  $this->resourceConnection = $resourceConnection;
99  $this->elementNameResolver = $elementNameResolver;
100  }
101 
110  public function addTablesData(array $tablesData)
111  {
112  $this->tablesData = $tablesData;
113  return $this;
114  }
115 
122  private function validate(Schema $schema)
123  {
124  $errors = $this->validationComposite->validate($schema);
125 
126  if (!empty($errors)) {
127  $messages = '';
128  foreach ($errors as $error) {
129  $messages .= sprintf("%s%s", PHP_EOL, $error['message']);
130  }
131 
132  throw new Exception(new Phrase($messages));
133  }
134  }
135 
143  public function build(Schema $schema): Schema
144  {
145  foreach ($this->tablesData as $tableData) {
146  if (!$schema->getTableByName($tableData['name'])) {
147  if (!$this->isDisabled($tableData)) {
148  $this->processTable($schema, $tableData);
149  }
150  }
151  }
152 
153  $this->validate($schema);
154 
155  return $schema;
156  }
157 
164  private function getStructuralElementResource(array $tableData): string
165  {
166  return isset($tableData['resource']) && $this->sharding->canUseResource($tableData['resource']) ?
167  $tableData['resource'] : 'default';
168  }
169 
176  private function isDisabled(array $structuralElementData): bool
177  {
178  return isset($structuralElementData['disabled']) &&
179  $this->booleanUtils->toBoolean($structuralElementData['disabled']);
180  }
181 
192  private function processColumns(array $tableData, string $resource, Table $table): array
193  {
194  $columns = [];
195 
196  foreach ($tableData['column'] as $columnData) {
197  if ($this->isDisabled($columnData)) {
198  continue;
199  }
200 
201  $columnData = $this->processGenericData($columnData, $resource, $table);
202  $column = $this->elementFactory->create($columnData['type'], $columnData);
203  $columns[$column->getName()] = $column;
204  }
205 
206  return $columns;
207  }
208 
217  private function processGenericData(array $elementData, string $resource, Table $table): array
218  {
219  $elementData['table'] = $table;
220  $elementData['resource'] = $resource;
221 
222  return $elementData;
223  }
224 
234  private function processTable(Schema $schema, array $tableData): Table
235  {
236  if (!$schema->getTableByName($tableData['name'])) {
237  $resource = $this->getStructuralElementResource($tableData);
238  $tableData['resource'] = $resource;
239  $tableData['comment'] = $tableData['comment'] ?? null;
241  $table = $this->elementFactory->create('table', $tableData);
242  $columns = $this->processColumns($tableData, $resource, $table);
243  $table->addColumns($columns);
244  //Add indexes to table
245  $table->addIndexes($this->processIndexes($tableData, $resource, $table));
246  //Add internal and reference constraints
247  $table->addConstraints($this->processConstraints($tableData, $resource, $schema, $table));
248  $schema->addTable($table);
249  }
250 
251  return $schema->getTableByName($tableData['name']);
252  }
253 
261  private function getColumnByName(string $columnName, Table $table): Column
262  {
263  $columnCandidate = $table->getColumnByName($columnName);
264 
265  if (!$columnCandidate) {
266  throw new \LogicException(
267  sprintf('Table %s do not have column with name %s', $table->getName(), $columnName)
268  );
269  }
270 
271  return $columnCandidate;
272  }
273 
281  private function convertColumnNamesToObjects(array $columnNames, Table $table): array
282  {
283  $columns = [];
284 
285  foreach ($columnNames as $columnName) {
286  $columns[] = $this->getColumnByName($columnName, $table);
287  }
288 
289  return $columns;
290  }
291 
300  private function processIndexes(array $tableData, string $resource, Table $table): array
301  {
302  if (!isset($tableData['index'])) {
303  return [];
304  }
305 
306  $indexes = [];
307 
308  foreach ($tableData['index'] as $indexData) {
309  if ($this->isDisabled($indexData)) {
310  continue;
311  }
312 
313  $indexData['name'] = $this->elementNameResolver->getFullIndexName(
314  $table,
315  $indexData['column'],
316  $indexData['indexType'] ?? null
317  );
318  $indexData = $this->processGenericData($indexData, $resource, $table);
319  $indexData['columns'] = $this->convertColumnNamesToObjects($indexData['column'], $table);
320  $index = $this->elementFactory->create('index', $indexData);
321  $indexes[$index->getName()] = $index;
322  }
323 
324  return $indexes;
325  }
326 
336  private function processConstraints(array $tableData, string $resource, Schema $schema, Table $table): array
337  {
338  if (!isset($tableData['constraint'])) {
339  return [];
340  }
341 
342  $constraints = [];
343 
344  foreach ($tableData['constraint'] as $constraintData) {
345  if ($this->isDisabled($constraintData)) {
346  continue;
347  }
348  $constraintData = $this->processGenericData($constraintData, $resource, $table);
349  //As foreign constraint has different schema we need to process it in different way
350  if ($constraintData['type'] === 'foreign') {
351  $constraintData['column'] = $this->getColumnByName($constraintData['column'], $table);
352  $referenceTableData = $this->tablesData[$constraintData['referenceTable']];
353  //If we are referenced to the same table we need to specify it
354  //Get table name from resource connection regarding prefix settings
355  $refTableName = $this->resourceConnection->getTableName($referenceTableData['name']);
356  $referenceTable = $refTableName === $table->getName() ?
357  $table :
358  $this->processTable($schema, $referenceTableData);
359 
360  if ($referenceTable->getResource() !== $table->getResource()) {
361  continue; //we should avoid creating foreign keys
362  //for tables that are on another shard
363  }
364  $constraintData['referenceTable'] = $referenceTable;
365 
366  if (!$constraintData['referenceTable']) {
367  throw new \LogicException(
368  sprintf('Cannot find reference table with name %s', $constraints['referenceTable'])
369  );
370  }
371 
372  $constraintData['referenceColumn'] = $this->getColumnByName(
373  $constraintData['referenceColumn'],
374  $constraintData['referenceTable']
375  );
376  $constraintData['name'] = $this->elementNameResolver->getFullFKName(
377  $table,
378  $constraintData['column'],
379  $constraintData['referenceTable'],
380  $constraintData['referenceColumn']
381  );
382  $constraint = $this->elementFactory->create($constraintData['type'], $constraintData);
383  $constraints[$constraint->getName()] = $constraint;
384  } else {
385  $constraintData['name'] = $this->elementNameResolver->getFullIndexName(
386  $table,
387  $constraintData['column'],
388  $constraintData['type']
389  );
390  $constraintData['columns'] = $this->convertColumnNamesToObjects($constraintData['column'], $table);
391  $constraint = $this->elementFactory->create($constraintData['type'], $constraintData);
392  $constraints[$constraint->getName()] = $constraint;
393  }
394  }
395 
396  return $constraints;
397  }
398 }
$resource
Definition: bulk.php:12
$columns
Definition: default.phtml:15
__construct(ElementFactory $elementFactory, BooleanUtils $booleanUtils, Sharding $sharding, ValidationComposite $validationComposite, \Magento\Framework\App\ResourceConnection $resourceConnection, ElementNameResolver $elementNameResolver)
$table
Definition: trigger.php:14
$index
Definition: list.phtml:44
$errors
Definition: overview.phtml:9