File size: 3,426 Bytes
d2897cd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
<?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;
}