123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Framework\Setup\Declaration\Schema\Operations;
- use Magento\Framework\Setup\Declaration\Schema\Db\DbSchemaWriterInterface;
- use Magento\Framework\Setup\Declaration\Schema\Db\DDLTriggerInterface;
- use Magento\Framework\Setup\Declaration\Schema\Db\DefinitionAggregator;
- use Magento\Framework\Setup\Declaration\Schema\Db\Statement;
- use Magento\Framework\Setup\Declaration\Schema\Dto\Column;
- use Magento\Framework\Setup\Declaration\Schema\Dto\Columns\Integer;
- use Magento\Framework\Setup\Declaration\Schema\Dto\ElementFactory;
- use Magento\Framework\Setup\Declaration\Schema\Dto\Index;
- use Magento\Framework\Setup\Declaration\Schema\ElementHistory;
- use Magento\Framework\Setup\Declaration\Schema\ElementHistoryFactory;
- use Magento\Framework\Setup\Declaration\Schema\OperationInterface;
- /**
- * Add column to table operation.
- * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
- */
- class AddColumn implements OperationInterface
- {
- /**
- * Operation name.
- */
- const OPERATION_NAME = 'add_column';
- /**
- * This key is service key and need only for migration of data on auto_increment field.
- */
- const TEMPORARY_KEY = 'AUTO_INCREMENT_TEMPORARY_KEY';
- /**
- * @var DefinitionAggregator
- */
- private $definitionAggregator;
- /**
- * @var DbSchemaWriterInterface
- */
- private $dbSchemaWriter;
- /**
- * @var ElementFactory
- */
- private $elementFactory;
- /**
- * @var ElementHistoryFactory
- */
- private $elementHistoryFactory;
- /**
- * @var AddComplexElement
- */
- private $addComplexElement;
- /**
- * @var DropElement
- */
- private $dropElement;
- /**
- * @var DDLTriggerInterface[]
- */
- private $triggers;
- /**
- * AddColumn constructor.
- *
- * @param DefinitionAggregator $definitionAggregator
- * @param DbSchemaWriterInterface $dbSchemaWriter
- * @param ElementFactory $elementFactory
- * @param ElementHistoryFactory $elementHistoryFactory
- * @param AddComplexElement $addComplexElement
- * @param DropElement $dropElement
- * @param array $triggers
- */
- public function __construct(
- DefinitionAggregator $definitionAggregator,
- DbSchemaWriterInterface $dbSchemaWriter,
- ElementFactory $elementFactory,
- ElementHistoryFactory $elementHistoryFactory,
- AddComplexElement $addComplexElement,
- DropElement $dropElement,
- array $triggers = []
- ) {
- $this->definitionAggregator = $definitionAggregator;
- $this->dbSchemaWriter = $dbSchemaWriter;
- $this->elementFactory = $elementFactory;
- $this->elementHistoryFactory = $elementHistoryFactory;
- $this->addComplexElement = $addComplexElement;
- $this->dropElement = $dropElement;
- $this->triggers = $triggers;
- }
- /**
- * Creates index history.
- *
- * @param Column $column
- * @return ElementHistory
- */
- private function getTemporaryIndexHistory(Column $column)
- {
- $index = $this->elementFactory->create(
- Index::TYPE,
- [
- 'name' => self::TEMPORARY_KEY,
- 'column' => [$column->getName()],
- 'columns' => [$column],
- 'table' => $column->getTable()
- ]
- );
- return $this->elementHistoryFactory->create(['new' => $index]);
- }
- /**
- * @inheritdoc
- */
- public function getOperationName()
- {
- return self::OPERATION_NAME;
- }
- /**
- * @inheritdoc
- */
- public function isOperationDestructive()
- {
- return false;
- }
- /**
- * Check whether column is auto increment or not.
- *
- * @param Column $column
- * @return bool
- */
- private function columnIsAutoIncrement(Column $column)
- {
- return $column instanceof Integer && $column->isIdentity();
- }
- /**
- * Setup triggers if column have onCreate syntax.
- *
- * @param Statement $statement
- * @param ElementHistory $elementHistory
- * @return array
- */
- private function setupTriggersIfExists(Statement $statement, ElementHistory $elementHistory)
- {
- /** @var Column $column */
- $column = $elementHistory->getNew();
- //Add triggers to column
- foreach ($this->triggers as $ddlTrigger) {
- if ($ddlTrigger->isApplicable((string) $column->getOnCreate())) {
- $statement->addTrigger($ddlTrigger->getCallback($elementHistory));
- }
- }
- $statements = [$statement];
- /**
- * If column has triggers, only than we need to create temporary index on it.
- * As triggers means, that we will not enable primary key until all data will be transferred,
- * so column can left without key (as primary key is disabled) and this cause an error.
- */
- if ($this->columnIsAutoIncrement($column) && !empty($statement->getTriggers())) {
- /**
- * We need to create additional index for auto_increment.
- * As we create new field, and for this field we do not have any key/index, that are
- * required by SQL on any auto_increment field.
- * Primary key will be added to the column later, because column is empty at the moment
- * and if the table is not empty we will get error, such as "Duplicate key entry:".
- */
- $indexHistory = $this->getTemporaryIndexHistory($column);
- /** Add index should goes first */
- $statements = array_merge($this->addComplexElement->doOperation($indexHistory), $statements);
- /** Drop index should goes last and in another query */
- $statements = array_merge($statements, $this->dropElement->doOperation($indexHistory));
- }
- return $statements;
- }
- /**
- * @inheritdoc
- */
- public function doOperation(ElementHistory $elementHistory)
- {
- /**
- * @var Column $element
- */
- $element = $elementHistory->getNew();
- $definition = $this->definitionAggregator->toDefinition($element);
-
- $statement = $this->dbSchemaWriter->addElement(
- $element->getName(),
- $element->getTable()->getResource(),
- $element->getTable()->getName(),
- $definition,
- Column::TYPE
- );
- $statements = $this->setupTriggersIfExists($statement, $elementHistory);
- if ($this->columnIsAutoIncrement($element)) {
- /** We need to reset auto_increment as new field should goes from 1 */
- $statements[] = $this->dbSchemaWriter->resetAutoIncrement(
- $element->getTable()->getName(),
- $element->getTable()->getResource()
- );
- }
- return $statements;
- }
- }
|