. */ namespace OrangeHRM\Installer\Util\V1; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\Column; use Doctrine\DBAL\Schema\ColumnDiff; use Doctrine\DBAL\Schema\ForeignKeyConstraint; use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Types; use InvalidArgumentException; use OrangeHRM\Installer\Util\V1\Dto\Table; use PDO; class SchemaHelper { private AbstractSchemaManager $schemaManager; private Connection $connection; /** * @param Connection $connection */ public function __construct(Connection $connection) { $this->connection = $connection; $this->schemaManager = $connection->createSchemaManager(); } /** * @return Connection */ protected function getConnection(): Connection { return $this->connection; } /** * @param string $tableName * @param string $columnName */ public function dropColumn(string $tableName, string $columnName): void { $column = $this->getTableColumn($tableName, $columnName); $diff = new TableDiff($tableName, [], [], [$column]); $this->getSchemaManager()->alterTable($diff); } /** * @param string $tableName * @param string[] $columnNames */ public function dropColumns(string $tableName, array $columnNames): void { $removedColumns = []; foreach ($columnNames as $columnName) { $removedColumns[] = $this->getTableColumn($tableName, $columnName); } $diff = new TableDiff($tableName, [], [], $removedColumns); $this->getSchemaManager()->alterTable($diff); } /** * @param string $tableName * @param string $columnName * @return Column|null */ public function getTableColumn(string $tableName, string $columnName): ?Column { return $this->getSchemaManager()->listTableColumns($tableName)[$columnName] ?? null; } /** * @return AbstractSchemaManager */ public function getSchemaManager(): AbstractSchemaManager { return $this->schemaManager; } /** * @param string $tableName * @param string $columnName * @param string $type e.g. \Doctrine\DBAL\Types\Types\Types::STRING, Types::INTEGER * @param array $options ['Type' => \Doctrine\DBAL\Types\Type, 'Length' => int|null, 'Precision' => int, 'Scale' => int, 'Unsigned' => bool, 'Fixed' => bool, 'Notnull' => bool, 'Default' => mixed, 'Autoincrement' => bool, 'Comment' => string|null */ public function addColumn(string $tableName, string $columnName, string $type, array $options = []): void { $column = new Column($columnName, Type::getType($type), $options); $diff = new TableDiff($tableName, [$column]); $this->getSchemaManager()->alterTable($diff); } /** * @param string $tableName * @param array $columnOptions */ public function addOrChangeColumns(string $tableName, array $columnOptions): void { $addedColumns = []; $changedColumns = []; foreach ($columnOptions as $columnName => $options) { $column = $this->getTableColumn($tableName, $columnName); if ($column == null) { if (!isset($options['Type'])) { throw new InvalidArgumentException("Option `Type` not defined under `$columnName` column"); } $addedColumns[] = new Column($columnName, $options['Type'], $options); } else { $newColumn = clone $column; $newColumn->setOptions($options); $changedColumns[] = new ColumnDiff($columnName, $newColumn, array_keys($options), $column); } } if (!(empty($addedColumns) && empty($changedColumns))) { $diff = new TableDiff($tableName, $addedColumns, $changedColumns); $this->getSchemaManager()->alterTable($diff); } } /** * @param string $tableName * @param string $columnName * @param array $options ['Type' => \Doctrine\DBAL\Types\Type, 'Length' => int|null, 'Precision' => int, 'Scale' => int, 'Unsigned' => bool, 'Fixed' => bool, 'Notnull' => bool, 'Default' => mixed, 'Autoincrement' => bool, 'Comment' => string|null] */ public function changeColumn(string $tableName, string $columnName, array $options = []): void { $column = $this->getTableColumn($tableName, $columnName); $newColumn = clone $column; $newColumn->setOptions($options); $columnDiff = new ColumnDiff($columnName, $newColumn, [], $column); $diff = new TableDiff($tableName, [], [$columnDiff]); $this->getSchemaManager()->alterTable($diff); } /** * @param string $tableName * @param string $currentColumnName * @param string $newColumnName */ public function renameColumn(string $tableName, string $currentColumnName, string $newColumnName): void { $column = $this->getTableColumn($tableName, $currentColumnName); $newColumn = new Column($newColumnName, $column->getType(), [ 'Length' => $column->getLength(), 'Precision' => $column->getPrecision(), 'Scale' => $column->getScale(), 'Unsigned' => $column->getUnsigned(), 'Fixed' => $column->getFixed(), 'Notnull' => $column->getNotnull(), 'Default' => $column->getDefault(), 'Autoincrement' => $column->getAutoincrement(), 'Comment' => $column->getComment(), ]); $columnDiff = new ColumnDiff($currentColumnName, $newColumn, [], $column); $diff = new TableDiff($tableName, [], [$columnDiff]); $this->getSchemaManager()->alterTable($diff); } /** * @param string $tableName * @param string[]|ForeignKeyConstraint[] $foreignKeys */ public function dropForeignKeys(string $tableName, array $foreignKeys): void { $diff = new TableDiff($tableName); $diff->removedForeignKeys = $foreignKeys; $this->getSchemaManager()->alterTable($diff); } /** * @param string $localTableName * @param ForeignKeyConstraint $foreignKeyConstraint */ public function addForeignKey(string $localTableName, ForeignKeyConstraint $foreignKeyConstraint): void { $diff = new TableDiff($localTableName); $diff->addedForeignKeys = [$foreignKeyConstraint]; $this->getSchemaManager()->alterTable($diff); } /** * @param string $name * @param string $charset e.g. utf8mb4, utf8 * @param string|null $collate e.g. utf8mb4_unicode_ci, utf8_general_ci * @return Table */ public function createTable(string $name, string $charset = 'utf8', ?string $collate = null): Table { $table = new Table($name); // Only applicable for `src/vendor/doctrine/dbal/src/Platforms/MySQLPlatform.php` $table->addOption('charset', $charset); $table->addOption('collate', $collate ?? $charset . '_unicode_ci'); $table->addOption('engine', 'InnoDB'); $table->setSchemaManager($this->getSchemaManager()); return $table; } /** * @param array $table * @return bool */ public function tableExists(array $table): bool { return $this->getSchemaManager()->tablesExist($table); } /** * @param string $tableName * @return void */ public function dropPrimaryKey(string $tableName): void { $table = $this->getSchemaManager()->listTableDetails($tableName); $this->getSchemaManager()->dropIndex($table->getPrimaryKey(), $table); } /** * @return PDO */ private function getNativeConnection(): PDO { return $this->getConnection()->getNativeConnection(); } /** * @return void */ public function disableConstraints(): void { $pdo = $this->getNativeConnection(); $pdo->exec('SET FOREIGN_KEY_CHECKS=0;'); } /** * @return void */ public function enableConstraints(): void { $pdo = $this->getNativeConnection(); $pdo->exec('SET FOREIGN_KEY_CHECKS=1;'); } /** * @param string $tableName * @param string $column * @return bool */ public function columnExists(string $tableName, string $column): bool { $table = $this->getSchemaManager()->listTableDetails($tableName); return $table->hasColumn($column); } /** * @param string $tableName * @param string $index */ public function dropIndex(string $tableName, string $index): void { $this->getSchemaManager()->dropIndex($index, $tableName); } }