Spaces:
No application file
No application file
namespace Mautic\CoreBundle\Doctrine\Helper; | |
use Doctrine\DBAL\Connection; | |
use Doctrine\DBAL\Schema\Comparator; | |
use Doctrine\DBAL\Schema\Table; | |
use Mautic\CoreBundle\Exception\SchemaException; | |
/** | |
* Used to manipulate the schema of an existing table. | |
*/ | |
class ColumnSchemaHelper | |
{ | |
/** | |
* @var \Doctrine\DBAL\Schema\AbstractSchemaManager<\Doctrine\DBAL\Platforms\AbstractMySQLPlatform> | |
*/ | |
protected \Doctrine\DBAL\Schema\AbstractSchemaManager $sm; | |
/** | |
* @var string | |
*/ | |
protected $tableName; | |
/** | |
* @var Table | |
*/ | |
protected $fromTable; | |
/** | |
* @var Table | |
*/ | |
protected $toTable; | |
private $columns; | |
/** | |
* @param string $prefix | |
*/ | |
public function __construct( | |
protected Connection $db, | |
protected $prefix | |
) { | |
$this->sm = $db->createSchemaManager(); | |
} | |
/** | |
* Set the table to be manipulated. | |
* | |
* @param bool $addPrefix | |
* | |
* @return $this | |
* | |
* @throws SchemaException | |
*/ | |
public function setName($table, $addPrefix = true) | |
{ | |
$this->tableName = ($addPrefix) ? $this->prefix.$table : $table; | |
// make sure the table exists | |
$this->checkTableExists($this->tableName, true); | |
// use the to schema to get table details so that changes will be calculated | |
$this->fromTable = $this->sm->introspectTable($this->tableName); | |
$this->toTable = clone $this->fromTable; | |
return $this; | |
} | |
/** | |
* Get the SchemaManager. | |
* | |
* @return \Doctrine\DBAL\Schema\AbstractSchemaManager<\Doctrine\DBAL\Platforms\AbstractMySQLPlatform> | |
*/ | |
public function getSchemaManager() | |
{ | |
return $this->sm; | |
} | |
/** | |
* Get table details. | |
* | |
* @return Table | |
*/ | |
public function getTable() | |
{ | |
return $this->toTable; | |
} | |
/** | |
* Get array of Doctrine\DBAL\Schema\Column instances for the table. | |
* | |
* @return array | |
*/ | |
public function getColumns() | |
{ | |
if (empty($this->columns)) { | |
$this->columns = $this->toTable->getColumns(); | |
} | |
return $this->columns; | |
} | |
/** | |
* Add an array of columns to the table. | |
* | |
* @throws SchemaException | |
*/ | |
public function addColumns(array $columns): void | |
{ | |
// ensure none of the columns exist before manipulating the schema | |
foreach ($columns as $column) { | |
if (empty($column['name'])) { | |
throw new SchemaException('Column is missing required name key.'); | |
} | |
$this->checkColumnExists($column['name'], true); | |
} | |
// now add the columns | |
foreach ($columns as $column) { | |
$this->addColumn($column, false); | |
} | |
} | |
/** | |
* Add a column to the table. | |
* | |
* ['name'] string (required) unique name of column; cannot already exist | |
* ['type'] string (optional) Doctrine type for column; defaults to text | |
* ['options'] array (optional) Defining options for column | |
* | |
* @param bool $checkExists Check if table exists; pass false if this has already been done | |
* | |
* @return $this | |
* | |
* @throws SchemaException | |
*/ | |
public function addColumn(array $column, $checkExists = true) | |
{ | |
if (empty($column['name'])) { | |
throw new SchemaException('Column is missing required name key.'); | |
} | |
if ($checkExists) { | |
$this->checkColumnExists($column['name'], true); | |
} | |
$type = $column['type'] ?? 'text'; | |
$options = $column['options'] ?? []; | |
$this->toTable->addColumn($column['name'], $type, $options); | |
return $this; | |
} | |
/** | |
* Drops a column from table. | |
* | |
* @return $this | |
*/ | |
public function dropColumn($columnName) | |
{ | |
if ($this->checkColumnExists($columnName)) { | |
$this->toTable->dropColumn($columnName); | |
} | |
return $this; | |
} | |
/** | |
* Computes and executes the changes. | |
*/ | |
public function executeChanges(): void | |
{ | |
// create a table diff | |
$comparator = new Comparator(); | |
$diff = $comparator->compareTables($this->fromTable, $this->toTable); | |
if (!$diff->isEmpty()) { | |
$this->sm->alterTable($diff); | |
} | |
} | |
/** | |
* Determine if a column already exists. | |
* | |
* @param string $column | |
* @param bool $throwException | |
* | |
* @throws SchemaException | |
*/ | |
public function checkColumnExists($column, $throwException = false): bool | |
{ | |
// check to ensure column doesn't exist | |
if ($this->toTable->hasColumn($column)) { | |
if ($throwException) { | |
throw new SchemaException("The column {$column} already exists in {$this->tableName}"); | |
} | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Determine if a table exists. | |
* | |
* @param bool|false $throwException | |
* | |
* @return bool | |
* | |
* @throws SchemaException | |
*/ | |
public function checkTableExists($table, $throwException = false) | |
{ | |
if (!$this->sm->tablesExist($table)) { | |
if ($throwException) { | |
throw new SchemaException("Table $table does not exist!"); | |
} else { | |
return false; | |
} | |
} else { | |
return true; | |
} | |
} | |
} | |