|
| 1 | +--- |
| 2 | +title: "Migrations" |
| 3 | +description: "Database migrations and general purpose migration scripts." |
| 4 | +--- |
| 5 | + |
| 6 | +{{% notice note %}} |
| 7 | +This covers the documentation on how to create migrations in Contao **4.9** |
| 8 | +and up. In previous Contao versions, migrations were written in `runonce.php` files that got deleted after execution. |
| 9 | +{{% /notice %}} |
| 10 | + |
| 11 | +Updating Contao, extensions or the application itself sometimes requires to migrate data to be compatible with the new version(s). For this purpose Contao has a migration framework that lets you write migration services that are integrated in the update process. |
| 12 | + |
| 13 | +The migrations get executed via the install tool database update or with the [`contao:migrate` command][commands]. |
| 14 | + |
| 15 | + |
| 16 | +## Definition |
| 17 | + |
| 18 | +To add a new migration, create a service that implements the interface `Contao\CoreBundle\Migration\MigrationInterface` and add the tag `contao.migration` (if autoconfiguration is enabled, this happens automatically): |
| 19 | + |
| 20 | +```yaml |
| 21 | +# config/services.yaml |
| 22 | +services: |
| 23 | + App\Migration\MyMigration: |
| 24 | + tags: |
| 25 | + - { name: contao.migration, priority: 0 } |
| 26 | +``` |
| 27 | +
|
| 28 | +## MigrationInterface |
| 29 | +
|
| 30 | +The migration interface specifies three methods that need to be implemented: |
| 31 | +
|
| 32 | +* __getName()__<br> |
| 33 | + A name that describes what the migration does. This text is shown to users when they are asked if they want to execute the migration. |
| 34 | +
|
| 35 | +* __shouldRun()__<br> |
| 36 | + This method checks if all prerequisites that are needed for the migration to run are met and if it actually needs to run. This method should be written very defensively because the application might be in an unexpected state when the method gets called, e.g. the database could be completely empty. |
| 37 | +
|
| 38 | +* __run()__<br> |
| 39 | + As the name suggests, that is where the real magic happens. If `shouldRun()` returned `true`, this method will be called and should do the actual migration.<br> |
| 40 | + It returns a `MigrationResult` object that can hold more information about what happened during the execution and if the migration was successful or not.<br> |
| 41 | + If something goes unexpectedly wrong here and you want to abort the migration process completeley you should throw an exception here. |
| 42 | + |
| 43 | + |
| 44 | +## Example |
| 45 | + |
| 46 | +Lets say we have a database table `tl_customers` with a `firstName` and `lastName` column that we combined to a `name` column in the new version: |
| 47 | + |
| 48 | +```php |
| 49 | +// src/Migration/CustomerNameMigration.php |
| 50 | +namespace App\Migration; |
| 51 | +
|
| 52 | +use Contao\CoreBundle\Migration\AbstractMigration; |
| 53 | +use Contao\CoreBundle\Migration\MigrationResult; |
| 54 | +use Doctrine\DBAL\Connection; |
| 55 | +
|
| 56 | +class CustomerNameMigration extends AbstractMigration |
| 57 | +{ |
| 58 | + /** |
| 59 | + * @var Connection |
| 60 | + */ |
| 61 | + private $connection; |
| 62 | +
|
| 63 | + public function __construct(Connection $connection) |
| 64 | + { |
| 65 | + $this->connection = $connection; |
| 66 | + } |
| 67 | +
|
| 68 | + public function shouldRun(): bool |
| 69 | + { |
| 70 | + $schemaManager = $this->connection->getSchemaManager(); |
| 71 | +
|
| 72 | + // If the database table itself does not exist we should do nothing |
| 73 | + if (!$schemaManager->tablesExist(['tl_customers'])) { |
| 74 | + return false; |
| 75 | + } |
| 76 | +
|
| 77 | + $columns = $schemaManager->listTableColumns('tl_customers'); |
| 78 | +
|
| 79 | + return |
| 80 | + isset($columns['firstName']) && |
| 81 | + isset($columns['lastName']) && |
| 82 | + !isset($columns['name']); |
| 83 | + } |
| 84 | +
|
| 85 | + public function run(): MigrationResult |
| 86 | + { |
| 87 | + $this->connection->query(' |
| 88 | + ALTER TABLE |
| 89 | + tl_customers |
| 90 | + ADD |
| 91 | + name varchar(255) NOT NULL DEFAULT '', |
| 92 | + '); |
| 93 | +
|
| 94 | + $stmt = $this->connection->prepare(' |
| 95 | + UPDATE |
| 96 | + tl_customers |
| 97 | + SET |
| 98 | + name = CONCAT(firstName, ' ', lastName) |
| 99 | + '); |
| 100 | +
|
| 101 | + $stmt->execute(); |
| 102 | +
|
| 103 | + return new MigrationResult( |
| 104 | + true, |
| 105 | + 'Combined '. $stmt->rowCount().' customer names.' |
| 106 | + ); |
| 107 | + } |
| 108 | +} |
| 109 | +``` |
| 110 | + |
| 111 | + |
| 112 | +## Read more |
| 113 | + |
| 114 | +* [Contao's console commands][commands] |
| 115 | + |
| 116 | + |
| 117 | +[commands]: /reference/commands/ |
0 commit comments