Reputation: 1
I'm currently facing an issue in migration with doctrine and my multiple database (db_name)
I have inside the same MYSQL instance multiple database, one for the company and the other for each Client (Entity).
Each database is created inside the postPersit operation (i'm running a custom command that override the database:create of doctrine to create a database with the uuid of the client, this is working).
I want to do the same with the migration but I'm facing an error "config not found" and when I put the general configuration, it do not found what it needs.
I created a WrapperConnection and added the possibility to select the database. I'm using this wrapper before running an API platform request (inside an ApiNormalizer).
My code ...
#[AsCommand(name: 'custom:migration:migrate')]
final class MigrationCommand extends DoctrineCommand
{
use EntityManagerTrait;
use ParameterBagTrait;
protected function configure(): void
{
$this
->setAliases(['migrate'])
->setDescription(
'Execute a migration to a specified version or the latest available version.',
)
->addArgument(
'name',
InputArgument::REQUIRED,
'The database name'
)
;
parent::configure();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$databaseName = $input->getArgument('name');
if (null === $databaseName) {
// return Command::FAILURE;
}
/** @var MultiDbConnectionWrapper */
$connection = $this->em->getConnection();
$connection->selectDatabase($databaseName);
// Doctrine code is below.
$migrateCmd = new DoctrineMigrateCommand($this->getDependencyFactory());
$migrateInput = new ArrayInput([
'--configuration' => $this->parameterBag->get('kernel.project_dir').'/config/packages/doctrine.yaml',
]);
$migrateCmd->run($migrateInput, $output);
}
}
I tried to copy/past directly the code of the class to edit it... I failed ...
I want to use the migration on dynamic schema but I'm new on doctrine code and migration. Can u help me ?
If I knew how the config is working, I could have created copies of the migration files with the good schema I think and run it ... what do you think ?
Sorry for my approximative english.
Upvotes: 0
Views: 119
Reputation: 1
I found it.
Here is the solution
<?php
namespace App\Command\Doctrine;
use App\Service\Trait\ParameterBagTrait;
use Doctrine\Migrations\Tools\Console\Command\MigrateCommand as DoctrineMigrateCommand;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Yaml\Yaml;
// the name of the command is what users type after "php bin/console"
#[AsCommand(name: 'alx:migration:migrate', aliases: ['alx:mi:mi', 'alx-migrate'])]
final class MigrationCommand extends Command
{
use ParameterBagTrait;
protected function configure(): void
{
$this
->setAliases(['migrate'])
->setDescription(
'Execute a migration on a specific database',
)
->addArgument(
'name',
InputArgument::REQUIRED,
'The database name'
)
;
parent::configure();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$databaseName = $input->getArgument('name');
if (null === $databaseName) {
return Command::FAILURE;
}
// Create the tmp file.
$dbPath = $this->getDatabaseConfigPath($databaseName);
$migrationPath = $this->getMigrationConfigPath();
$migrateCmd = new DoctrineMigrateCommand();
$migrateInput = new ArrayInput([
'--db-configuration' => $dbPath,
'--configuration' => $migrationPath,
]);
// I dont want it to ask the question.
$migrateInput->setInteractive(false);
// execute the migration.
$migrateCmd->run($migrateInput, $output);
// Delete the tmp file.
unlink($dbPath);
unlink($migrationPath);
return 0;
}
public function getDatabaseConfigPath(string $dbName): string
{
$urlDb = $this->parameterBag->get('database_url');
$url = str_replace('bastion', $dbName, $urlDb);
$phpContent = "<?php return ['url' => '$url']; ?>";
return $this->createTmpFile('db_'.$dbName.'_'.uniqid().'.php', $phpContent);
}
public function getMigrationConfigPath(): string
{
/** @var string $directory */
$directory = $this->parameterBag->get('kernel.project_dir');
$migrationPath = $directory.'/migrations';
$content = Yaml::dump([
'migrations_paths' => [
'DoctrineMigrations' => $migrationPath,
],
'table_storage' => [
'table_name' => 'doctrine_migration_versions',
'version_column_name' => 'version',
'version_column_length' => '191',
],
]);
$name = uniqid('migration_');
return $this->createTmpFile($name.'.yaml', $content);
}
private function createTmpFile(string $filename, string $content): string
{
$filesystem = new Filesystem();
$tempFilePath = sys_get_temp_dir().'/'.$filename;
$filesystem->dumpFile($tempFilePath, $content);
return $tempFilePath;
}
}
the db file
<?php
return [
'url' => 'mysql://USER:PWD@database:3306/myDatabaseName?serverVersion=10.5.15-MariaDB&charset=utf8mb4'
];
The migration file (yaml)
migrations_paths:
# namespace is arbitrary but should be different from App\Migrations
# as migrations classes should NOT be autoloaded
'DoctrineMigrations': '/app/migrations'
table_storage:
table_name: 'doctrine_migration_versions'
version_column_name: 'version'
version_column_length: '191'
Upvotes: 0