*/ 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; } }