<?php
declare(strict_types=1);
/**
* Pimcore
*
* This source file is available under two different licenses:
* - GNU General Public License version 3 (GPLv3)
* - Pimcore Enterprise License (PEL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
* @license http://www.pimcore.org/license GPLv3 and PEL
*/
namespace Pimcore\Migrations\Configuration;
use Doctrine\DBAL\Migrations\OutputWriter;
use Pimcore\Db\ConnectionInterface;
use Pimcore\Event\TestEvents;
use Pimcore\Extension\Bundle\Installer\MigrationInstallerInterface;
use Pimcore\Extension\Bundle\PimcoreBundleInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
class ConfigurationFactory implements EventSubscriberInterface
{
/**
* @var ContainerInterface
*/
private $container;
/**
* @var MigrationSetConfiguration[]
*/
private $migrationSets = [];
/**
* @var Configuration[]
*/
private $configurations = [];
/**
* @var InstallConfiguration[]
*/
private $installConfigurations = [];
public function __construct(ContainerInterface $container, array $migrationSetConfigurations = [])
{
$this->container = $container;
$this->buildMigrationSets($migrationSetConfigurations);
}
public static function getSubscribedEvents()
{
// reset configurations when test kernel boots
return [
TestEvents::KERNEL_BOOTED => 'reset'
];
}
private function buildMigrationSets(array $configurations)
{
foreach ($configurations as $configuration) {
$this->registerMigrationSet(MigrationSetConfiguration::fromConfig($configuration));
}
}
public function getForSet(
string $set,
ConnectionInterface $connection,
OutputWriter $outputWriter = null
) {
$migrationSet = $this->getMigrationSet($set);
$configuration = $this->getConfiguration($migrationSet, $connection, $outputWriter);
$this->injectContainerToVersions($configuration);
return $configuration;
}
public function getForBundle(
BundleInterface $bundle,
ConnectionInterface $connection,
OutputWriter $outputWriter = null
): Configuration {
$migrationSet = $this->getMigrationSetForBundle($bundle);
$configuration = $this->getConfiguration($migrationSet, $connection, $outputWriter);
if ($bundle instanceof PimcoreBundleInterface) {
$installer = $bundle->getInstaller();
if (null !== $installer && $installer instanceof MigrationInstallerInterface) {
$configuration->setInstaller($installer);
}
}
$this->injectContainerToVersions($configuration);
return $configuration;
}
protected function getConfiguration(
MigrationSetConfiguration $migrationSet,
ConnectionInterface $connection,
OutputWriter $outputWriter = null
): Configuration {
if (isset($this->configurations[$migrationSet->getIdentifier()])) {
return $this->configurations[$migrationSet->getIdentifier()];
}
// fetch custom connection if migration set defines a dedicated one
if (null !== $migrationSet->getConnection()) {
$connection = $this->container->get(sprintf(
'doctrine.dbal.%s_connection',
$migrationSet->getConnection()
));
}
$configuration = new Configuration(
$migrationSet->getIdentifier(),
$connection,
$outputWriter
);
$this->configureConfiguration($configuration, $migrationSet);
$this->configurations[$migrationSet->getIdentifier()] = $configuration;
return $configuration;
}
/**
* Creates a dedicated install configuration from an existing configuration
*
* @param Configuration $configuration
* @param MigrationInstallerInterface $installer
*
* @return InstallConfiguration
*/
public function getInstallConfiguration(
Configuration $configuration,
MigrationInstallerInterface $installer
): InstallConfiguration {
$migrationSetId = $configuration->getMigrationSet();
if (isset($this->installConfigurations[$migrationSetId])) {
return $this->installConfigurations[$migrationSetId];
}
$migrationSet = $this->getMigrationSet($migrationSetId);
// pipe messages to original config output writer
$outputWriter = new OutputWriter(function ($message) use ($configuration) {
$configuration->getOutputWriter()->write($message);
});
$installConfiguration = new InstallConfiguration(
$installer,
$configuration->getMigrationSet(),
$configuration->getConnection(),
$outputWriter
);
$this->configureConfiguration($installConfiguration, $migrationSet);
$this->installConfigurations[$migrationSetId] = $installConfiguration;
return $installConfiguration;
}
/**
* Reset all registered configurations. Can be necessary during tests when rebuilding the DB.
*/
public function reset()
{
foreach ($this->configurations as $configuration) {
$configuration->reset();
}
foreach ($this->installConfigurations as $installConfiguration) {
$installConfiguration->reset();
}
}
/**
* Applies migration set configuration to configuration instance
*
* @param Configuration $configuration
* @param MigrationSetConfiguration $migrationSet
*/
protected function configureConfiguration(Configuration $configuration, MigrationSetConfiguration $migrationSet)
{
$configuration->setName($migrationSet->getName());
$configuration->setMigrationsNamespace($migrationSet->getNamespace());
$configuration->setMigrationsDirectory($migrationSet->getDirectory());
}
protected function injectContainerToVersions(Configuration $configuration)
{
$versions = $configuration->getMigrations();
foreach ($versions as $version) {
$migration = $version->getMigration();
if ($migration instanceof ContainerAwareInterface) {
$migration->setContainer($this->container);
}
}
}
private function getMigrationSetForBundle(BundleInterface $bundle): MigrationSetConfiguration
{
if (!isset($this->migrationSets[$bundle->getName()])) {
$this->registerMigrationSet($this->buildBundleMigrationSet($bundle));
}
return $this->migrationSets[$bundle->getName()];
}
protected function buildBundleMigrationSet(BundleInterface $bundle)
{
return new MigrationSetConfiguration(
$bundle->getName(),
$bundle->getName() . ' Migrations',
$bundle->getNamespace() . '\\Migrations',
$bundle->getPath() . '/Migrations'
);
}
private function registerMigrationSet(MigrationSetConfiguration $migrationSet)
{
if (isset($this->migrationSets[$migrationSet->getIdentifier()])) {
throw new \RuntimeException(sprintf('Migration set "%s" is already registered.', $migrationSet->getIdentifier()));
}
$this->migrationSets[$migrationSet->getIdentifier()] = $migrationSet;
}
protected function getMigrationSet(string $set): MigrationSetConfiguration
{
if (!isset($this->migrationSets[$set])) {
throw new \InvalidArgumentException(sprintf('Migration set "%s" is not registered.', $set));
}
return $this->migrationSets[$set];
}
}