9
9
use Doctrine \DBAL \Connections \MasterSlaveConnection ;
10
10
use Doctrine \DBAL \Platforms \AbstractPlatform ;
11
11
use Doctrine \DBAL \Schema \AbstractSchemaManager ;
12
+ use Doctrine \DBAL \Schema \Comparator ;
12
13
use Doctrine \DBAL \Schema \Table ;
14
+ use Doctrine \DBAL \Schema \TableDiff ;
13
15
use Doctrine \DBAL \Types \Type ;
16
+ use Doctrine \Migrations \Exception \MetadataStorageError ;
17
+ use Doctrine \Migrations \Metadata \AvailableMigration ;
14
18
use Doctrine \Migrations \Metadata \ExecutedMigration ;
15
19
use Doctrine \Migrations \Metadata \ExecutedMigrationsSet ;
20
+ use Doctrine \Migrations \MigrationRepository ;
16
21
use Doctrine \Migrations \Version \Direction ;
17
22
use Doctrine \Migrations \Version \ExecutionResult ;
18
23
use Doctrine \Migrations \Version \Version ;
22
27
use function floatval ;
23
28
use function round ;
24
29
use function sprintf ;
30
+ use function strlen ;
31
+ use function strpos ;
25
32
use function strtolower ;
26
33
27
34
final class TableMetadataStorage implements MetadataStorage
@@ -38,46 +45,28 @@ final class TableMetadataStorage implements MetadataStorage
38
45
/** @var TableMetadataStorageConfiguration */
39
46
private $ configuration ;
40
47
41
- public function __construct (Connection $ connection , ?MetadataStorageConfiguration $ configuration = null )
42
- {
43
- $ this ->connection = $ connection ;
44
- $ this ->schemaManager = $ connection ->getSchemaManager ();
45
- $ this ->platform = $ connection ->getDatabasePlatform ();
48
+ /** @var MigrationRepository|null */
49
+ private $ migrationRepository ;
50
+
51
+ public function __construct (
52
+ Connection $ connection ,
53
+ ?MetadataStorageConfiguration $ configuration = null ,
54
+ ?MigrationRepository $ migrationRepository = null
55
+ ) {
56
+ $ this ->migrationRepository = $ migrationRepository ;
57
+ $ this ->connection = $ connection ;
58
+ $ this ->schemaManager = $ connection ->getSchemaManager ();
59
+ $ this ->platform = $ connection ->getDatabasePlatform ();
46
60
47
61
if ($ configuration !== null && ! ($ configuration instanceof TableMetadataStorageConfiguration)) {
48
62
throw new InvalidArgumentException (sprintf ('%s accepts only %s as configuration ' , self ::class, TableMetadataStorageConfiguration::class));
49
63
}
50
64
$ this ->configuration = $ configuration ?: new TableMetadataStorageConfiguration ();
51
65
}
52
66
53
- private function isInitialized () : bool
54
- {
55
- if ($ this ->connection instanceof MasterSlaveConnection) {
56
- $ this ->connection ->connect ('master ' );
57
- }
58
-
59
- return $ this ->schemaManager ->tablesExist ([$ this ->configuration ->getTableName ()]);
60
- }
61
-
62
- private function initialize () : void
63
- {
64
- $ schemaChangelog = new Table ($ this ->configuration ->getTableName ());
65
-
66
- $ schemaChangelog ->addColumn ($ this ->configuration ->getVersionColumnName (), 'string ' , ['notnull ' => true , 'length ' => $ this ->configuration ->getVersionColumnLength ()]);
67
- $ schemaChangelog ->addColumn ($ this ->configuration ->getExecutedAtColumnName (), 'datetime ' , ['notnull ' => false ]);
68
- $ schemaChangelog ->addColumn ($ this ->configuration ->getExecutionTimeColumnName (), 'integer ' , ['notnull ' => false ]);
69
-
70
- $ schemaChangelog ->setPrimaryKey ([$ this ->configuration ->getVersionColumnName ()]);
71
-
72
- $ this ->schemaManager ->createTable ($ schemaChangelog );
73
- }
74
-
75
67
public function getExecutedMigrations () : ExecutedMigrationsSet
76
68
{
77
- if (! $ this ->isInitialized ()) {
78
- $ this ->initialize ();
79
- }
80
-
69
+ $ this ->checkInitialization ();
81
70
$ rows = $ this ->connection ->fetchAll (sprintf ('SELECT * FROM %s ' , $ this ->configuration ->getTableName ()));
82
71
83
72
$ migrations = [];
@@ -109,9 +98,7 @@ public function getExecutedMigrations() : ExecutedMigrationsSet
109
98
110
99
public function reset () : void
111
100
{
112
- if (! $ this ->isInitialized ()) {
113
- $ this ->initialize ();
114
- }
101
+ $ this ->checkInitialization ();
115
102
116
103
$ this ->connection ->executeUpdate (
117
104
sprintf (
@@ -123,9 +110,7 @@ public function reset() : void
123
110
124
111
public function complete (ExecutionResult $ result ) : void
125
112
{
126
- if (! $ this ->isInitialized ()) {
127
- $ this ->initialize ();
128
- }
113
+ $ this ->checkInitialization ();
129
114
130
115
if ($ result ->getDirection () === Direction::DOWN ) {
131
116
$ this ->connection ->delete ($ this ->configuration ->getTableName (), [
@@ -143,4 +128,111 @@ public function complete(ExecutionResult $result) : void
143
128
]);
144
129
}
145
130
}
131
+
132
+ public function ensureInitialized () : void
133
+ {
134
+ $ expectedSchemaChangelog = $ this ->getExpectedTable ();
135
+
136
+ if (! $ this ->isInitialized ($ expectedSchemaChangelog )) {
137
+ $ this ->schemaManager ->createTable ($ expectedSchemaChangelog );
138
+
139
+ return ;
140
+ }
141
+
142
+ $ diff = $ this ->needsUpdate ($ expectedSchemaChangelog );
143
+ if ($ diff === null ) {
144
+ return ;
145
+ }
146
+
147
+ $ this ->schemaManager ->alterTable ($ diff );
148
+ $ this ->updateMigratedVersionsFromV1orV2toV3 ();
149
+ }
150
+
151
+ private function needsUpdate (Table $ expectedTable ) : ?TableDiff
152
+ {
153
+ $ comparator = new Comparator ();
154
+ $ currentTable = $ this ->schemaManager ->listTableDetails ($ this ->configuration ->getTableName ());
155
+ $ diff = $ comparator ->diffTable ($ currentTable , $ expectedTable );
156
+
157
+ return $ diff instanceof TableDiff ? $ diff : null ;
158
+ }
159
+
160
+ private function isInitialized (Table $ expectedTable ) : bool
161
+ {
162
+ if ($ this ->connection instanceof MasterSlaveConnection) {
163
+ $ this ->connection ->connect ('master ' );
164
+ }
165
+
166
+ return $ this ->schemaManager ->tablesExist ([$ expectedTable ->getName ()]);
167
+ }
168
+
169
+ private function checkInitialization () : void
170
+ {
171
+ $ expectedTable = $ this ->getExpectedTable ();
172
+
173
+ if (! $ this ->isInitialized ($ expectedTable )) {
174
+ throw MetadataStorageError::notInitialized ();
175
+ }
176
+
177
+ if ($ this ->needsUpdate ($ expectedTable )!== null ) {
178
+ throw MetadataStorageError::notUpToDate ();
179
+ }
180
+ }
181
+
182
+ private function getExpectedTable () : Table
183
+ {
184
+ $ schemaChangelog = new Table ($ this ->configuration ->getTableName ());
185
+
186
+ $ schemaChangelog ->addColumn (
187
+ $ this ->configuration ->getVersionColumnName (),
188
+ 'string ' ,
189
+ ['notnull ' => true , 'length ' => $ this ->configuration ->getVersionColumnLength ()]
190
+ );
191
+ $ schemaChangelog ->addColumn ($ this ->configuration ->getExecutedAtColumnName (), 'datetime ' , ['notnull ' => false ]);
192
+ $ schemaChangelog ->addColumn ($ this ->configuration ->getExecutionTimeColumnName (), 'integer ' , ['notnull ' => false ]);
193
+
194
+ $ schemaChangelog ->setPrimaryKey ([$ this ->configuration ->getVersionColumnName ()]);
195
+
196
+ return $ schemaChangelog ;
197
+ }
198
+
199
+ private function updateMigratedVersionsFromV1orV2toV3 () : void
200
+ {
201
+ if ($ this ->migrationRepository === null ) {
202
+ return ;
203
+ }
204
+
205
+ $ availableMigrations = $ this ->migrationRepository ->getMigrations ()->getItems ();
206
+ $ executedMigrations = $ this ->getExecutedMigrations ()->getItems ();
207
+
208
+ foreach ($ availableMigrations as $ availableMigration ) {
209
+ foreach ($ executedMigrations as $ k => $ executedMigration ) {
210
+ if ($ this ->isAlreadyV3Format ($ availableMigration , $ executedMigration )) {
211
+ continue ;
212
+ }
213
+
214
+ $ this ->connection ->update (
215
+ $ this ->configuration ->getTableName (),
216
+ [
217
+ $ this ->configuration ->getVersionColumnName () => (string ) $ availableMigration ->getVersion (),
218
+ ],
219
+ [
220
+ $ this ->configuration ->getVersionColumnName () => (string ) $ executedMigration ->getVersion (),
221
+ ]
222
+ );
223
+ unset($ executedMigrations [$ k ]);
224
+ }
225
+ }
226
+ }
227
+
228
+ private function isAlreadyV3Format (AvailableMigration $ availableMigration , ExecutedMigration $ executedMigration ) : bool
229
+ {
230
+ return strpos (
231
+ (string ) $ availableMigration ->getVersion (),
232
+ (string ) $ executedMigration ->getVersion ()
233
+ ) !== (
234
+ strlen ((string ) $ availableMigration ->getVersion ()) -
235
+ strlen ((string ) $ executedMigration ->getVersion ())
236
+ );
237
+ }
146
238
}
0 commit comments