Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\IntegrationsBundle\Migration;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
|
||||
abstract class AbstractMigration implements MigrationInterface
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $queries = [];
|
||||
|
||||
public function __construct(
|
||||
protected EntityManager $entityManager,
|
||||
protected string $tablePrefix,
|
||||
) {
|
||||
}
|
||||
|
||||
public function shouldExecute(): bool
|
||||
{
|
||||
return $this->isApplicable($this->entityManager->getConnection()->createSchemaManager()->introspectSchema());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Doctrine\DBAL\Exception
|
||||
*/
|
||||
public function execute(): void
|
||||
{
|
||||
$this->up();
|
||||
|
||||
if (!$this->queries) {
|
||||
return;
|
||||
}
|
||||
|
||||
$connection = $this->entityManager->getConnection();
|
||||
|
||||
foreach ($this->queries as $sql) {
|
||||
$stmt = $connection->prepare($sql);
|
||||
$stmt->executeStatement();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the ALTER TABLE query that adds the foreign key.
|
||||
*
|
||||
* @param string[] $columns
|
||||
* @param string[] $referenceColumns
|
||||
* @param string $suffix usually a 'ON DELETE ...' statement
|
||||
*/
|
||||
protected function generateAlterTableForeignKeyStatement(
|
||||
string $table,
|
||||
array $columns,
|
||||
string $referenceTable,
|
||||
array $referenceColumns,
|
||||
string $suffix = '',
|
||||
): string {
|
||||
return "ALTER TABLE {$this->concatPrefix($table)}
|
||||
ADD CONSTRAINT {$this->generatePropertyName($table, 'fk', $columns)}
|
||||
FOREIGN KEY ({$this->columnsToString($columns)})
|
||||
REFERENCES {$this->concatPrefix($referenceTable)} ({$this->columnsToString($referenceColumns)}) {$suffix}
|
||||
";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $columns
|
||||
*/
|
||||
protected function generateIndexStatement(string $table, array $columns): string
|
||||
{
|
||||
return "INDEX {$this->generatePropertyName($table, 'idx', $columns)} ({$this->columnsToString($columns)})";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $columns
|
||||
*/
|
||||
protected function columnsToString(array $columns): string
|
||||
{
|
||||
return implode(',', $columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the name for the property.
|
||||
*
|
||||
* This method was copied from AbstractMauticMigration.
|
||||
*
|
||||
* @param string[] $columnNames
|
||||
*/
|
||||
protected function generatePropertyName(string $table, string $type, array $columnNames): string
|
||||
{
|
||||
$columnNames = array_merge([$this->tablePrefix.$table], $columnNames);
|
||||
$hash = implode(
|
||||
'',
|
||||
array_map(
|
||||
fn ($column): string => dechex(crc32($column)),
|
||||
$columnNames
|
||||
)
|
||||
);
|
||||
|
||||
return substr(strtoupper($type.'_'.$hash), 0, 63);
|
||||
}
|
||||
|
||||
protected function addSql(string $sql): void
|
||||
{
|
||||
$this->queries[] = $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates table/index prefix to the provided name.
|
||||
*/
|
||||
protected function concatPrefix(string $name): string
|
||||
{
|
||||
return $this->tablePrefix.$name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define in the child migration whether the migration should be executed.
|
||||
* Check if the migration is applied in the schema already.
|
||||
*/
|
||||
abstract protected function isApplicable(Schema $schema): bool;
|
||||
|
||||
/**
|
||||
* Define queries for migration up.
|
||||
*/
|
||||
abstract protected function up(): void;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\IntegrationsBundle\Migration;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Mautic\IntegrationsBundle\Exception\PathNotFoundException;
|
||||
|
||||
class Engine
|
||||
{
|
||||
private string $migrationsPath;
|
||||
|
||||
public function __construct(
|
||||
private EntityManagerInterface $entityManager,
|
||||
private string $tablePrefix,
|
||||
string $pluginPath,
|
||||
private string $bundleName,
|
||||
) {
|
||||
$this->migrationsPath = $pluginPath.'/Migrations/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Run available migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
try {
|
||||
$migrationClasses = $this->getMigrationClasses();
|
||||
} catch (PathNotFoundException $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$migrationClasses) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->entityManager->beginTransaction();
|
||||
|
||||
try {
|
||||
foreach ($migrationClasses as $migrationClass) {
|
||||
/** @var AbstractMigration $migration */
|
||||
$migration = new $migrationClass($this->entityManager, $this->tablePrefix);
|
||||
|
||||
if ($migration->shouldExecute()) {
|
||||
$migration->execute();
|
||||
}
|
||||
}
|
||||
|
||||
// PHP 8+ and pdo_mysql will autocommit a DDL transaction and therefore will throw "No active transaction"
|
||||
// So check directly if the transaction is still active before committing
|
||||
$connection = $this->entityManager->getConnection()->getNativeConnection();
|
||||
if (!$connection instanceof \PDO || $connection->inTransaction()) {
|
||||
$this->entityManager->commit();
|
||||
}
|
||||
} catch (\Doctrine\DBAL\Exception $e) {
|
||||
$this->entityManager->rollback();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get migration classes to proceed.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function getMigrationClasses(): array
|
||||
{
|
||||
$migrationFileNames = $this->getMigrationFileNames();
|
||||
$migrationClasses = [];
|
||||
|
||||
foreach ($migrationFileNames as $fileName) {
|
||||
require_once $this->migrationsPath.$fileName;
|
||||
$className = preg_replace('/\\.[^.\\s]{3,4}$/', '', $fileName);
|
||||
$className = 'MauticPlugin\\'.$this->bundleName."\Migrations\\{$className}";
|
||||
$migrationClasses[] = $className;
|
||||
}
|
||||
|
||||
return $migrationClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get migration file names.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function getMigrationFileNames(): array
|
||||
{
|
||||
$fileNames = @scandir($this->migrationsPath);
|
||||
|
||||
if (false === $fileNames) {
|
||||
throw new PathNotFoundException(sprintf("'%s' directory not found", $this->migrationsPath));
|
||||
}
|
||||
|
||||
return array_diff($fileNames, ['.', '..']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\IntegrationsBundle\Migration;
|
||||
|
||||
interface MigrationInterface
|
||||
{
|
||||
/**
|
||||
* Returns true if the migration should be executed.
|
||||
*/
|
||||
public function shouldExecute(): bool;
|
||||
|
||||
/**
|
||||
* Execute migration if applicable.
|
||||
*/
|
||||
public function execute(): void;
|
||||
}
|
||||
Reference in New Issue
Block a user