$addedColumns * @param array $changedColumns * @param array $droppedColumns * @param array $addedIndexes * @param array $modifiedIndexes * @param array $droppedIndexes * @param array $renamedIndexes * @param array $addedForeignKeys * @param array $modifiedForeignKeys * @param array $droppedForeignKeys * @param array $tableOptions * * @internal The diff can be only instantiated by a {@see Comparator}. * * @todo Consider to change from array to typed collections with array access support. */ public function __construct( public Table $oldTable, public array $addedColumns = [], public array $changedColumns = [], public array $droppedColumns = [], public array $addedIndexes = [], public array $modifiedIndexes = [], public array $droppedIndexes = [], public array $renamedIndexes = [], public array $addedForeignKeys = [], public array $modifiedForeignKeys = [], public array $droppedForeignKeys = [], public array $tableOptions = [], ) { // NOTE: parent::__construct() not called by intention. } /** * Getter for table options. * * @return array */ public function getTableOptions(): array { return $this->tableOptions; } /** * Setter for table options * * @param array $tableOptions */ public function setTableOptions(array $tableOptions): self { $this->tableOptions = $tableOptions; return $this; } /** * Check if a table options has been set. */ public function hasTableOption(string $optionName): bool { return array_key_exists($optionName, $this->tableOptions); } public function getTableOption(string $optionName): string { if ($this->hasTableOption($optionName)) { return (string)$this->tableOptions[$optionName]; } return ''; } public function getOldTable(): Table { return $this->oldTable; } /** @return array */ public function getAddedColumns(): array { return $this->addedColumns; } /** @return array */ public function getChangedColumns(): array { return $this->changedColumns; } /** @return array */ public function getDroppedColumns(): array { return $this->droppedColumns; } /** @return array */ public function getAddedIndexes(): array { return $this->addedIndexes; } /** * @deprecated Use {@see getAddedIndexes()} and {@see getDroppedIndexes()} instead. * * @return array */ public function getModifiedIndexes(): array { return $this->modifiedIndexes; } /** @return array */ public function getDroppedIndexes(): array { return $this->droppedIndexes; } /** @return array */ public function getRenamedIndexes(): array { return $this->renamedIndexes; } /** @return array */ public function getAddedForeignKeys(): array { return $this->addedForeignKeys; } /** * @deprecated Use {@see getAddedForeignKeys()} and {@see getDroppedForeignKeys()} instead. * * @return array */ public function getModifiedForeignKeys(): array { return $this->modifiedForeignKeys; } /** * @deprecated Use {@see getDroppedForeignKeyConstraintNames()}. * * @return array */ public function getDroppedForeignKeys(): array { return $this->droppedForeignKeys; } /** * Overridden, because the parent implementation reads the parent property directly. As the parent * constructor is not called by intention, that property is never initialized, making the inherited * method raise an `Error`. Reads the redeclared property here instead, keeping the parent behaviour. * * @return array */ public function getDroppedForeignKeyConstraintNames(): array { $names = []; foreach ($this->droppedForeignKeys as $droppedForeignKey) { $name = $droppedForeignKey->getObjectName(); if ($name === null) { throw InvalidState::tableDiffContainsUnnamedDroppedForeignKeyConstraints(); } $names[] = $name; } return $names; } public function isEmpty(): bool { return count($this->getAddedColumns()) === 0 && count($this->getChangedColumns()) === 0 && count($this->getDroppedColumns()) === 0 && count($this->getAddedIndexes()) === 0 && count($this->getModifiedIndexes()) === 0 && count($this->getDroppedIndexes()) === 0 && count($this->getRenamedIndexes()) === 0 && count($this->getAddedForeignKeys()) === 0 && count($this->getModifiedForeignKeys()) === 0 && count($this->getDroppedForeignKeys()) === 0 // doctrine/dbal 4.x removed the newName. TYPO3 needs that to provide a rename to prefix logic before // really dropping tables instead. Therefore, we need to add here an empty check for the reintroduced // property.See for example: ConnectionMigrator->migrateUnprefixedRemovedTablesToRenames && $this->getNewName() !== null && $this->getNewName() !== '' && $this->getTableOptions() === []; } public function getNewName(): ?string { return $this->newName; } public static function ensure(DoctrineTableDiff|TableDiff $tableDiff): self { $diff = new self( // oldTable $tableDiff->getOldTable(), // addedColumns $tableDiff->getAddedColumns(), // changedColumns [], // droppedColumns $tableDiff->getDroppedColumns(), // addedIndexes $tableDiff->getAddedIndexes(), // modifiedIndexes [], // droppedIndexes $tableDiff->getDroppedIndexes(), // renamedIndexes $tableDiff->getRenamedIndexes(), // addedForeignKeys $tableDiff->getAddedForeignKeys(), // modifiedForeignKeys $tableDiff->getModifiedForeignKeys(), // droppedForeignKeys $tableDiff->getDroppedForeignKeys(), // tableOptions ($tableDiff instanceof TableDiff ? $tableDiff->tableOptions : []), ); // doctrine/dbal 4+ removed the column name as array index for modified column definitions, // but we rely on it. Restore it ! // Ensure to use custom ColumnDiff instance with more data and foreach ($tableDiff->getChangedColumns() as $changedColumn) { $diff->changedColumns[$changedColumn->getOldColumn()->getName()] = new ColumnDiff( // oldColumn $changedColumn->getOldColumn(), // newColumn $changedColumn->getNewColumn(), ); } // doctrine/dbal 4+ removed the index name as array index for modified index definitions, // but we rely on it. Restore it !. foreach ($tableDiff->getModifiedIndexes() as $modifiedIndex) { $diff->modifiedIndexes[$modifiedIndex->getName()] = $modifiedIndex; } // Accumulate modified index separated into added and dropped information to modifiedIndexes again, // otherwise required drop action may not be executed before trying to add an existing index first. // Required for planned doctrine/dbal 4.3.0 change (deprecation) and currently breaking with an open // discussion to mitigate that before dbal release. We still prepare for this case to be on the safer // side here. // Needs to be done in a two-step strategy to avoid changing array while iterating over it. // - https://github.com/doctrine/dbal/pull/6831 // - https://github.com/doctrine/dbal/issues/6880 /** * @var array $transformIndexOperations */ $transformIndexOperations = []; foreach ($diff->getAddedIndexes() as $addedIndex) { foreach ($diff->getDroppedIndexes() as $droppedIndex) { if ($droppedIndex->getName() === $addedIndex->getName()) { $transformIndexOperations[] = [ 'added' => $addedIndex, 'dropped' => $droppedIndex, ]; } } } foreach ($transformIndexOperations as $data) { $diff->unsetAddedIndex($data['added']); $diff->unsetDroppedIndex($data['dropped']); $diff->modifiedIndexes[$data['added']->getName()] = $data['added']; } return $diff; } /** * @internal This method exists only for compatibility with the current implementation of schema managers * that modify the diff while processing it. */ public function unsetAddedIndex(Index $index): void { $this->addedIndexes = array_filter( $this->addedIndexes, static function (Index $addedIndex) use ($index): bool { return $addedIndex !== $index; }, ); } /** * @internal This method exists only for compatibility with the current implementation of schema managers * that modify the diff while processing it. */ public function unsetDroppedIndex(Index $index): void { $this->droppedIndexes = array_filter( $this->droppedIndexes, static function (Index $droppedIndex) use ($index): bool { return $droppedIndex !== $index; }, ); } }