[DoctrineBundle] Enhancing the Doctrine 2 integration further to better handle multiple connections/entity managers
This commit is contained in:
parent
b3d8aa414e
commit
1a45bb6d63
@ -0,0 +1,139 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Symfony\Framework\DoctrineBundle\Command;
|
||||||
|
|
||||||
|
use Symfony\Components\Console\Input\InputArgument;
|
||||||
|
use Symfony\Components\Console\Input\InputOption;
|
||||||
|
use Symfony\Components\Console\Input\InputInterface;
|
||||||
|
use Symfony\Components\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Components\Console\Output\Output;
|
||||||
|
use Symfony\Framework\WebBundle\Util\Filesystem;
|
||||||
|
use Doctrine\Common\Cli\Configuration;
|
||||||
|
use Doctrine\Common\Cli\CliController as DoctrineCliController;
|
||||||
|
use Doctrine\DBAL\Connection;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the symfony framework.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build command allows you to easily build and re-build your Doctrine development environment
|
||||||
|
*
|
||||||
|
* @package symfony
|
||||||
|
* @subpackage console
|
||||||
|
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||||
|
* @author Jonathan H. Wage <jonwage@gmail.com>
|
||||||
|
* @author Kris Wallsmith <kris.wallsmith@symfony-project.org>
|
||||||
|
*/
|
||||||
|
class BuildDoctrineCommand extends DoctrineCommand
|
||||||
|
{
|
||||||
|
const
|
||||||
|
BUILD_ENTITIES = 1,
|
||||||
|
BUILD_DB = 16,
|
||||||
|
|
||||||
|
OPTION_ENTITIES = 1,
|
||||||
|
OPTION_DB = 16,
|
||||||
|
OPTION_ALL = 31;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Command
|
||||||
|
*/
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->setName('doctrine:build')
|
||||||
|
->setDescription('Build task for easily re-building your Doctrine development environment.')
|
||||||
|
->addOption('all', null, null, 'Build everything and reset the database')
|
||||||
|
->addOption('entities', null, null, 'Build model classes')
|
||||||
|
->addOption('db', null, null, 'Drop database, create database and create schema.')
|
||||||
|
->addOption('and-load', null, InputOption::PARAMETER_OPTIONAL | InputOption::PARAMETER_IS_ARRAY, 'Load data fixtures')
|
||||||
|
->addOption('and-append', null, InputOption::PARAMETER_OPTIONAL | InputOption::PARAMETER_IS_ARRAY, 'Load data fixtures and append to existing data')
|
||||||
|
->addOption('and-update-schema', null, null, 'Update schema after rebuilding all classes')
|
||||||
|
->addOption('connection', null, null, 'The connection to use.')
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Command
|
||||||
|
*/
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
if (!$mode = $this->calculateMode($input))
|
||||||
|
{
|
||||||
|
throw new \InvalidArgumentException(sprintf("You must include one or more of the following build options:\n--%s\n\nSee this task's help page for more information:\n\n php console help doctrine:build", join(', --', array_keys($this->getBuildOptions()))));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::BUILD_ENTITIES == (self::BUILD_ENTITIES & $mode))
|
||||||
|
{
|
||||||
|
$this->runCommand('doctrine:build-entities');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::BUILD_DB == (self::BUILD_DB & $mode))
|
||||||
|
{
|
||||||
|
$this->runCommand('doctrine:schema-tool', array('--re-create' => true, '--connection' => $input->getOption('connection')));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($input->getOption('and-update-schema'))
|
||||||
|
{
|
||||||
|
$this->runCommand('doctrine:schema-tool', array('--update' => true, '--connection' => $input->getOption('connection')));
|
||||||
|
$this->runCommand('doctrine:schema-tool', array('--complete-update' => true, '--connection' => $input->getOption('connection')));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($input->hasOption('and-load'))
|
||||||
|
{
|
||||||
|
$dirOrFile = $input->getOption('and-load');
|
||||||
|
$this->runCommand('doctrine:load-data-fixtures',
|
||||||
|
array('--dir_or_file' => $dirOrFile, '--append' => false)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if ($input->hasOption('and-append'))
|
||||||
|
{
|
||||||
|
$dirOrFile = $input->getOption('and-append');
|
||||||
|
$this->runCommand('doctrine:load-data-fixtures', array('--dir_or_file' => $dirOrFile, '--append' => true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates a bit mode based on the supplied options.
|
||||||
|
*
|
||||||
|
* @param InputInterface $input
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
protected function calculateMode(InputInterface $input)
|
||||||
|
{
|
||||||
|
$mode = 0;
|
||||||
|
foreach ($this->getBuildOptions() as $name => $value)
|
||||||
|
{
|
||||||
|
if ($input->getOption($name) === true)
|
||||||
|
{
|
||||||
|
$mode = $mode | $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of valid build options.
|
||||||
|
*
|
||||||
|
* @return array An array of option names and their mode
|
||||||
|
*/
|
||||||
|
protected function getBuildOptions()
|
||||||
|
{
|
||||||
|
$options = array();
|
||||||
|
foreach ($this->getDefinition()->getOptions() as $option)
|
||||||
|
{
|
||||||
|
if (defined($constant = __CLASS__.'::OPTION_'.str_replace('-', '_', strtoupper($option->getName()))))
|
||||||
|
{
|
||||||
|
$options[$option->getName()] = constant($constant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Symfony\Framework\DoctrineBundle\Command;
|
||||||
|
|
||||||
|
use Symfony\Components\Console\Input\InputArgument;
|
||||||
|
use Symfony\Components\Console\Input\InputOption;
|
||||||
|
use Symfony\Components\Console\Input\InputInterface;
|
||||||
|
use Symfony\Components\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Components\Console\Output\Output;
|
||||||
|
use Symfony\Framework\WebBundle\Util\Filesystem;
|
||||||
|
use Doctrine\Common\Cli\Configuration;
|
||||||
|
use Doctrine\Common\Cli\CliController as DoctrineCliController;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the symfony framework.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build all Bundle entity classes from mapping information.
|
||||||
|
*
|
||||||
|
* @package symfony
|
||||||
|
* @subpackage console
|
||||||
|
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||||
|
* @author Jonathan H. Wage <jonwage@gmail.com>
|
||||||
|
*/
|
||||||
|
class BuildEntitiesDoctrineCommand extends DoctrineCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @see Command
|
||||||
|
*/
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->setName('doctrine:build-entities')
|
||||||
|
->setDescription('Build all Bundle entity classes from mapping information.')
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Command
|
||||||
|
*/
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
foreach ($this->container->getParameter('kernel.bundle_dirs') as $bundle => $path)
|
||||||
|
{
|
||||||
|
$bundles = glob($path.'/*Bundle');
|
||||||
|
foreach ($bundles as $p)
|
||||||
|
{
|
||||||
|
if (!is_dir($metadataPath = $p.'/Resources/config/doctrine/metadata'))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$opts = array();
|
||||||
|
$opts['--from'] = $metadataPath;
|
||||||
|
$opts['--to'] = 'annotation';
|
||||||
|
$opts['--dest'] = realpath($path.'/..');
|
||||||
|
$this->runCommand('doctrine:convert-mapping', $opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,127 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Symfony\Framework\DoctrineBundle\Command;
|
||||||
|
|
||||||
|
use Symfony\Components\Console\Input\InputArgument;
|
||||||
|
use Symfony\Components\Console\Input\InputOption;
|
||||||
|
use Symfony\Components\Console\Input\InputInterface;
|
||||||
|
use Symfony\Components\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Components\Console\Output\Output;
|
||||||
|
use Symfony\Framework\WebBundle\Util\Filesystem;
|
||||||
|
use Doctrine\Common\Cli\Configuration;
|
||||||
|
use Doctrine\Common\Cli\CliController as DoctrineCliController;
|
||||||
|
use Doctrine\DBAL\Connection;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the symfony framework.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database tool allows you to easily drop and create your configured databases.
|
||||||
|
*
|
||||||
|
* @package symfony
|
||||||
|
* @subpackage console
|
||||||
|
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||||
|
* @author Jonathan H. Wage <jonwage@gmail.com>
|
||||||
|
*/
|
||||||
|
class DatabaseToolDoctrineCommand extends DoctrineCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @see Command
|
||||||
|
*/
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->setName('doctrine:database-tool')
|
||||||
|
->setDescription('Create and drop the configured databases.')
|
||||||
|
->addOption('re-create', null, null, 'Drop and re-create your databases.')
|
||||||
|
->addOption('drop', null, null, 'Drop your databases.')
|
||||||
|
->addOption('create', null, null, 'Create your databases.')
|
||||||
|
->addOption('connection', null, null, 'The connection name to work on.')
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Command
|
||||||
|
*/
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
if ($input->getOption('re-create'))
|
||||||
|
{
|
||||||
|
$input->setOption('drop', true);
|
||||||
|
$input->setOption('create', true);
|
||||||
|
}
|
||||||
|
if (!$input->getOption('drop') && !$input->getOption('create'))
|
||||||
|
{
|
||||||
|
throw new \InvalidArgumentException('You must specify one of the --drop and --create options or both.');
|
||||||
|
}
|
||||||
|
$found = false;
|
||||||
|
$connections = $this->getDoctrineConnections();
|
||||||
|
foreach ($connections as $name => $connection)
|
||||||
|
{
|
||||||
|
if ($input->getOption('connection') && $name != $input->getOption('connection'))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($input->getOption('drop'))
|
||||||
|
{
|
||||||
|
$this->dropDatabaseForConnection($connection, $output);
|
||||||
|
}
|
||||||
|
if ($input->getOption('create'))
|
||||||
|
{
|
||||||
|
$this->createDatabaseForConnection($connection, $output);
|
||||||
|
}
|
||||||
|
$found = true;
|
||||||
|
}
|
||||||
|
if ($found === false)
|
||||||
|
{
|
||||||
|
if ($input->getOption('connection'))
|
||||||
|
{
|
||||||
|
throw new \InvalidArgumentException(sprintf('<error>Could not find a connection named <comment>%s</comment></error>', $input->getOption('connection')));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new \InvalidArgumentException(sprintf('<error>Could not find any configured connections</error>', $input->getOption('connection')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function dropDatabaseForConnection(Connection $connection, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$params = $connection->getParams();
|
||||||
|
$name = isset($params['path']) ? $params['path']:$params['dbname'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$connection->getSchemaManager()->dropDatabase($name);
|
||||||
|
$output->writeln(sprintf('<info>Dropped database for connection named <comment>%s</comment></info>', $name));
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$output->writeln(sprintf('<error>Could not drop database for connection named <comment>%s</comment></error>', $name));
|
||||||
|
$output->writeln(sprintf('<error>%s</error>', $e->getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createDatabaseForConnection(Connection $connection, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$params = $connection->getParams();
|
||||||
|
$name = isset($params['path']) ? $params['path']:$params['dbname'];
|
||||||
|
|
||||||
|
unset($params['dbname']);
|
||||||
|
|
||||||
|
$tmpConnection = \Doctrine\DBAL\DriverManager::getConnection($params);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$tmpConnection->getSchemaManager()->createDatabase($name);
|
||||||
|
$output->writeln(sprintf('<info>Created database for connection named <comment>%s</comment></info>', $name));
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$output->writeln(sprintf('<error>Could not create database for connection named <comment>%s</comment></error>', $name));
|
||||||
|
$output->writeln(sprintf('<error>%s</error>', $e->getMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
$tmpConnection->close();
|
||||||
|
}
|
||||||
|
}
|
@ -3,11 +3,13 @@
|
|||||||
namespace Symfony\Framework\DoctrineBundle\Command;
|
namespace Symfony\Framework\DoctrineBundle\Command;
|
||||||
|
|
||||||
use Symfony\Framework\WebBundle\Command\Command;
|
use Symfony\Framework\WebBundle\Command\Command;
|
||||||
|
use Symfony\Components\Console\Input\ArrayInput;
|
||||||
use Symfony\Components\Console\Input\InputArgument;
|
use Symfony\Components\Console\Input\InputArgument;
|
||||||
use Symfony\Components\Console\Input\InputOption;
|
use Symfony\Components\Console\Input\InputOption;
|
||||||
use Symfony\Components\Console\Input\InputInterface;
|
use Symfony\Components\Console\Input\InputInterface;
|
||||||
use Symfony\Components\Console\Output\OutputInterface;
|
use Symfony\Components\Console\Output\OutputInterface;
|
||||||
use Symfony\Components\Console\Output\Output;
|
use Symfony\Components\Console\Output\Output;
|
||||||
|
use Symfony\Framework\WebBundle\Console\Application;
|
||||||
use Symfony\Framework\WebBundle\Util\Filesystem;
|
use Symfony\Framework\WebBundle\Util\Filesystem;
|
||||||
use Doctrine\Common\Cli\Configuration;
|
use Doctrine\Common\Cli\Configuration;
|
||||||
use Doctrine\Common\Cli\CliController as DoctrineCliController;
|
use Doctrine\Common\Cli\CliController as DoctrineCliController;
|
||||||
@ -30,16 +32,19 @@ use Doctrine\Common\Cli\CliController as DoctrineCliController;
|
|||||||
*/
|
*/
|
||||||
abstract class DoctrineCommand extends Command
|
abstract class DoctrineCommand extends Command
|
||||||
{
|
{
|
||||||
protected $cli;
|
protected
|
||||||
|
$cli,
|
||||||
|
$em;
|
||||||
|
|
||||||
protected function getDoctrineCli()
|
protected function getDoctrineCli()
|
||||||
{
|
{
|
||||||
if ($this->cli === null)
|
if ($this->cli === null)
|
||||||
{
|
{
|
||||||
$configuration = new Configuration();
|
$configuration = new Configuration();
|
||||||
$configuration->setAttribute('em', $this->container->getDoctrine_Orm_EntityManagerService());
|
|
||||||
$this->cli = new DoctrineCliController($configuration);
|
$this->cli = new DoctrineCliController($configuration);
|
||||||
}
|
}
|
||||||
|
$em = $this->em ? $this->em : $this->container->getDoctrine_Orm_EntityManagerService();
|
||||||
|
$this->cli->getConfiguration()->setAttribute('em', $em);
|
||||||
return $this->cli;
|
return $this->cli;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +65,7 @@ abstract class DoctrineCommand extends Command
|
|||||||
return $this->getDoctrineCli()->run(array_merge(array('doctrine', $name), $builtOptions));
|
return $this->getDoctrineCli()->run(array_merge(array('doctrine', $name), $builtOptions));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildDoctrineCliTaskOptions(InputInterface $input, array $options)
|
protected function buildDoctrineCliTaskOptions(InputInterface $input, array $options)
|
||||||
{
|
{
|
||||||
$taskOptions = array();
|
$taskOptions = array();
|
||||||
foreach ($options as $option)
|
foreach ($options as $option)
|
||||||
@ -72,4 +77,49 @@ abstract class DoctrineCommand extends Command
|
|||||||
}
|
}
|
||||||
return $options;
|
return $options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function runCommand($name, array $input = array())
|
||||||
|
{
|
||||||
|
$arguments = array();
|
||||||
|
$arguments = array_merge(array($name), $input);
|
||||||
|
$input = new ArrayInput($arguments);
|
||||||
|
$application = new Application($this->container->getKernelService());
|
||||||
|
$application->setAutoExit(false);
|
||||||
|
$application->run($input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Better way to do these functions?
|
||||||
|
*/
|
||||||
|
protected function getDoctrineConnections()
|
||||||
|
{
|
||||||
|
$connections = array();
|
||||||
|
$ids = $this->container->getServiceIds();
|
||||||
|
foreach ($ids as $id)
|
||||||
|
{
|
||||||
|
preg_match('/doctrine.dbal.(.*)_connection/', $id, $matches);
|
||||||
|
if ($matches)
|
||||||
|
{
|
||||||
|
$name = $matches[1];
|
||||||
|
$connections[$name] = $this->container->getService($id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $connections;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getDoctrineEntityManagers()
|
||||||
|
{
|
||||||
|
$entityManagers = array();
|
||||||
|
$ids = $this->container->getServiceIds();
|
||||||
|
foreach ($ids as $id)
|
||||||
|
{
|
||||||
|
preg_match('/doctrine.orm.(.*)_entity_manager/', $id, $matches);
|
||||||
|
if ($matches)
|
||||||
|
{
|
||||||
|
$name = $matches[1];
|
||||||
|
$entityManagers[$name] = $this->container->getService($id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $entityManagers;
|
||||||
|
}
|
||||||
}
|
}
|
@ -41,7 +41,7 @@ class LoadDataFixturesDoctrineCommand extends DoctrineCommand
|
|||||||
$this
|
$this
|
||||||
->setName('doctrine:load-data-fixtures')
|
->setName('doctrine:load-data-fixtures')
|
||||||
->setDescription('Load data fixtures to your database.')
|
->setDescription('Load data fixtures to your database.')
|
||||||
->addOption('dir_or_file', null, null, 'The directory or file to load data fixtures from.')
|
->addOption('dir_or_file', null, InputOption::PARAMETER_OPTIONAL | InputOption::PARAMETER_IS_ARRAY, 'The directory or file to load data fixtures from.')
|
||||||
->addOption('append', null, InputOption::PARAMETER_OPTIONAL, 'Whether or not to append the data fixtures.', false)
|
->addOption('append', null, InputOption::PARAMETER_OPTIONAL, 'Whether or not to append the data fixtures.', false)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
@ -51,37 +51,11 @@ class LoadDataFixturesDoctrineCommand extends DoctrineCommand
|
|||||||
*/
|
*/
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$em = $this->container->getDoctrine_ORM_EntityManagerService();
|
$defaultEm = $this->container->getDoctrine_ORM_EntityManagerService();
|
||||||
if (!$input->getOption('append'))
|
|
||||||
{
|
|
||||||
$classes = array();
|
|
||||||
$metadatas = $em->getMetadataFactory()->getAllMetadata();
|
|
||||||
|
|
||||||
foreach ($metadatas as $metadata)
|
|
||||||
{
|
|
||||||
if (!$metadata->isMappedSuperclass)
|
|
||||||
{
|
|
||||||
$classes[] = $metadata;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$cmf = $em->getMetadataFactory();
|
|
||||||
$classes = $this->getCommitOrder($em, $classes);
|
|
||||||
for ($i = count($classes) - 1; $i >= 0; --$i)
|
|
||||||
{
|
|
||||||
$class = $classes[$i];
|
|
||||||
if ($cmf->hasMetadataFor($class->name))
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$em->createQuery('DELETE FROM '.$class->name.' a')->execute();
|
|
||||||
} catch (Exception $e) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$dirOrFile = $input->getOption('dir_or_file');
|
$dirOrFile = $input->getOption('dir_or_file');
|
||||||
if ($dirOrFile)
|
if ($dirOrFile)
|
||||||
{
|
{
|
||||||
$paths = $dirOrFile;
|
$paths = is_array($dirOrFile) ? $dirOrFile : array($dirOrFile);
|
||||||
} else {
|
} else {
|
||||||
$paths = array();
|
$paths = array();
|
||||||
$bundleDirs = $this->container->getKernelService()->getBundleDirs();
|
$bundleDirs = $this->container->getKernelService()->getBundleDirs();
|
||||||
@ -112,20 +86,83 @@ class LoadDataFixturesDoctrineCommand extends DoctrineCommand
|
|||||||
$files = array_merge($files, $found);
|
$files = array_merge($files, $found);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$ems = array();
|
||||||
|
$emEntities = array();
|
||||||
$files = array_unique($files);
|
$files = array_unique($files);
|
||||||
|
|
||||||
foreach ($files as $file)
|
foreach ($files as $file)
|
||||||
{
|
{
|
||||||
|
$em = $defaultEm;
|
||||||
|
$output->writeln(sprintf('<info>Loading data fixtures from <comment>"%s"</comment></info>', $file));
|
||||||
|
|
||||||
$before = array_keys(get_defined_vars());
|
$before = array_keys(get_defined_vars());
|
||||||
include($file);
|
include($file);
|
||||||
$after = array_keys(get_defined_vars());
|
$after = array_keys(get_defined_vars());
|
||||||
$new = array_diff($after, $before);
|
$new = array_diff($after, $before);
|
||||||
$entities = array_values($new);
|
$params = $em->getConnection()->getParams();
|
||||||
unset($entities[array_search('before', $entities)]);
|
$emName = isset($params['path']) ? $params['path']:$params['dbname'];
|
||||||
foreach ($entities as $entity) {
|
|
||||||
$em->persist($$entity);
|
$ems[$emName] = $em;
|
||||||
|
$emEntities[$emName] = array();
|
||||||
|
$variables = array_values($new);
|
||||||
|
|
||||||
|
foreach ($variables as $variable)
|
||||||
|
{
|
||||||
|
$value = $$variable;
|
||||||
|
if (!is_object($value) || $value instanceof \Doctrine\ORM\EntityManager)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$emEntities[$emName][] = $value;
|
||||||
|
}
|
||||||
|
foreach ($ems as $emName => $em)
|
||||||
|
{
|
||||||
|
if (!$input->getOption('append'))
|
||||||
|
{
|
||||||
|
$output->writeln(sprintf('<info>Purging data from entity manager named <comment>"%s"</comment></info>', $emName));
|
||||||
|
$this->purgeEntityManager($em);
|
||||||
|
}
|
||||||
|
|
||||||
|
$entities = $emEntities[$emName];
|
||||||
|
$numEntities = count($entities);
|
||||||
|
$output->writeln(sprintf('<info>Persisting "%s" '.($numEntities > 1 ? 'entities' : 'entity').'</info>', count($entities)));
|
||||||
|
|
||||||
|
foreach ($entities as $entity)
|
||||||
|
{
|
||||||
|
$output->writeln(sprintf('<info>Persisting "%s" entity:</info>', get_class($entity)));
|
||||||
|
$output->writeln('');
|
||||||
|
$output->writeln(var_dump($entity));
|
||||||
|
|
||||||
|
$em->persist($entity);
|
||||||
|
}
|
||||||
|
$output->writeln('<info>Flushing entity manager</info>');
|
||||||
|
$em->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function purgeEntityManager(EntityManager $em)
|
||||||
|
{
|
||||||
|
$classes = array();
|
||||||
|
$metadatas = $em->getMetadataFactory()->getAllMetadata();
|
||||||
|
|
||||||
|
foreach ($metadatas as $metadata)
|
||||||
|
{
|
||||||
|
if (!$metadata->isMappedSuperclass)
|
||||||
|
{
|
||||||
|
$classes[] = $metadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$cmf = $em->getMetadataFactory();
|
||||||
|
$classes = $this->getCommitOrder($em, $classes);
|
||||||
|
for ($i = count($classes) - 1; $i >= 0; --$i)
|
||||||
|
{
|
||||||
|
$class = $classes[$i];
|
||||||
|
if ($cmf->hasMetadataFor($class->name))
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$em->createQuery('DELETE FROM '.$class->name.' a')->execute();
|
||||||
|
} catch (Exception $e) {}
|
||||||
}
|
}
|
||||||
$em->flush();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ class RunDqlDoctrineCommand extends DoctrineCommand
|
|||||||
->setDescription('Executes arbitrary DQL directly from the command line.')
|
->setDescription('Executes arbitrary DQL directly from the command line.')
|
||||||
->addOption('dql', null, null, 'The DQL query to run.')
|
->addOption('dql', null, null, 'The DQL query to run.')
|
||||||
->addOption('depth', null, null, 'The depth to output the data to.')
|
->addOption('depth', null, null, 'The depth to output the data to.')
|
||||||
|
->addOption('connection', null, null, 'The connection to use.')
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ class RunSqlDoctrineCommand extends DoctrineCommand
|
|||||||
->addOption('sql', null, null, 'The SQL query to run.')
|
->addOption('sql', null, null, 'The SQL query to run.')
|
||||||
->addOption('file', null, null, 'Path to a SQL file to run.')
|
->addOption('file', null, null, 'Path to a SQL file to run.')
|
||||||
->addOption('depth', null, null, 'The depth to output the data to.')
|
->addOption('depth', null, null, 'The depth to output the data to.')
|
||||||
|
->addOption('connection', null, null, 'The connection to use.')
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ class SchemaToolDoctrineCommand extends DoctrineCommand
|
|||||||
->addOption('complete-update', null, null, 'Complete update and drop anything that is not in your schema.')
|
->addOption('complete-update', null, null, 'Complete update and drop anything that is not in your schema.')
|
||||||
->addOption('re-create', null, null, 'Drop and re-create your database schema.')
|
->addOption('re-create', null, null, 'Drop and re-create your database schema.')
|
||||||
->addOption('dump-sql', null, null, 'Dump the SQL instead of executing it.')
|
->addOption('dump-sql', null, null, 'Dump the SQL instead of executing it.')
|
||||||
|
->addOption('connection', null, null, 'The connection to use.')
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,8 +56,33 @@ class SchemaToolDoctrineCommand extends DoctrineCommand
|
|||||||
$options = $this->buildDoctrineCliTaskOptions($input, array(
|
$options = $this->buildDoctrineCliTaskOptions($input, array(
|
||||||
'create', 'drop', 'update', 'complete-update', 're-create', 'dump-sql'
|
'create', 'drop', 'update', 'complete-update', 're-create', 'dump-sql'
|
||||||
));
|
));
|
||||||
|
|
||||||
$entityDirs = $this->container->getParameter('doctrine.orm.entity_dirs');
|
$entityDirs = $this->container->getParameter('doctrine.orm.entity_dirs');
|
||||||
$options['class-dir'] = implode(', ', $entityDirs);
|
$options['class-dir'] = implode(', ', $entityDirs);
|
||||||
$this->runDoctrineCliTask('orm:schema-tool', $options);
|
|
||||||
|
$found = false;
|
||||||
|
$ems = $this->getDoctrineEntityManagers();
|
||||||
|
foreach ($ems as $name => $em)
|
||||||
|
{
|
||||||
|
if ($input->getOption('connection') && $name !== $input->getOption('connection'))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->em = $em;
|
||||||
|
$this->runDoctrineCliTask('orm:schema-tool', $options);
|
||||||
|
$found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($found === false)
|
||||||
|
{
|
||||||
|
if ($input->getOption('connection'))
|
||||||
|
{
|
||||||
|
$output->writeln(sprintf('<error>Could not find a connection named <comment>%s</comment></error>', $input->getOption('connection')));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$output->writeln(sprintf('<error>Could not find any configured connections</error>', $input->getOption('connection')));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
191
src/Symfony/Framework/DoctrineBundle/README
Normal file
191
src/Symfony/Framework/DoctrineBundle/README
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
DoctrineBundle
|
||||||
|
--------------
|
||||||
|
|
||||||
|
This document describes some of the functionality provided by the
|
||||||
|
**DoctrineBundle**. Doctrine 2 is a first class citizen in Symfony 2 and is
|
||||||
|
tightly integrated. Continue reading to learn how to use Doctrine 2 with the
|
||||||
|
latest Symfony 2!
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
This section will help you with configuring your Symfony 2 project to enable
|
||||||
|
the Doctrine DBAL and ORM services.
|
||||||
|
|
||||||
|
### Database Abstraction Layer
|
||||||
|
|
||||||
|
You can configure your database abstraction layer simply configuration a few
|
||||||
|
pieces of information about your database. If you only have one database you can
|
||||||
|
do the following:
|
||||||
|
|
||||||
|
[yml]
|
||||||
|
doctrine.dbal:
|
||||||
|
dbname: symfony_guestbook
|
||||||
|
user: root
|
||||||
|
password: ~
|
||||||
|
|
||||||
|
Or if you have multiple connections and want to customize the configuration of the
|
||||||
|
connection further you can use the following:
|
||||||
|
|
||||||
|
[yml]
|
||||||
|
doctrine.dbal:
|
||||||
|
default_connection: default
|
||||||
|
connections:
|
||||||
|
default:
|
||||||
|
driver: PDOSqlite # PDOSqlite, PDOMySql, PDOMsSql, PDOOracle, PDOPgSql, OCI8
|
||||||
|
dbname: symfony
|
||||||
|
user: root
|
||||||
|
password: null
|
||||||
|
host: localhost
|
||||||
|
port: ~
|
||||||
|
path: %kernel.data_dir%/symfony.sqlite
|
||||||
|
event_manager_class: Doctrine\Common\EventManager
|
||||||
|
configuration_class: Doctrine\DBAL\Configuration
|
||||||
|
wrapper_class: ~
|
||||||
|
options: []
|
||||||
|
|
||||||
|
### Object Relational Mapper
|
||||||
|
|
||||||
|
If you want to enable the Doctrine 2 ORM you can do so with the following:
|
||||||
|
|
||||||
|
doctrine.orm:
|
||||||
|
default_entity_manager: default
|
||||||
|
metadata_driver: xml # xml, yml, annotation
|
||||||
|
cache_driver: apc # array, apc, memcache, xcache
|
||||||
|
entity_managers:
|
||||||
|
default:
|
||||||
|
connection: default
|
||||||
|
|
||||||
|
It's pretty simple, you can specify which entity managers you want to instantiate
|
||||||
|
for which connections and also configure some other information about the ORM
|
||||||
|
like what type of mapping files to use or what cache driver to use.
|
||||||
|
|
||||||
|
## Creating a Bundle
|
||||||
|
|
||||||
|
To get started we need to create a new bundle:
|
||||||
|
|
||||||
|
$ php console init:bundle "Bundle\\GuestbookBundle"
|
||||||
|
Initializing bundle "GuestbookBundle" in "/path/to/symfony-sandbox/src/Bundle"
|
||||||
|
|
||||||
|
Now basically the most important thing to know about using Doctrine 2 with Symfony
|
||||||
|
is where to put your mapping information files, where your entity classes are and
|
||||||
|
a few commands to help move things faster!
|
||||||
|
|
||||||
|
## Mapping Information
|
||||||
|
|
||||||
|
You can place all your mapping information inside a bundle. Below is an example
|
||||||
|
path for the **GuestbookBundle** we created above:
|
||||||
|
|
||||||
|
/path/to/symfony-sandbox/src/Bundle/GuestbookBundle/Resources/config/doctrine/metadata
|
||||||
|
|
||||||
|
Any files found in here that have a suffix of **.dcm.xml** (or whatever
|
||||||
|
mapping_driver you picked) are used as your entities mapping information.
|
||||||
|
|
||||||
|
In the **GuestbookBundle** we have a file named **Bundle.GuestbookBundle.Entities.Entry.dcm.xml**
|
||||||
|
which contains the following XML:
|
||||||
|
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||||
|
http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||||
|
|
||||||
|
<entity name="Bundle\GuestbookBundle\Entities\Entry" table="guestbook_entry">
|
||||||
|
|
||||||
|
<id name="id" type="integer" column="id">
|
||||||
|
<generator strategy="AUTO"/>
|
||||||
|
</id>
|
||||||
|
|
||||||
|
<field name="createdAt" column="created_at" type="datetime" />
|
||||||
|
<field name="name" column="name" type="string" length="255" />
|
||||||
|
<field name="emailAddress" column="email_address" type="string" length="255" />
|
||||||
|
<field name="body" column="body" type="text" />
|
||||||
|
|
||||||
|
</entity>
|
||||||
|
|
||||||
|
</doctrine-mapping>
|
||||||
|
|
||||||
|
## Building Entities
|
||||||
|
|
||||||
|
Doctrine can help you a little bit by generating the entity classes for your
|
||||||
|
mapping information with the command:
|
||||||
|
|
||||||
|
$ php console doctrine:build-entities
|
||||||
|
|
||||||
|
Now if you have a look in the bundles **Entities** directory you will see a new
|
||||||
|
file named **Entry.php** with some code like the following:
|
||||||
|
|
||||||
|
[php]
|
||||||
|
// Bundle/GuestbookBundle/Entities/Entry.php
|
||||||
|
|
||||||
|
namespace Bundle\GuestbookBundle\Entities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Entity
|
||||||
|
* @Table(name="guestbook_entry")
|
||||||
|
*/
|
||||||
|
class Entry
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @Column(name="created_at", type="datetime")
|
||||||
|
*/
|
||||||
|
private $createdAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Column(name="name", type="string", length=255)
|
||||||
|
*/
|
||||||
|
private $name;
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
> **NOTE**
|
||||||
|
> If you modify your mapping information and re-run the build entities command
|
||||||
|
> it will modify the classes and update them based on the mapping information.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
The Doctrine 2 CLI is integrated with the Symfony 2 CLI so we have all the common
|
||||||
|
commands we need to make working with Doctrine 2 just as easy and fast as before!
|
||||||
|
|
||||||
|
$ php console list doctrine
|
||||||
|
|
||||||
|
Available commands for the "doctrine" namespace:
|
||||||
|
:build Build task for easily re-building your Doctrine development environment.
|
||||||
|
:build-entities Build all Bundle entity classes from mapping information.
|
||||||
|
:clear-cache Clear cache from configured query, result and metadata drivers. (doctrine:cc)
|
||||||
|
:convert-mapping Convert mapping information between supported formats.
|
||||||
|
:database-tool Create and drop the configured databases.
|
||||||
|
:ensure-production-settings Verify that Doctrine is properly configured for a production environment.
|
||||||
|
:generate-proxies Generates proxy classes for entity classes.
|
||||||
|
:load-data-fixtures Load data fixtures to your database.
|
||||||
|
:run-dql Executes arbitrary DQL directly from the command line.
|
||||||
|
:run-sql Executes arbitrary SQL from a file or directly from the command line.
|
||||||
|
:schema-tool Processes the schema and either apply it directly on EntityManager or generate the SQL output.
|
||||||
|
:version Displays the current installed Doctrine version.
|
||||||
|
|
||||||
|
The development workflow is very similar to how it is in Symfony 1.4. You can modify
|
||||||
|
your mapping information and use **doctrine:build --all** to re-build your
|
||||||
|
environment:
|
||||||
|
|
||||||
|
php console doctrine:build --all
|
||||||
|
|
||||||
|
The recommend way to work is to re-build your entities and update your database
|
||||||
|
schema:
|
||||||
|
|
||||||
|
php console doctrine:build --entities --and-update-schema
|
||||||
|
|
||||||
|
Now any changes you made in your mapping information will be reflected in the
|
||||||
|
according databases! Here are all the available options for the **build** task:
|
||||||
|
|
||||||
|
$ php console help doctrine:build
|
||||||
|
Usage:
|
||||||
|
Symfony doctrine:build [--all] [--all-classes] [--entities] [--db] [--and-load[="..."]] [--and-append[="..."]] [--and-update-schema] [--dump-sql] [--connection]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--all Build everything and reset the database
|
||||||
|
--entities Build model classes
|
||||||
|
--db Drop database, create database and create schema.
|
||||||
|
--and-load Load data fixtures (multiple values allowed)
|
||||||
|
--and-append Load data fixtures and append to existing data (multiple values allowed)
|
||||||
|
--and-update-schema Update schema after rebuilding all classes
|
||||||
|
--connection The connection to use.
|
Reference in New Issue
Block a user