cakephp / cakephp/phinx

Dependency injection into migrations

Open
#2,183 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
PHP
Stars
4.5k
Forks
884
PR merge metrics
No merged PRs in 30d

Description

Phinx has a serious design issue IMHO.

As far as I can tell, there is no way to inject anything to migrations. At least there is a way to do so with the seeders (using the undocumented 'container' option), which would not be the best solution for the migrations, but would do the job.
Obviously, defining dependencies for every migration would be tedious, unless something with magic (like PHP-DI) is used to construct them.
And there would be the problem of inlining the dynamic arguments ($migration = new $class($environment, $version, $this->getInput(), $this->getOutput());).

Currently, I am able to set my base migration class, which might have the option setManager which would allow to call something like $this->getManager()->getContainer() inside the migrations and to fetch my dependencies from there.
But I am not able to cofigure it in any way, so I can't call the fictional setManager method.

Suggestions

I suggest the following solutions

  1. create an interface allowing us to decorate the migration classes
  2. allow us to set a migration factory (for seeders as well?)
  3. introduce hooks to call after a migration has been instantiated enabling decoration (for seeders as well?)
1. Interface

A simple interface like

interface ManagerAwareMigrationInterface {
  public function setManager(Manager $manager);
}

would do the job.

Inside Manager class, right after instantiating a migration, add this code

if ($migration instanceof ManagerAwareMigrationInterface ){
  $migration->setManager($this);
}

Add Manager::getContainer public method to enable access to the container from within the migrations.

Or do something similar directly using the container

if (if $this->container && $migration instanceof ContainerAwareMigrationInterface ){
  $migration->setContainer($this->container);
}
2. Migration factory
$phinxOptions = [
  'migration_factory' => $container->get(MyMigrationFactory::class),
];

as the possible values, use a generic callable with signature same as the AbstractMigration's constructor (plus the class name) or an interface with a method with the same signature...

// a generic callable
$factory = fn(string $className, string $environment, int $version, ?InputInterface $input = null, ?OutputInterface $output = null): AbstractMigration => WhateverHere();

// an interface
interface MigrationFactory{
  public function createMigration(string $className, string $environment, int $version, ?InputInterface $input = null, ?OutputInterface $output = null): AbstractMigration;
}
3. Decoration hook
$phinxOptions = [
  'migration_decorator' => $container->get(MyMigrationFactory::class)->makeDecorator(),
];
// Manager.php
$migration = new $class($environment, $version, $this->getInput(), $this->getOutput());
if ($this->migrationFactoryHook){
  $migration = ($this->migrationFactoryHook)($migration) ?? $migration;
}

Conclusion

All the above options are simple to implement and will do the job.
I could provide a PR if you wanted me to, but let me know which approach to take.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in Manager.php at the migration instantiation point shown in the issue, and review how migration options are currently passed. Compare the proposed interface, factory, and decoration-hook approaches before choosing one with maintainers. Done means a supported dependency-injection mechanism is implemented for migrations, with its configuration and behavior documented and tested.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
backend, databases
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.