phinx
phinx copied to clipboard
Conditional migration
Hello, is it possible to conditionally execute a migration?
I'm working on a software that communicates with a few databases of different types. Depending on its type, (e.g. X or Y) a migration should or should not be executed Example:
| DB type | Migration 1 | Migration 2 | Migration 3 |
|---|---|---|---|
| X | ☑ | ☑ | |
| Y | ☑ | ☑ |
I'm currently doing it this way:
<?php
class Migration2 extends AbstractConditionalMigration
{
public function change()
{
if ($this->shouldExecute()) {
// migration script
}
}
public function isTypeX(): bool
{
return false;
}
public function isTypeY(): bool
{
return true;
}
}
where "shouldExecute" is a method implemented by AbstractConditionalMigration that verifies the type of the target environment and, based on the returned value of isTypeX() and isTypeY(), whether the migration should be executed.
The AbstractConditionalMigration class:
use Phinx\Migration\AbstractMigration;
abstract class AbstractConditionalMigration extends AbstractMigration
{
public abstract function isTypeX(): bool;
public abstract function isTypeY(): bool;
protected function shouldExecute(): bool
{
$env = $this->getEnvironment();
$shouldExecute = false;
if ($this->isTypeX() && $this->isEnvTypeX($env)) {
$shouldExecute = true;
}
if ($this->isTypeY() && $this->isEnvTypeY($env)) {
$shouldExecute = true;
}
return $shouldExecute;
}
// ...
}
The thing is: for every migration class, I have to copy and paste that condition. Also, it will generate a row for that migration in the database, even though it was not supposed to be executed.
Just noticed that it may be related to https://github.com/cakephp/phinx/issues/1044.
In general I like the idea, as sometimes you are otherwise used to use inline if/else switches etc.
You're almost there.
In your phinx.php config file (or yaml or whatever), change
'migration_base_class' => AbstractMigration::class,
to
'migration_base_class' => AbstractConditionalMigration::class,
Then copy the standard template from src/Phinx/Migration/Migration.template.php.dist to somewhere in your codebase.
Change the phinx.php config to include:
'templates' => [
'file' => './lib/Migration/Phinx/Templates/migration.template',
],
And then adjust that template to include your calls to the AbstractConditionalMigration class.
You're almost there.
In your phinx.php config file (or yaml or whatever), change
'migration_base_class' => AbstractMigration::class,to'migration_base_class' => AbstractConditionalMigration::class,Then copy the standard template from src/Phinx/Migration/Migration.template.php.dist to somewhere in your codebase.
Change the phinx.php config to include:
'templates' => [ 'file' => './lib/Migration/Phinx/Templates/migration.template', ],And then adjust that template to include your calls to the AbstractConditionalMigration class.
Thanks! I've actually done that, but I just think it's a little verbose.
In general I like the idea, as sometimes you are otherwise used to use inline if/else switches etc.
I'll see if I can do something about it as soon as I have some spare time
The "verbose" ness is that you are in control of the template and the code that implements your desired behaviour of having conditionality in your migrations.
And other than this extra setup, you've already done the work and ALL your migrations now support conditionality.
You only need to add a method to your concrete migration if you wish to override the default behaviour (which is either going to be 'execute everywhere' or 'execute nowhere').
If you wanted to make sure the migration wasn't part of the phinxlog for a particular environment, then you'd need a way to reject the migration from the list of migrations, so if you were to look at those that had been executed for DBs 2 and 3 (in your original example), you'd only see 1 migration.
To achieve that, there would need to be some work on the preFlightCheck() method of the migration to determine if the migration should go ahead. Currently, there is no response for pre and post checks and so you cannot alter the flow at this stage, but it may be a more appropriate place to do the check, rather than in the up/down/change methods.