Completed
Pull Request — 2.10 (#3762)
by Benjamin
64:30
created

SchemaDiff::getNewTablesSortedByDependencies()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
eloc 12
c 0
b 0
f 0
dl 0
loc 23
ccs 0
cts 0
cp 0
rs 9.5555
cc 5
nc 8
nop 0
crap 30
1
<?php
2
3
namespace Doctrine\DBAL\Schema;
4
5
use Doctrine\DBAL\Internal\CommitOrderCalculator;
6
use Doctrine\DBAL\Platforms\AbstractPlatform;
7
use function array_merge;
8
9
/**
10
 * Schema Diff.
11
 */
12
class SchemaDiff
13
{
14
    /** @var Schema|null */
15
    public $fromSchema;
16
17
    /**
18
     * All added namespaces.
19
     *
20
     * @var string[]
21
     */
22
    public $newNamespaces = [];
23
24
    /**
25
     * All removed namespaces.
26
     *
27
     * @var string[]
28
     */
29
    public $removedNamespaces = [];
30
31
    /**
32
     * All added tables.
33
     *
34
     * @var Table[]
35
     */
36
    public $newTables = [];
37
38
    /**
39
     * All changed tables.
40
     *
41
     * @var TableDiff[]
42
     */
43
    public $changedTables = [];
44
45
    /**
46
     * All removed tables.
47
     *
48
     * @var Table[]
49
     */
50
    public $removedTables = [];
51
52
    /** @var Sequence[] */
53
    public $newSequences = [];
54
55
    /** @var Sequence[] */
56
    public $changedSequences = [];
57
58
    /** @var Sequence[] */
59
    public $removedSequences = [];
60
61
    /** @var ForeignKeyConstraint[] */
62
    public $orphanedForeignKeys = [];
63
64
    /**
65
     * Constructs an SchemaDiff object.
66
     *
67
     * @param Table[]     $newTables
68
     * @param TableDiff[] $changedTables
69
     * @param Table[]     $removedTables
70 820
     */
71
    public function __construct($newTables = [], $changedTables = [], $removedTables = [], ?Schema $fromSchema = null)
72 820
    {
73 820
        $this->newTables     = $newTables;
74 820
        $this->changedTables = $changedTables;
75 820
        $this->removedTables = $removedTables;
76 820
        $this->fromSchema    = $fromSchema;
77
    }
78
79
    /**
80
     * The to save sql mode ensures that the following things don't happen:
81
     *
82
     * 1. Tables are deleted
83
     * 2. Sequences are deleted
84
     * 3. Foreign Keys which reference tables that would otherwise be deleted.
85
     *
86
     * This way it is ensured that assets are deleted which might not be relevant to the metadata schema at all.
87
     *
88
     * @return string[]
89 52
     */
90
    public function toSaveSql(AbstractPlatform $platform)
91 52
    {
92
        return $this->_toSql($platform, true);
93
    }
94
95
    /**
96
     * @return string[]
97 714
     */
98
    public function toSql(AbstractPlatform $platform)
99 714
    {
100
        return $this->_toSql($platform, false);
101
    }
102
103
    /**
104
     * @param bool $saveMode
105
     *
106
     * @return string[]
107 716
     */
108
    protected function _toSql(AbstractPlatform $platform, $saveMode = false)
109 716
    {
110
        $sql = [];
111 716
112 322
        if ($platform->supportsSchemas()) {
113 322
            foreach ($this->newNamespaces as $newNamespace) {
114
                $sql[] = $platform->getCreateSchemaSQL($newNamespace);
115
            }
116
        }
117 716
118 712
        if ($platform->supportsForeignKeyConstraints() && $saveMode === false) {
119 77
            foreach ($this->orphanedForeignKeys as $orphanedForeignKey) {
120
                $sql[] = $platform->getDropForeignKeySQL($orphanedForeignKey, $orphanedForeignKey->getLocalTable());
121
            }
122
        }
123 716
124 322
        if ($platform->supportsSequences() === true) {
0 ignored issues
show
introduced by
The condition $platform->supportsSequences() === true is always false.
Loading history...
125 79
            foreach ($this->changedSequences as $sequence) {
126
                $sql[] = $platform->getAlterSequenceSQL($sequence);
127
            }
128 322
129 320
            if ($saveMode === false) {
130 77
                foreach ($this->removedSequences as $sequence) {
131
                    $sql[] = $platform->getDropSequenceSQL($sequence);
132
                }
133
            }
134 322
135 79
            foreach ($this->newSequences as $sequence) {
136
                $sql[] = $platform->getCreateSequenceSQL($sequence);
137
            }
138
        }
139 716
140 716
        $foreignKeySql = [];
141 81
        $createFlags   = AbstractPlatform::CREATE_INDEXES;
142 81
143 81
        if (! $platform->supportsCreateDropForeignKeyConstraints()) {
144
            $createFlags |= AbstractPlatform::CREATE_FOREIGNKEYS;
145
        }
146 81
147 27
        foreach ($this->getNewTablesSortedByDependencies() as $table) {
148
            $sql = array_merge($sql, $platform->getCreateTableSQL($table, $createFlags));
149
150 79
            if (! $platform->supportsCreateDropForeignKeyConstraints()) {
151 79
                continue;
152
            }
153
154 716
            foreach ($table->getForeignKeys() as $foreignKey) {
155
                $foreignKeySql[] = $platform->getCreateForeignKeySQL($foreignKey, $table);
156 716
            }
157 714
        }
158 77
        $sql = array_merge($sql, $foreignKeySql);
159
160
        if ($saveMode === false) {
161
            foreach ($this->removedTables as $table) {
162 716
                $sql[] = $platform->getDropTableSQL($table);
163 471
            }
164
        }
165
166 716
        foreach ($this->changedTables as $tableDiff) {
167
            $sql = array_merge($sql, $platform->getAlterTableSQL($tableDiff));
168
        }
169
170
        return $sql;
171
    }
172
173
    /**
174
     * Sorts tables by dependencies so that they are created in the right order.
175
     *
176
     * This is ncessary when one table depends on another while creating foreign key
177
     * constraints directly during CREATE TABLE.
178
     *
179
     * @return array<Table>
180
     */
181
    private function getNewTablesSortedByDependencies()
182
    {
183
        $commitOrderCalculator = new CommitOrderCalculator();
184
        $newTables             = [];
185
186
        foreach ($this->newTables as $table) {
187
            $newTables[$table->getName()] = true;
188
            $commitOrderCalculator->addNode($table->getName(), $table);
189
        }
190
191
        foreach ($this->newTables as $table) {
192
            foreach ($table->getForeignKeys() as $foreignKey) {
193
                $foreignTableName = $foreignKey->getForeignTableName();
194
195
                if (! isset($newTables[$foreignTableName])) {
196
                    continue;
197
                }
198
199
                $commitOrderCalculator->addDependency($foreignTableName, $table->getName(), 1);
200
            }
201
        }
202
203
        return $commitOrderCalculator->sort();
204
    }
205
}
206