From 0f353c14110c1dc2383b231503cf3c933b846d55 Mon Sep 17 00:00:00 2001 From: "Jonathan H. Wage" Date: Tue, 22 Feb 2011 20:54:40 -0600 Subject: [PATCH 1/3] A command to generate a migration from the sql queries executed when you load some data fixtures. --- ...rationsGenerateFromDataFixturesCommand.php | 117 ++++++++++++++++++ .../FixturesToMigrationSQLLogger.php | 22 ++++ 2 files changed, 139 insertions(+) create mode 100644 src/Symfony/Bundle/DoctrineMigrationsBundle/Command/MigrationsGenerateFromDataFixturesCommand.php create mode 100644 src/Symfony/Bundle/DoctrineMigrationsBundle/SQLLogger/FixturesToMigrationSQLLogger.php diff --git a/src/Symfony/Bundle/DoctrineMigrationsBundle/Command/MigrationsGenerateFromDataFixturesCommand.php b/src/Symfony/Bundle/DoctrineMigrationsBundle/Command/MigrationsGenerateFromDataFixturesCommand.php new file mode 100644 index 0000000000000..5e9e31e267dec --- /dev/null +++ b/src/Symfony/Bundle/DoctrineMigrationsBundle/Command/MigrationsGenerateFromDataFixturesCommand.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\DoctrineMigrationsBundle\Command; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputOption; +use Doctrine\DBAL\Migrations\Tools\Console\Command\GenerateCommand; +use Symfony\Bundle\FrameworkBundle\Command\Command; +use Doctrine\Common\DataFixtures\Executor\ORMExecutor; +use Doctrine\Common\DataFixtures\Purger\ORMPurger; +use Symfony\Bundle\DoctrineAbstractBundle\Common\DataFixtures\Loader as DataFixturesLoader; +use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper; +use Doctrine\DBAL\Migrations\Configuration\Configuration; +use Symfony\Bundle\DoctrineMigrationsBundle\SQLLogger\FixturesToMigrationSQLLogger; + +/** + * Command for generating a Doctrine database migration class from a set of fixtures. + * + * @author Jonathan H. Wage + */ +class MigrationsGenerateFromDataFixturesCommand extends GenerateCommand +{ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:migrations:generate:from-data-fixtures') + ->addOption('fixtures', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The directory or file to load data fixtures from.') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command.') + ; + } + + public function execute(InputInterface $input, OutputInterface $output) + { + if ( ! class_exists('Doctrine\Common\DataFixtures\Loader')) { + throw new \Exception('You must have the Doctrine data fixtures extension installed in order to use this command.'); + } + + $sqlLogger = new FixturesToMigrationSQLLogger(); + + $container = $this->application->getKernel()->getContainer(); + + $emName = $input->getOption('em'); + $emName = $emName ? $emName : 'default'; + + DoctrineCommand::setApplicationEntityManager($this->application, $emName); + + $emServiceName = sprintf('doctrine.orm.%s_entity_manager', $emName); + $em = $container->get($emServiceName); + + $em->getConnection()->getConfiguration()->setSQLLogger($sqlLogger); + + $configuration = $this->getMigrationConfiguration($input, $output); + + $dirOrFile = $input->getOption('fixtures'); + if ($dirOrFile) { + $paths = is_array($dirOrFile) ? $dirOrFile : array($dirOrFile); + } else { + $paths = array(); + foreach ($this->application->getKernel()->getBundles() as $bundle) { + $paths[] = $bundle->getPath().'/DataFixtures/ORM'; + } + } + + $loader = new DataFixturesLoader($container); + foreach ($paths as $path) { + if (is_dir($path)) { + $loader->loadFromDirectory($path); + } + } + $fixtures = $loader->getFixtures(); + $purger = new ORMPurger($em); + $executor = new ORMExecutor($em, $purger); + $executor->setLogger(function($message) use ($output) { + $output->writeln(sprintf(' > %s', $message)); + }); + $executor->execute($fixtures); + + $queries = $sqlLogger->getQueries(); + + $output->writeln(sprintf(' > %s queries logged', count($queries))); + foreach ($queries as $query) { + $output->writeln(sprintf(' - %s (%s)', $query[0], implode(',', $query[1]))); + } + + $version = date('YmdHis'); + + $up = $this->buildCodeFromSql($configuration, $queries); + $down = 'throw new \Doctrine\DBAL\Migrations\IrreversibleMigrationException();'; + $path = $this->generateMigration($configuration, $input, $version, $up, $down); + + $output->writeln(sprintf(' > Generated new migration class to %s', $path)); + } + + private function buildCodeFromSql(Configuration $configuration, array $queries) + { + $code = array(); + foreach ($queries as $query) { + if (strpos($query[0], $configuration->getMigrationsTableName()) !== false) { + continue; + } + $code[] = sprintf("\$this->_addSql(\"%s\", %s);", $query[0], var_export($query[1], true)); + } + return implode("\n", $code); + } +} \ No newline at end of file diff --git a/src/Symfony/Bundle/DoctrineMigrationsBundle/SQLLogger/FixturesToMigrationSQLLogger.php b/src/Symfony/Bundle/DoctrineMigrationsBundle/SQLLogger/FixturesToMigrationSQLLogger.php new file mode 100644 index 0000000000000..01909ed25e85e --- /dev/null +++ b/src/Symfony/Bundle/DoctrineMigrationsBundle/SQLLogger/FixturesToMigrationSQLLogger.php @@ -0,0 +1,22 @@ +queries[] = array($sql, $params, $types); + } + + public function getQueries() + { + return $this->queries; + } + + public function stopQuery() + { + } +} \ No newline at end of file From 988b8f37360cf53bd87be66f3b4d7ca7dcb1bb66 Mon Sep 17 00:00:00 2001 From: Lukas Kahwe Smith Date: Thu, 24 Feb 2011 10:44:47 +0100 Subject: [PATCH 2/3] initial steps to handling objects --- ...rationsGenerateFromDataFixturesCommand.php | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Bundle/DoctrineMigrationsBundle/Command/MigrationsGenerateFromDataFixturesCommand.php b/src/Symfony/Bundle/DoctrineMigrationsBundle/Command/MigrationsGenerateFromDataFixturesCommand.php index 5e9e31e267dec..fd4b5b35a5913 100644 --- a/src/Symfony/Bundle/DoctrineMigrationsBundle/Command/MigrationsGenerateFromDataFixturesCommand.php +++ b/src/Symfony/Bundle/DoctrineMigrationsBundle/Command/MigrationsGenerateFromDataFixturesCommand.php @@ -22,6 +22,7 @@ use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper; use Doctrine\DBAL\Migrations\Configuration\Configuration; use Symfony\Bundle\DoctrineMigrationsBundle\SQLLogger\FixturesToMigrationSQLLogger; +use Symfony\Bundle\DoctrineMigrationsBundle\Command\DoctrineCommand; /** * Command for generating a Doctrine database migration class from a set of fixtures. @@ -56,13 +57,14 @@ public function execute(InputInterface $input, OutputInterface $output) DoctrineCommand::setApplicationEntityManager($this->application, $emName); + $configuration = $this->getMigrationConfiguration($input, $output); + DoctrineCommand::configureMigrations($this->application->getKernel()->getContainer(), $configuration); + $emServiceName = sprintf('doctrine.orm.%s_entity_manager', $emName); $em = $container->get($emServiceName); $em->getConnection()->getConfiguration()->setSQLLogger($sqlLogger); - $configuration = $this->getMigrationConfiguration($input, $output); - $dirOrFile = $input->getOption('fixtures'); if ($dirOrFile) { $paths = is_array($dirOrFile) ? $dirOrFile : array($dirOrFile); @@ -88,10 +90,23 @@ public function execute(InputInterface $input, OutputInterface $output) $executor->execute($fixtures); $queries = $sqlLogger->getQueries(); + foreach ($queries as $key => $query) { + foreach ($query[1] as $key2 => $param) { + if (is_object($param)) { + if ($param instanceOf \DateTime) { + $queries[$key][1][$key2] = $param->format('Y-m-d\TH:i:s\Z'); + } else if (in_array('__toString', get_class_methods($param))) { + $queries[$key][1][$key2] = (string)$param; + } else { + $output->writeln(sprintf(' > cannot convert object of type %s to a string', get_class($param))); + } + } + } + } $output->writeln(sprintf(' > %s queries logged', count($queries))); foreach ($queries as $query) { - $output->writeln(sprintf(' - %s (%s)', $query[0], implode(',', $query[1]))); + $output->writeln(sprintf(' - %s (parameters? %s)', $query[0], is_array($query[1]) ? 'yes' : 'no')); } $version = date('YmdHis'); @@ -110,8 +125,8 @@ private function buildCodeFromSql(Configuration $configuration, array $queries) if (strpos($query[0], $configuration->getMigrationsTableName()) !== false) { continue; } - $code[] = sprintf("\$this->_addSql(\"%s\", %s);", $query[0], var_export($query[1], true)); + $code[] = sprintf("\$this->addSql(\"%s\", %s);", $query[0], var_export($query[1], true)); } return implode("\n", $code); } -} \ No newline at end of file +} From e356c2a9025212d6d4af08d1fcd958ef785195d5 Mon Sep 17 00:00:00 2001 From: Lukas Kahwe Smith Date: Thu, 24 Feb 2011 11:57:32 +0100 Subject: [PATCH 3/3] cosmetic tweak --- .../Command/MigrationsGenerateFromDataFixturesCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/DoctrineMigrationsBundle/Command/MigrationsGenerateFromDataFixturesCommand.php b/src/Symfony/Bundle/DoctrineMigrationsBundle/Command/MigrationsGenerateFromDataFixturesCommand.php index fd4b5b35a5913..b0bce0d309ce4 100644 --- a/src/Symfony/Bundle/DoctrineMigrationsBundle/Command/MigrationsGenerateFromDataFixturesCommand.php +++ b/src/Symfony/Bundle/DoctrineMigrationsBundle/Command/MigrationsGenerateFromDataFixturesCommand.php @@ -95,7 +95,7 @@ public function execute(InputInterface $input, OutputInterface $output) if (is_object($param)) { if ($param instanceOf \DateTime) { $queries[$key][1][$key2] = $param->format('Y-m-d\TH:i:s\Z'); - } else if (in_array('__toString', get_class_methods($param))) { + } else if (method_exists($param, '__toString')) { $queries[$key][1][$key2] = (string)$param; } else { $output->writeln(sprintf(' > cannot convert object of type %s to a string', get_class($param)));