Spaces:
No application file
No application file
namespace Mautic\CoreBundle\Doctrine\Helper; | |
use Doctrine\DBAL\Connection; | |
use Doctrine\DBAL\Schema\Schema; | |
use Mautic\CoreBundle\Exception\SchemaException; | |
/** | |
* Used to manipulate creation/removal of tables. | |
*/ | |
class TableSchemaHelper | |
{ | |
/** | |
* @var \Doctrine\DBAL\Schema\AbstractSchemaManager<\Doctrine\DBAL\Platforms\AbstractMySQLPlatform> | |
*/ | |
protected \Doctrine\DBAL\Schema\AbstractSchemaManager $sm; | |
/** | |
* @var Schema | |
*/ | |
protected $schema; | |
/** | |
* @var string[] | |
*/ | |
protected array $dropTables = []; | |
/** | |
* @var string[] | |
*/ | |
protected array $addTables = []; | |
/** | |
* @param string $prefix | |
*/ | |
public function __construct( | |
protected Connection $db, | |
protected $prefix, | |
protected ColumnSchemaHelper $columnHelper | |
) { | |
$this->sm = $db->createSchemaManager(); | |
} | |
/** | |
* Get the SchemaManager. | |
* | |
* @return \Doctrine\DBAL\Schema\AbstractSchemaManager<\Doctrine\DBAL\Platforms\AbstractMySQLPlatform> | |
*/ | |
public function getSchemaManager() | |
{ | |
return $this->sm; | |
} | |
/** | |
* Add an array of tables to db. | |
* | |
* @return $this | |
* | |
* @throws SchemaException | |
*/ | |
public function addTables(array $tables) | |
{ | |
// ensure none of the tables exist before manipulating the schema | |
foreach ($tables as $table) { | |
if (empty($table['name'])) { | |
throw new SchemaException('Table is missing required name key.'); | |
} | |
$this->checkTableExists($table['name'], true); | |
} | |
// now add the tables | |
foreach ($tables as $table) { | |
$this->addTables[] = $table; | |
$this->addTable($table, false); | |
} | |
return $this; | |
} | |
/** | |
* Add a table to the db. | |
* | |
* ['name'] string (required) unique name of table; cannot already exist | |
* ['columns'] array (optional) Array of columns to add in the format of | |
* array( | |
* array( | |
* 'name' => 'column_name', //required | |
* 'type' => 'string', //optional, defaults to text | |
* 'options' => array(...) //optional, column options | |
* ), | |
* ... | |
* ) | |
* ['options'] array (optional) Defining options for table | |
* array( | |
* 'primaryKey' => array(), | |
* 'uniqueIndex' => array() | |
* ) | |
* | |
* @return $this | |
* | |
* @throws SchemaException | |
*/ | |
public function addTable(array $table, $checkExists = true, $dropExisting = false) | |
{ | |
if (empty($table['name'])) { | |
throw new SchemaException('Table is missing required name key.'); | |
} | |
if ($checkExists || $dropExisting) { | |
$throwException = ($dropExisting) ? false : true; | |
if ($this->checkTableExists($table['name'], $throwException) && $dropExisting) { | |
$this->deleteTable($table['name']); | |
} | |
} | |
$this->addTables[] = $table; | |
$options = $table['options'] ?? []; | |
$columns = $table['columns'] ?? []; | |
$newTable = $this->getSchema()->createTable($this->prefix.$table['name']); | |
if (!empty($columns)) { | |
// just to make sure a same name column is not added | |
$columnsAdded = []; | |
foreach ($columns as $column) { | |
if (empty($column['name'])) { | |
throw new SchemaException('A column is missing required name key.'); | |
} | |
if (!isset($columns[$column['name']])) { | |
$type = $column['type'] ?? 'text'; | |
$colOptions = $column['options'] ?? []; | |
$newTable->addColumn($column['name'], $type, $colOptions); | |
$columnsAdded[] = $column['name']; | |
} | |
} | |
} | |
if (!empty($options)) { | |
foreach ($options as $option => $value) { | |
$func = ('uniqueIndex' == $option ? 'add' : 'set').ucfirst($option); | |
$newTable->$func($value); | |
} | |
} | |
return $this; | |
} | |
/** | |
* @return $this | |
* | |
* @throws SchemaException | |
*/ | |
public function deleteTable($table) | |
{ | |
if ($this->checkTableExists($table)) { | |
$this->dropTables[] = $table; | |
} | |
return $this; | |
} | |
/** | |
* Executes the changes. | |
*/ | |
public function executeChanges(): void | |
{ | |
$platform = $this->db->getDatabasePlatform(); | |
foreach ($this->dropTables as $t) { | |
$this->sm->dropTable($this->prefix.$t); | |
} | |
$sql = $this->getSchema()->toSql($platform); | |
foreach ($sql as $s) { | |
$this->db->executeStatement($s); | |
} | |
// reset schema | |
$this->schema = new Schema([], [], $this->sm->createSchemaConfig()); | |
$this->dropTables = $this->addTables = []; | |
} | |
/** | |
* Determine if a table exists. | |
* | |
* @param string $table | |
* @param bool $throwException | |
* | |
* @throws SchemaException | |
*/ | |
public function checkTableExists($table, $throwException = false): bool | |
{ | |
if ($this->sm->tablesExist($this->prefix.$table)) { | |
if ($throwException) { | |
throw new SchemaException($this->prefix."$table already exists"); | |
} | |
return true; | |
} | |
return false; | |
} | |
private function getSchema(): Schema | |
{ | |
if ($this->schema) { | |
return $this->schema; | |
} | |
if ($this->db instanceof \Doctrine\DBAL\Connections\PrimaryReadReplicaConnection) { | |
$params = $this->db->getParams(); | |
$schemaConfig = new \Doctrine\DBAL\Schema\SchemaConfig(); | |
$schemaConfig->setName($params['master']['dbname']); | |
$this->schema = new Schema([], [], $schemaConfig); | |
} else { | |
$this->schema = new Schema([], [], $this->sm->createSchemaConfig()); | |
} | |
return $this->schema; | |
} | |
} | |