|
1 <?php |
|
2 /* |
|
3 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
4 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
5 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
6 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
7 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
8 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
9 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
10 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
11 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
12 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
13 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
14 * |
|
15 * This software consists of voluntary contributions made by many individuals |
|
16 * and is licensed under the LGPL. For more information, see |
|
17 * <http://www.doctrine-project.org>. |
|
18 */ |
|
19 |
|
20 namespace Doctrine\DBAL\Migrations\Configuration; |
|
21 |
|
22 use Doctrine\DBAL\Connection, |
|
23 Doctrine\DBAL\Migrations\MigrationException, |
|
24 Doctrine\DBAL\Migrations\Version, |
|
25 Doctrine\DBAL\Migrations\OutputWriter, |
|
26 Doctrine\DBAL\Schema\Table, |
|
27 Doctrine\DBAL\Schema\Column, |
|
28 Doctrine\DBAL\Types\Type; |
|
29 |
|
30 /** |
|
31 * Default Migration Configurtion object used for configuring an instance of |
|
32 * the Migration class. Set the connection, version table name, register migration |
|
33 * classes/versions, etc. |
|
34 * |
|
35 * @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
|
36 * @link www.doctrine-project.org |
|
37 * @since 2.0 |
|
38 * @author Jonathan H. Wage <jonwage@gmail.com> |
|
39 */ |
|
40 class Configuration |
|
41 { |
|
42 /** |
|
43 * Name of this set of migrations |
|
44 * |
|
45 * @var string |
|
46 */ |
|
47 private $name; |
|
48 |
|
49 /** |
|
50 * Flag for whether or not the migration table has been created |
|
51 * |
|
52 * @var bool |
|
53 */ |
|
54 private $migrationTableCreated = false; |
|
55 |
|
56 /** |
|
57 * Connection instance to use for migrations |
|
58 * |
|
59 * @var Connection |
|
60 */ |
|
61 private $connection; |
|
62 |
|
63 /** |
|
64 * OutputWriter instance for writing output during migrations |
|
65 * |
|
66 * @var OutputWriter |
|
67 */ |
|
68 private $outputWriter; |
|
69 |
|
70 /** |
|
71 * The migration table name to track versions in |
|
72 * |
|
73 * @var string |
|
74 */ |
|
75 private $migrationsTableName = 'doctrine_migration_versions'; |
|
76 |
|
77 /** |
|
78 * The path to a directory where new migration classes will be written |
|
79 * |
|
80 * @var string |
|
81 */ |
|
82 private $migrationsDirectory; |
|
83 |
|
84 /** |
|
85 * Namespace the migration classes live in |
|
86 * |
|
87 * @var string |
|
88 */ |
|
89 private $migrationsNamespace; |
|
90 |
|
91 /** |
|
92 * Array of the registered migrations |
|
93 * |
|
94 * @var array |
|
95 */ |
|
96 private $migrations = array(); |
|
97 |
|
98 /** |
|
99 * Construct a migration configuration object. |
|
100 * |
|
101 * @param Connection $connection A Connection instance |
|
102 * @param OutputWriter $outputWriter A OutputWriter instance |
|
103 */ |
|
104 public function __construct(Connection $connection, OutputWriter $outputWriter = null) |
|
105 { |
|
106 $this->connection = $connection; |
|
107 if ($outputWriter === null) { |
|
108 $outputWriter = new OutputWriter(); |
|
109 } |
|
110 $this->outputWriter = $outputWriter; |
|
111 } |
|
112 |
|
113 /** |
|
114 * Validation that this instance has all the required properties configured |
|
115 * |
|
116 * @return void |
|
117 * @throws MigrationException |
|
118 */ |
|
119 public function validate() |
|
120 { |
|
121 if ( ! $this->migrationsNamespace) { |
|
122 throw MigrationException::migrationsNamespaceRequired(); |
|
123 } |
|
124 if ( ! $this->migrationsDirectory) { |
|
125 throw MigrationException::migrationsDirectoryRequired(); |
|
126 } |
|
127 } |
|
128 |
|
129 /** |
|
130 * Set the name of this set of migrations |
|
131 * |
|
132 * @param string $name The name of this set of migrations |
|
133 */ |
|
134 public function setName($name) |
|
135 { |
|
136 $this->name = $name; |
|
137 } |
|
138 |
|
139 /** |
|
140 * Returns the name of this set of migrations |
|
141 * |
|
142 * @return string $name The name of this set of migrations |
|
143 */ |
|
144 public function getName() |
|
145 { |
|
146 return $this->name; |
|
147 } |
|
148 |
|
149 /** |
|
150 * Returns the OutputWriter instance |
|
151 * |
|
152 * @return OutputWriter $outputWriter The OutputWriter instance |
|
153 */ |
|
154 public function getOutputWriter() |
|
155 { |
|
156 return $this->outputWriter; |
|
157 } |
|
158 |
|
159 /** |
|
160 * Returns a timestamp version as a formatted date |
|
161 * |
|
162 * @param string $version |
|
163 * @return string $formattedVersion The formatted version |
|
164 */ |
|
165 public function formatVersion($version) |
|
166 { |
|
167 return sprintf('%s-%s-%s %s:%s:%s', |
|
168 substr($version, 0, 4), |
|
169 substr($version, 4, 2), |
|
170 substr($version, 6, 2), |
|
171 substr($version, 8, 2), |
|
172 substr($version, 10, 2), |
|
173 substr($version, 12, 2) |
|
174 ); |
|
175 } |
|
176 |
|
177 /** |
|
178 * Returns the Connection instance |
|
179 * |
|
180 * @return Connection $connection The Connection instance |
|
181 */ |
|
182 public function getConnection() |
|
183 { |
|
184 return $this->connection; |
|
185 } |
|
186 |
|
187 /** |
|
188 * Set the migration table name |
|
189 * |
|
190 * @param string $tableName The migration table name |
|
191 */ |
|
192 public function setMigrationsTableName($tableName) |
|
193 { |
|
194 $this->migrationsTableName = $tableName; |
|
195 } |
|
196 |
|
197 /** |
|
198 * Returns the migration table name |
|
199 * |
|
200 * @return string $migrationsTableName The migration table name |
|
201 */ |
|
202 public function getMigrationsTableName() |
|
203 { |
|
204 return $this->migrationsTableName; |
|
205 } |
|
206 |
|
207 /** |
|
208 * Set the new migrations directory where new migration classes are generated |
|
209 * |
|
210 * @param string $migrationsDirectory The new migrations directory |
|
211 */ |
|
212 public function setMigrationsDirectory($migrationsDirectory) |
|
213 { |
|
214 $this->migrationsDirectory = $migrationsDirectory; |
|
215 } |
|
216 |
|
217 /** |
|
218 * Returns the new migrations directory where new migration classes are generated |
|
219 * |
|
220 * @return string $migrationsDirectory The new migrations directory |
|
221 */ |
|
222 public function getMigrationsDirectory() |
|
223 { |
|
224 return $this->migrationsDirectory; |
|
225 } |
|
226 |
|
227 /** |
|
228 * Set the migrations namespace |
|
229 * |
|
230 * @param string $migrationsNamespace The migrations namespace |
|
231 */ |
|
232 public function setMigrationsNamespace($migrationsNamespace) |
|
233 { |
|
234 $this->migrationsNamespace = $migrationsNamespace; |
|
235 } |
|
236 |
|
237 /** |
|
238 * Returns the migrations namespace |
|
239 * |
|
240 * @return string $migrationsNamespace The migrations namespace |
|
241 */ |
|
242 public function getMigrationsNamespace() |
|
243 { |
|
244 return $this->migrationsNamespace; |
|
245 } |
|
246 |
|
247 /** |
|
248 * Register migrations from a given directory. Recursively finds all files |
|
249 * with the pattern VersionYYYYMMDDHHMMSS.php as the filename and registers |
|
250 * them as migrations. |
|
251 * |
|
252 * @param string $path The root directory to where some migration classes live. |
|
253 * @return $migrations The array of migrations registered. |
|
254 */ |
|
255 public function registerMigrationsFromDirectory($path) |
|
256 { |
|
257 $path = realpath($path); |
|
258 $path = rtrim($path, '/'); |
|
259 $files = glob($path . '/Version*.php'); |
|
260 $versions = array(); |
|
261 if ($files) { |
|
262 foreach ($files as $file) { |
|
263 require_once($file); |
|
264 $info = pathinfo($file); |
|
265 $version = substr($info['filename'], 7); |
|
266 $class = $this->migrationsNamespace . '\\' . $info['filename']; |
|
267 $versions[] = $this->registerMigration($version, $class); |
|
268 } |
|
269 } |
|
270 return $versions; |
|
271 } |
|
272 |
|
273 /** |
|
274 * Register a single migration version to be executed by a AbstractMigration |
|
275 * class. |
|
276 * |
|
277 * @param string $version The version of the migration in the format YYYYMMDDHHMMSS. |
|
278 * @param string $class The migration class to execute for the version. |
|
279 */ |
|
280 public function registerMigration($version, $class) |
|
281 { |
|
282 $version = (string) $version; |
|
283 $class = (string) $class; |
|
284 if (isset($this->migrations[$version])) { |
|
285 throw MigrationException::duplicateMigrationVersion($version, get_class($this->migrations[$version])); |
|
286 } |
|
287 $version = new Version($this, $version, $class); |
|
288 $this->migrations[$version->getVersion()] = $version; |
|
289 ksort($this->migrations); |
|
290 return $version; |
|
291 } |
|
292 |
|
293 /** |
|
294 * Register an array of migrations. Each key of the array is the version and |
|
295 * the value is the migration class name. |
|
296 * |
|
297 * |
|
298 * @param array $migrations |
|
299 * @return void |
|
300 */ |
|
301 public function registerMigrations(array $migrations) |
|
302 { |
|
303 $versions = array(); |
|
304 foreach ($migrations as $version => $class) { |
|
305 $versions[] = $this->registerMigration($version, $class); |
|
306 } |
|
307 return $versions; |
|
308 } |
|
309 |
|
310 /** |
|
311 * Get the array of registered migration versions. |
|
312 * |
|
313 * @return array $migrations |
|
314 */ |
|
315 public function getMigrations() |
|
316 { |
|
317 return $this->migrations; |
|
318 } |
|
319 |
|
320 /** |
|
321 * Returns the Version instance for a given version in the format YYYYMMDDHHMMSS. |
|
322 * |
|
323 * @param string $version The version string in the format YYYYMMDDHHMMSS. |
|
324 * @return Version $version |
|
325 * @throws MigrationException $exception Throws exception if migration version does not exist. |
|
326 */ |
|
327 public function getVersion($version) |
|
328 { |
|
329 if ( ! isset($this->migrations[$version])) { |
|
330 throw MigrationException::unknownMigrationVersion($version); |
|
331 } |
|
332 return $this->migrations[$version]; |
|
333 } |
|
334 |
|
335 /** |
|
336 * Check if a version exists. |
|
337 * |
|
338 * @param string $version |
|
339 * @return bool $exists |
|
340 */ |
|
341 public function hasVersion($version) |
|
342 { |
|
343 return isset($this->migrations[$version]) ? true : false; |
|
344 } |
|
345 |
|
346 /** |
|
347 * Check if a version has been migrated or not yet |
|
348 * |
|
349 * @param Version $version |
|
350 * @return bool $migrated |
|
351 */ |
|
352 public function hasVersionMigrated(Version $version) |
|
353 { |
|
354 $this->createMigrationTable(); |
|
355 |
|
356 $version = $this->connection->fetchColumn("SELECT version FROM " . $this->migrationsTableName . " WHERE version = ?", array($version->getVersion())); |
|
357 return $version !== false ? true : false; |
|
358 } |
|
359 |
|
360 /** |
|
361 * Returns all migrated versions from the versions table, in an array. |
|
362 * |
|
363 * @return array $migrated |
|
364 */ |
|
365 public function getMigratedVersions() |
|
366 { |
|
367 $this->createMigrationTable(); |
|
368 |
|
369 $ret = $this->connection->fetchAll("SELECT version FROM " . $this->migrationsTableName); |
|
370 $versions = array(); |
|
371 foreach ($ret as $version) { |
|
372 $versions[] = current($version); |
|
373 } |
|
374 |
|
375 return $versions; |
|
376 } |
|
377 |
|
378 /** |
|
379 * Returns the current migrated version from the versions table. |
|
380 * |
|
381 * @return bool $currentVersion |
|
382 */ |
|
383 public function getCurrentVersion() |
|
384 { |
|
385 $this->createMigrationTable(); |
|
386 |
|
387 $sql = "SELECT version FROM " . $this->migrationsTableName . " ORDER BY version DESC"; |
|
388 $sql = $this->connection->getDatabasePlatform()->modifyLimitQuery($sql, 1); |
|
389 $result = $this->connection->fetchColumn($sql); |
|
390 return $result !== false ? (string) $result : '0'; |
|
391 } |
|
392 |
|
393 /** |
|
394 * Returns the total number of executed migration versions |
|
395 * |
|
396 * @return integer $count |
|
397 */ |
|
398 public function getNumberOfExecutedMigrations() |
|
399 { |
|
400 $this->createMigrationTable(); |
|
401 |
|
402 $result = $this->connection->fetchColumn("SELECT COUNT(version) FROM " . $this->migrationsTableName); |
|
403 return $result !== false ? $result : 0; |
|
404 } |
|
405 |
|
406 /** |
|
407 * Returns the total number of available migration versions |
|
408 * |
|
409 * @return integer $count |
|
410 */ |
|
411 public function getNumberOfAvailableMigrations() |
|
412 { |
|
413 return count($this->migrations); |
|
414 } |
|
415 |
|
416 /** |
|
417 * Returns the latest available migration version. |
|
418 * |
|
419 * @return string $version The version string in the format YYYYMMDDHHMMSS. |
|
420 */ |
|
421 public function getLatestVersion() |
|
422 { |
|
423 $versions = array_keys($this->migrations); |
|
424 $latest = end($versions); |
|
425 return $latest !== false ? (string) $latest : '0'; |
|
426 } |
|
427 |
|
428 /** |
|
429 * Create the migration table to track migrations with. |
|
430 * |
|
431 * @return bool $created Whether or not the table was created. |
|
432 */ |
|
433 public function createMigrationTable() |
|
434 { |
|
435 $this->validate(); |
|
436 |
|
437 if ($this->migrationTableCreated) { |
|
438 return false; |
|
439 } |
|
440 |
|
441 $schema = $this->connection->getSchemaManager()->createSchema(); |
|
442 if ( ! $schema->hasTable($this->migrationsTableName)) { |
|
443 $columns = array( |
|
444 'version' => new Column('version', Type::getType('string'), array('length' => 255)), |
|
445 ); |
|
446 $table = new Table($this->migrationsTableName, $columns); |
|
447 $table->setPrimaryKey(array('version')); |
|
448 $this->connection->getSchemaManager()->createTable($table); |
|
449 |
|
450 $this->migrationTableCreated = true; |
|
451 |
|
452 return true; |
|
453 } |
|
454 return false; |
|
455 } |
|
456 |
|
457 /** |
|
458 * Returns the array of migrations to executed based on the given direction |
|
459 * and target version number. |
|
460 * |
|
461 * @param string $direction The direction we are migrating. |
|
462 * @param string $to The version to migrate to. |
|
463 * @return array $migrations The array of migrations we can execute. |
|
464 */ |
|
465 public function getMigrationsToExecute($direction, $to) |
|
466 { |
|
467 if ($direction === 'down') { |
|
468 if (count($this->migrations)) { |
|
469 $allVersions = array_reverse(array_keys($this->migrations)); |
|
470 $classes = array_reverse(array_values($this->migrations)); |
|
471 $allVersions = array_combine($allVersions, $classes); |
|
472 } else { |
|
473 $allVersions = array(); |
|
474 } |
|
475 } else { |
|
476 $allVersions = $this->migrations; |
|
477 } |
|
478 $versions = array(); |
|
479 $migrated = $this->getMigratedVersions(); |
|
480 foreach ($allVersions as $version) { |
|
481 if ($this->shouldExecuteMigration($direction, $version, $to, $migrated)) { |
|
482 $versions[$version->getVersion()] = $version; |
|
483 } |
|
484 } |
|
485 return $versions; |
|
486 } |
|
487 |
|
488 /** |
|
489 * Check if we should execute a migration for a given direction and target |
|
490 * migration version. |
|
491 * |
|
492 * @param string $direction The direction we are migrating. |
|
493 * @param Version $version The Version instance to check. |
|
494 * @param string $to The version we are migrating to. |
|
495 * @param array $migrated Migrated versions array. |
|
496 * @return void |
|
497 */ |
|
498 private function shouldExecuteMigration($direction, Version $version, $to, $migrated) |
|
499 { |
|
500 if ($direction === 'down') { |
|
501 if ( ! in_array($version->getVersion(), $migrated)) { |
|
502 return false; |
|
503 } |
|
504 return $version->getVersion() > $to ? true : false; |
|
505 } else if ($direction === 'up') { |
|
506 if (in_array($version->getVersion(), $migrated)) { |
|
507 return false; |
|
508 } |
|
509 return $version->getVersion() <= $to ? true : false; |
|
510 } |
|
511 } |
|
512 } |