Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updating DoctrineMigrationsBundle for Doctrine Migrations 2.0 #228

Merged
merged 3 commits into from Jun 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
@@ -1,2 +1,3 @@
composer.lock
/vendor/
/.phpcs-cache

30 changes: 30 additions & 0 deletions .scrutinizer.yml
@@ -0,0 +1,30 @@
build:
nodes:
analysis:
environment:
php:
version: 7.1
cache:
disabled: false
directories:
- ~/.composer/cache
project_setup:
override: true
tests:
override:
- php-scrutinizer-run
- phpcs-run
dependencies:
override:
- composer install --ignore-platform-reqs --no-interaction

tools:
external_code_coverage:
timeout: 600

build_failure_conditions:
- 'elements.rating(<= C).new.exists' # No new classes/methods with a rating of C or worse allowed
- 'issues.label("coding-style").new.exists' # No new coding style issues allowed
- 'issues.severity(>= MAJOR).new.exists' # New issues of major or higher severity
- 'project.metric_change("scrutinizer.test_coverage", < 0)' # Code Coverage decreased from previous inspection

43 changes: 27 additions & 16 deletions .travis.yml
Expand Up @@ -3,42 +3,53 @@ sudo: false
language: php

php:
- 5.4
- 5.5
- 5.6
- 7.0
- 7.1
- 7.2
- nightly

cache:
directories:
- $HOME/.composer/cache
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should cache only $HOME/.composer/cache/files, to avoid invalidating the Travis cache due to changes in the packagist metadata (cached in $HOME/.composer/cache/repo). Packagist changes too often (every few minutes) to avoi this invalidation.


before_install:
- mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{,.disabled} || echo "xdebug not available"
- composer self-update
- travis_retry composer self-update

install: travis_retry composer update --prefer-dist
install:
- rm composer.lock
- travis_retry composer update --prefer-dist

script:
- ./vendor/bin/phpunit -v
- ./vendor/bin/phpunit

jobs:
allow_failures:
- php: nightly

include:
- stage: Test
env: DEPENDENCIES=low
install: travis_retry composer update --prefer-dist --prefer-lowest
install:
- rm composer.lock
- travis_retry composer update --prefer-dist --prefer-lowest

- stage: Coverage
- stage: Test
env: COVERAGE
before_script:
- mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{.disabled,}
- if [[ ! $(php -m | grep -si xdebug) ]]; then echo "xdebug required for coverage"; exit 1; fi
script:
- ./vendor/bin/phpunit -v --coverage-clover ./build/logs/clover.xml
- ./vendor/bin/phpunit --coverage-clover clover.xml
after_script:
- wget https://scrutinizer-ci.com/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml
- php ocular.phar code-coverage:upload --format=php-clover clover.xml

allow_failures:
- php: nightly
- stage: Code Quality
env: CODING_STANDARDS
install: travis_retry composer install --prefer-dist
script: ./vendor/bin/phpcs

cache:
directories:
- $HOME/.composer/cache
- stage: Code Quality
env: STATIC_ANALYSIS
install: travis_retry composer install --prefer-dist
script: vendor/bin/phpstan analyse -l 7 -c phpstan.neon Command Tests
91 changes: 66 additions & 25 deletions Command/DoctrineCommand.php
@@ -1,66 +1,106 @@
<?php

declare(strict_types=1);

namespace Doctrine\Bundle\MigrationsBundle\Command;

use Doctrine\Bundle\DoctrineBundle\Command\DoctrineCommand as BaseCommand;
use Doctrine\DBAL\Migrations\Configuration\AbstractFileConfiguration;
use Doctrine\DBAL\Migrations\Configuration\Configuration;
use Doctrine\Migrations\Configuration\AbstractFileConfiguration;
use Doctrine\Migrations\Configuration\Configuration;
use Doctrine\Migrations\Version\Version;
use ErrorException;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use function error_get_last;
use function is_dir;
use function method_exists;
use function mkdir;
use function preg_match;
use function sprintf;
use function str_replace;

/**
* Base class for Doctrine console commands to extend from.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class DoctrineCommand extends BaseCommand
{
public static function configureMigrations(ContainerInterface $container, Configuration $configuration)
public static function configureMigrations(ContainerInterface $container, Configuration $configuration) : void
{
if (!$configuration->getMigrationsDirectory()) {
$dir = $configuration->getMigrationsDirectory();

if (empty($dir)) {
$dir = $container->getParameter('doctrine_migrations.dir_name');
if (!is_dir($dir) && !@mkdir($dir, 0777, true) && !is_dir($dir)) {

if (! is_dir($dir) && ! @mkdir($dir, 0777, true) && ! is_dir($dir)) {
$error = error_get_last();
throw new \ErrorException($error['message']);

throw new ErrorException(sprintf(
'Failed to create directory "%s" with message "%s"',
$dir,
$error['message']
));
}

$configuration->setMigrationsDirectory($dir);
} else {
$dir = $configuration->getMigrationsDirectory();
// class Kernel has method getKernelParameters with some of the important path parameters
$pathPlaceholderArray = array('kernel.root_dir', 'kernel.cache_dir', 'kernel.logs_dir');
$pathPlaceholderArray = ['kernel.root_dir', 'kernel.cache_dir', 'kernel.logs_dir'];

foreach ($pathPlaceholderArray as $pathPlaceholder) {
if ($container->hasParameter($pathPlaceholder) && preg_match('/\%'.$pathPlaceholder.'\%/', $dir)) {
$dir = str_replace('%'.$pathPlaceholder.'%', $container->getParameter($pathPlaceholder), $dir);
if (! $container->hasParameter($pathPlaceholder) || ! preg_match('/\%' . $pathPlaceholder . '\%/', $dir)) {
continue;
}

$dir = str_replace('%' . $pathPlaceholder . '%', $container->getParameter($pathPlaceholder), $dir);
}
if (!is_dir($dir) && !@mkdir($dir, 0777, true) && !is_dir($dir)) {

if (! is_dir($dir) && ! @mkdir($dir, 0777, true) && ! is_dir($dir)) {
$error = error_get_last();
throw new \ErrorException($error['message']);

throw new ErrorException(sprintf(
'Failed to create directory "%s" with message "%s"',
$dir,
$error['message']
));
}

$configuration->setMigrationsDirectory($dir);
}
if (!$configuration->getMigrationsNamespace()) {

if (empty($configuration->getMigrationsNamespace())) {
$configuration->setMigrationsNamespace($container->getParameter('doctrine_migrations.namespace'));
}
if (!$configuration->getName()) {

if (empty($configuration->getName())) {
$configuration->setName($container->getParameter('doctrine_migrations.name'));
}

// For backward compatibility, need use a table from parameters for overwrite the default configuration
if (!($configuration instanceof AbstractFileConfiguration) || !$configuration->getMigrationsTableName()) {
if (! ($configuration instanceof AbstractFileConfiguration) || empty($configuration->getMigrationsTableName())) {
$configuration->setMigrationsTableName($container->getParameter('doctrine_migrations.table_name'));
}

$configuration->setMigrationsColumnName($container->getParameter('doctrine_migrations.column_name'));
$configuration->setMigrationsColumnLength($container->getParameter('doctrine_migrations.column_length'));
$configuration->setMigrationsExecutedAtColumnName($container->getParameter('doctrine_migrations.executed_at_column_name'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these ones should check for an explicit config file, as done for the table name

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure I understand what you mean. You mean how the others have an if (empty(...)) condition around them? These values in the Configuration class have a default value so it can't work the same way unless you have another thought/idea?

$configuration->setAllOrNothing($container->getParameter('doctrine_migrations.all_or_nothing'));

// Migrations is not register from configuration loader
if (!($configuration instanceof AbstractFileConfiguration)) {
$configuration->registerMigrationsFromDirectory($configuration->getMigrationsDirectory());
if (! ($configuration instanceof AbstractFileConfiguration)) {
$migrationsDirectory = $configuration->getMigrationsDirectory();

if ($migrationsDirectory !== null) {
$configuration->registerMigrationsFromDirectory($migrationsDirectory);
}
}

if (method_exists($configuration, 'getCustomTemplate') && !$configuration->getCustomTemplate()) {
if (method_exists($configuration, 'getCustomTemplate') && empty($configuration->getCustomTemplate())) {
$configuration->setCustomTemplate($container->getParameter('doctrine_migrations.custom_template'));
}

$organizeMigrations = $container->getParameter('doctrine_migrations.organize_migrations');

switch ($organizeMigrations) {
case Configuration::VERSIONS_ORGANIZATION_BY_YEAR:
$configuration->setMigrationsAreOrganizedByYear(true);
Expand All @@ -81,18 +121,19 @@ public static function configureMigrations(ContainerInterface $container, Config
}

/**
* @param ContainerInterface $container
* @param array $versions
* @param Version[] $versions
*
* Injects the container to migrations aware of it
*/
private static function injectContainerToMigrations(ContainerInterface $container, array $versions)
private static function injectContainerToMigrations(ContainerInterface $container, array $versions) : void
{
foreach ($versions as $version) {
$migration = $version->getMigration();
if ($migration instanceof ContainerAwareInterface) {
$migration->setContainer($container);
if (! ($migration instanceof ContainerAwareInterface)) {
continue;
}

$migration->setContainer($container);
}
}
}
48 changes: 34 additions & 14 deletions Command/Helper/DoctrineCommandHelper.php
@@ -1,43 +1,63 @@
<?php

declare(strict_types=1);

namespace Doctrine\Bundle\MigrationsBundle\Command\Helper;

use Doctrine\Bundle\DoctrineBundle\Command\Proxy\DoctrineCommandHelper as BaseDoctrineCommandHelper;
use Doctrine\Bundle\DoctrineBundle\Registry;
use Doctrine\DBAL\Sharding\PoolingShardConnection;
use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper;
use LogicException;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\InputInterface;
use function count;
use function sprintf;

/**
* Provides some helper and convenience methods to configure doctrine commands in the context of bundles
* and multiple connections/entity managers.
*
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
*/
abstract class DoctrineCommandHelper extends BaseDoctrineCommandHelper
{
public static function setApplicationHelper(Application $application, InputInterface $input)
public static function setApplicationHelper(Application $application, InputInterface $input) : void
{
$container = $application->getKernel()->getContainer();
$doctrine = $container->get('doctrine');

/** @var Registry $doctrine */
$doctrine = $container->get('doctrine');

$managerNames = $doctrine->getManagerNames();

if ($input->getOption('db') || empty($managerNames)) {
if ($input->getOption('db') !== null || count($managerNames) === 0) {
self::setApplicationConnection($application, $input->getOption('db'));
} else {
self::setApplicationEntityManager($application, $input->getOption('em'));
}

if ($input->getOption('shard')) {
$connection = $application->getHelperSet()->get('db')->getConnection();
if (!$connection instanceof PoolingShardConnection) {
if (empty($managerNames)) {
throw new \LogicException(sprintf("Connection '%s' must implement shards configuration.", $input->getOption('db')));
} else {
throw new \LogicException(sprintf("Connection of EntityManager '%s' must implement shards configuration.", $input->getOption('em')));
}
if ($input->getOption('shard') === null) {
return;
}

/** @var ConnectionHelper $dbHelper */
$dbHelper = $application->getHelperSet()->get('db');

$connection = $dbHelper->getConnection();

if (! $connection instanceof PoolingShardConnection) {
if (count($managerNames) === 0) {
throw new LogicException(sprintf(
"Connection '%s' must implement shards configuration.",
$input->getOption('db')
));
}

$connection->connect($input->getOption('shard'));
throw new LogicException(sprintf(
"Connection of EntityManager '%s' must implement shards configuration.",
$input->getOption('em')
));
}

$connection->connect($input->getOption('shard'));
}
}
29 changes: 21 additions & 8 deletions Command/MigrationsDiffDoctrineCommand.php
@@ -1,23 +1,23 @@
<?php

declare(strict_types=1);

namespace Doctrine\Bundle\MigrationsBundle\Command;

use Doctrine\DBAL\Migrations\Tools\Console\Command\DiffCommand;
use Doctrine\Migrations\Tools\Console\Command\DiffCommand;
use InvalidArgumentException;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

/**
* Command for generate migration classes by comparing your current database schema
* to your mapping information.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
class MigrationsDiffDoctrineCommand extends DiffCommand
{
protected function configure()
protected function configure() : void
{
parent::configure();

Expand All @@ -29,12 +29,25 @@ protected function configure()
;
}

public function execute(InputInterface $input, OutputInterface $output)
public function initialize(InputInterface $input, OutputInterface $output) : void
{
Helper\DoctrineCommandHelper::setApplicationHelper($this->getApplication(), $input);
/** @var Application $application */
$application = $this->getApplication();

Helper\DoctrineCommandHelper::setApplicationHelper($application, $input);

$configuration = $this->getMigrationConfiguration($input, $output);
DoctrineCommand::configureMigrations($this->getApplication()->getKernel()->getContainer(), $configuration);
DoctrineCommand::configureMigrations($application->getKernel()->getContainer(), $configuration);

parent::initialize($input, $output);
}

public function execute(InputInterface $input, OutputInterface $output) : ?int
{
// EM and DB options cannot be set at same time
if ($input->getOption('em') !== null && $input->getOption('db') !== null) {
throw new InvalidArgumentException('Cannot set both "em" and "db" for command execution.');
}

return parent::execute($input, $output);
}
Expand Down