Merge remote-tracking branch 'remotes/upstream/master'
This commit is contained in:
commit
524c799e37
@ -55,7 +55,24 @@ class DatabaseDriver implements Driver
|
||||
* @var array
|
||||
*/
|
||||
private $manyToManyTables = array();
|
||||
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $classNamesForTables = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $fieldNamesForColumns = array();
|
||||
|
||||
/**
|
||||
* The namespace for the generated entities.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $namespace;
|
||||
|
||||
/**
|
||||
* Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
|
||||
* docblock annotations.
|
||||
@ -78,7 +95,7 @@ class DatabaseDriver implements Driver
|
||||
{
|
||||
$this->tables = $this->manyToManyTables = $this->classToTableNames = array();
|
||||
foreach ($entityTables AS $table) {
|
||||
$className = Inflector::classify(strtolower($table->getName()));
|
||||
$className = $this->getClassNameForTable($table->getName());
|
||||
$this->classToTableNames[$className] = $table->getName();
|
||||
$this->tables[$table->getName()] = $table;
|
||||
}
|
||||
@ -120,7 +137,7 @@ class DatabaseDriver implements Driver
|
||||
} else {
|
||||
// lower-casing is necessary because of Oracle Uppercase Tablenames,
|
||||
// assumption is lower-case + underscore separated.
|
||||
$className = Inflector::classify(strtolower($tableName));
|
||||
$className = $this->getClassNameForTable($tableName);
|
||||
$this->tables[$tableName] = $table;
|
||||
$this->classToTableNames[$className] = $tableName;
|
||||
}
|
||||
@ -172,7 +189,7 @@ class DatabaseDriver implements Driver
|
||||
continue;
|
||||
}
|
||||
|
||||
$fieldMapping['fieldName'] = Inflector::camelize(strtolower($column->getName()));
|
||||
$fieldMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $column->getName(), false);
|
||||
$fieldMapping['columnName'] = $column->getName();
|
||||
$fieldMapping['type'] = strtolower((string) $column->getType());
|
||||
|
||||
@ -226,10 +243,10 @@ class DatabaseDriver implements Driver
|
||||
|
||||
$localColumn = current($myFk->getColumns());
|
||||
$associationMapping = array();
|
||||
$associationMapping['fieldName'] = Inflector::camelize(str_replace('_id', '', strtolower(current($otherFk->getColumns()))));
|
||||
$associationMapping['targetEntity'] = Inflector::classify(strtolower($otherFk->getForeignTableName()));
|
||||
$associationMapping['fieldName'] = $this->getFieldNameForColumn($manyTable->getName(), current($otherFk->getColumns()), true);
|
||||
$associationMapping['targetEntity'] = $this->getClassNameForTable($otherFk->getForeignTableName());
|
||||
if (current($manyTable->getColumns())->getName() == $localColumn) {
|
||||
$associationMapping['inversedBy'] = Inflector::camelize(str_replace('_id', '', strtolower(current($myFk->getColumns()))));
|
||||
$associationMapping['inversedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true);
|
||||
$associationMapping['joinTable'] = array(
|
||||
'name' => strtolower($manyTable->getName()),
|
||||
'joinColumns' => array(),
|
||||
@ -254,7 +271,7 @@ class DatabaseDriver implements Driver
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$associationMapping['mappedBy'] = Inflector::camelize(str_replace('_id', '', strtolower(current($myFk->getColumns()))));
|
||||
$associationMapping['mappedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true);
|
||||
}
|
||||
$metadata->mapManyToMany($associationMapping);
|
||||
break;
|
||||
@ -269,8 +286,8 @@ class DatabaseDriver implements Driver
|
||||
|
||||
$localColumn = current($cols);
|
||||
$associationMapping = array();
|
||||
$associationMapping['fieldName'] = Inflector::camelize(str_replace('_id', '', strtolower($localColumn)));
|
||||
$associationMapping['targetEntity'] = Inflector::classify($foreignTable);
|
||||
$associationMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $localColumn, true);
|
||||
$associationMapping['targetEntity'] = $this->getClassNameForTable($foreignTable);
|
||||
|
||||
for ($i = 0; $i < count($cols); $i++) {
|
||||
$associationMapping['joinColumns'][] = array(
|
||||
@ -303,4 +320,78 @@ class DatabaseDriver implements Driver
|
||||
|
||||
return array_keys($this->classToTableNames);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set class name for a table.
|
||||
*
|
||||
* @param string $tableName
|
||||
* @param string $className
|
||||
* @return void
|
||||
*/
|
||||
public function setClassNameForTable($tableName, $className)
|
||||
{
|
||||
$this->classNamesForTables[$tableName] = $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set field name for a column on a specific table.
|
||||
*
|
||||
* @param string $tableName
|
||||
* @param string $columnName
|
||||
* @param string $fieldName
|
||||
* @return void
|
||||
*/
|
||||
public function setFieldNameForColumn($tableName, $columnName, $fieldName)
|
||||
{
|
||||
$this->fieldNamesForColumns[$tableName][$columnName] = $fieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mapped class name for a table if it exists. Otherwise return "classified" version.
|
||||
*
|
||||
* @param string $tableName
|
||||
* @return string
|
||||
*/
|
||||
private function getClassNameForTable($tableName)
|
||||
{
|
||||
if (isset($this->classNamesForTables[$tableName])) {
|
||||
return $this->namespace . $this->classNamesForTables[$tableName];
|
||||
}
|
||||
|
||||
return $this->namespace . Inflector::classify(strtolower($tableName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mapped field name for a column, if it exists. Otherwise return camelized version.
|
||||
*
|
||||
* @param string $tableName
|
||||
* @param string $columnName
|
||||
* @param boolean $fk Whether the column is a foreignkey or not.
|
||||
* @return string
|
||||
*/
|
||||
private function getFieldNameForColumn($tableName, $columnName, $fk = false)
|
||||
{
|
||||
if (isset($this->fieldNamesForColumns[$tableName]) && isset($this->fieldNamesForColumns[$tableName][$columnName])) {
|
||||
return $this->fieldNamesForColumns[$tableName][$columnName];
|
||||
}
|
||||
|
||||
$columnName = strtolower($columnName);
|
||||
|
||||
// Replace _id if it is a foreignkey column
|
||||
if ($fk) {
|
||||
$columnName = str_replace('_id', '', $columnName);
|
||||
}
|
||||
return Inflector::camelize($columnName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the namespace for the generated entities.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @return void
|
||||
*/
|
||||
public function setNamespace($namespace)
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
}
|
||||
}
|
||||
|
@ -240,7 +240,7 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the root alias of the query. This is the first entity alias involved
|
||||
* Gets the root aliases of the query. This is the entity aliases involved
|
||||
* in the construction of the query.
|
||||
*
|
||||
* <code>
|
||||
@ -251,7 +251,7 @@ class QueryBuilder
|
||||
* $qb->getRootAliases(); // array('u')
|
||||
* </code>
|
||||
*
|
||||
* @return string $rootAlias
|
||||
* @return array $rootAliases
|
||||
*/
|
||||
public function getRootAliases()
|
||||
{
|
||||
@ -272,6 +272,39 @@ class QueryBuilder
|
||||
return $aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the root entities of the query. This is the entity aliases involved
|
||||
* in the construction of the query.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u');
|
||||
*
|
||||
* $qb->getRootEntities(); // array('User')
|
||||
* </code>
|
||||
*
|
||||
* @return array $rootEntities
|
||||
*/
|
||||
public function getRootEntities()
|
||||
{
|
||||
$entities = array();
|
||||
|
||||
foreach ($this->_dqlParts['from'] as &$fromClause) {
|
||||
if (is_string($fromClause)) {
|
||||
$spacePos = strrpos($fromClause, ' ');
|
||||
$from = substr($fromClause, 0, $spacePos);
|
||||
$alias = substr($fromClause, $spacePos + 1);
|
||||
|
||||
$fromClause = new Query\Expr\From($from, $alias);
|
||||
}
|
||||
|
||||
$entities[] = $fromClause->getFrom();
|
||||
}
|
||||
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a query parameter for the query being constructed.
|
||||
*
|
||||
|
@ -78,6 +78,10 @@ class ConvertMappingCommand extends Console\Command\Command
|
||||
'num-spaces', null, InputOption::VALUE_OPTIONAL,
|
||||
'Defines the number of indentation spaces', 4
|
||||
),
|
||||
new InputOption(
|
||||
'namespace', null, InputOption::VALUE_OPTIONAL,
|
||||
'Defines a namespace for the generated entity classes, if converted from database.'
|
||||
),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
Convert mapping information between supported formats.
|
||||
@ -107,11 +111,17 @@ EOT
|
||||
$em = $this->getHelper('em')->getEntityManager();
|
||||
|
||||
if ($input->getOption('from-database') === true) {
|
||||
$em->getConfiguration()->setMetadataDriverImpl(
|
||||
new \Doctrine\ORM\Mapping\Driver\DatabaseDriver(
|
||||
$em->getConnection()->getSchemaManager()
|
||||
)
|
||||
$databaseDriver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver(
|
||||
$em->getConnection()->getSchemaManager()
|
||||
);
|
||||
|
||||
$em->getConfiguration()->setMetadataDriverImpl(
|
||||
$databaseDriver
|
||||
);
|
||||
|
||||
if (($namespace = $input->getOption('namespace')) !== null) {
|
||||
$databaseDriver->setNamespace($namespace);
|
||||
}
|
||||
}
|
||||
|
||||
$cmf = new DisconnectedClassMetadataFactory();
|
||||
|
@ -28,7 +28,8 @@ use Symfony\Component\Console\Input\InputArgument,
|
||||
Doctrine\ORM\Tools\SchemaTool;
|
||||
|
||||
/**
|
||||
* Command to update the database schema for a set of classes based on their mappings.
|
||||
* Command to generate the SQL needed to update the database schema to match
|
||||
* the current mapping information.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
@ -38,37 +39,58 @@ use Symfony\Component\Console\Input\InputArgument,
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Ryan Weaver <ryan@thatsquality.com>
|
||||
*/
|
||||
class UpdateCommand extends AbstractCommand
|
||||
{
|
||||
protected $name = 'orm:schema-tool:update';
|
||||
|
||||
/**
|
||||
* @see Console\Command\Command
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('orm:schema-tool:update')
|
||||
->setName($this->name)
|
||||
->setDescription(
|
||||
'Processes the schema and either update the database schema of EntityManager Storage Connection or generate the SQL output.'
|
||||
'Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata.'
|
||||
)
|
||||
->setDefinition(array(
|
||||
new InputOption(
|
||||
'complete', null, InputOption::VALUE_NONE,
|
||||
'If defined, all assets of the database which are not relevant to the current metadata will be dropped.'
|
||||
),
|
||||
|
||||
new InputOption(
|
||||
'dump-sql', null, InputOption::VALUE_NONE,
|
||||
'Instead of try to apply generated SQLs into EntityManager Storage Connection, output them.'
|
||||
'Dumps the generated SQL statements to the screen (does not execute them).'
|
||||
),
|
||||
new InputOption(
|
||||
'force', null, InputOption::VALUE_NONE,
|
||||
"Don't ask for the incremental update of the database, but force the operation to run."
|
||||
'Causes the generated SQL statements to be physically executed against your database.'
|
||||
),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
Processes the schema and either update the database schema of EntityManager Storage Connection or generate the SQL output.
|
||||
Beware that if --complete is not defined, it will do a save update, which does not delete any tables, sequences or affected foreign keys.
|
||||
If defined, all assets of the database which are not relevant to the current metadata are dropped by this command.
|
||||
));
|
||||
|
||||
$fullName = $this->getFullName();
|
||||
$this->setHelp(<<<EOT
|
||||
The <info>$fullName</info> command generates the SQL needed to
|
||||
synchronize the database schema with the current mapping metadata of the
|
||||
default entity manager.
|
||||
|
||||
For example, if you add metadata for a new column to an entity, this command
|
||||
would generate and output the SQL needed to add the new column to the database:
|
||||
|
||||
<info>$fullName --dump-sql</info>
|
||||
|
||||
Alternatively, you can execute the generated queries:
|
||||
|
||||
<info>$fullName --force</info>
|
||||
|
||||
Finally, be aware that if the <info>--complete</info> option is passed, this
|
||||
task will drop all database assets (e.g. tables, etc) that are *not* described
|
||||
by the current metadata. In other words, without this option, this task leaves
|
||||
untouched any "extra" tables that exist in the database, but which aren't
|
||||
described by any metadata.
|
||||
EOT
|
||||
);
|
||||
}
|
||||
@ -78,26 +100,36 @@ EOT
|
||||
// Defining if update is complete or not (--complete not defined means $saveMode = true)
|
||||
$saveMode = ($input->getOption('complete') !== true);
|
||||
|
||||
if ($input->getOption('dump-sql') === true) {
|
||||
$sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode);
|
||||
$output->write(implode(';' . PHP_EOL, $sqls) . PHP_EOL);
|
||||
} else if ($input->getOption('force') === true) {
|
||||
$output->write('Updating database schema...' . PHP_EOL);
|
||||
$sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode);
|
||||
if (0 == count($sqls)) {
|
||||
$output->writeln('Nothing to update - your database is already in sync with the current entity metadata.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$dumpSql = (true === $input->getOption('dump-sql'));
|
||||
$force = (true === $input->getOption('force'));
|
||||
if ($dumpSql && $force) {
|
||||
throw new \InvalidArgumentException('You can pass either the --dump-sql or the --force option (but not both simultaneously).');
|
||||
}
|
||||
|
||||
if ($dumpSql) {
|
||||
$output->writeln(implode(';' . PHP_EOL, $sqls));
|
||||
} else if ($force) {
|
||||
$output->writeln('Updating database schema...');
|
||||
$schemaTool->updateSchema($metadatas, $saveMode);
|
||||
$output->write('Database schema updated successfully!' . PHP_EOL);
|
||||
$output->writeln(sprintf('Database schema updated successfully! "<info>%s</info>" queries were executed', count($sqls)));
|
||||
} else {
|
||||
$output->write('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL);
|
||||
$output->write('Use the incremental update to detect changes during development and use' . PHP_EOL);
|
||||
$output->write('this SQL DDL to manually update your database in production.' . PHP_EOL . PHP_EOL);
|
||||
$output->writeln('<comment>ATTENTION</comment>: This operation should not be executed in a production environment.');
|
||||
$output->writeln(' Use the incremental update to detect changes during development and use');
|
||||
$output->writeln(' the SQL DDL provided to manually update your database in production.');
|
||||
$output->writeln('');
|
||||
|
||||
$sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode);
|
||||
|
||||
if (count($sqls)) {
|
||||
$output->write('Schema-Tool would execute ' . count($sqls) . ' queries to update the database.' . PHP_EOL);
|
||||
$output->write('Please run the operation with --force to execute these queries or use --dump-sql to see them.' . PHP_EOL);
|
||||
} else {
|
||||
$output->write('Nothing to update. The database is in sync with the current entity metadata.' . PHP_EOL);
|
||||
}
|
||||
$output->writeln(sprintf('The Schema-Tool would execute <info>"%s"</info> queries to update the database.', count($sqls)));
|
||||
$output->writeln('Please run the operation by passing one of the following options:');
|
||||
|
||||
$output->writeln(sprintf(' <info>%s --force</info> to execute the command', $this->getFullName()));
|
||||
$output->writeln(sprintf(' <info>%s --dump-sql</info> to dump the SQL statements to the screen', $this->getFullName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,6 +91,8 @@ class EntityGenerator
|
||||
|
||||
<namespace>
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
<entityAnnotation>
|
||||
<entityClassName>
|
||||
{
|
||||
@ -146,11 +148,18 @@ public function <methodName>()
|
||||
}
|
||||
';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (version_compare(\Doctrine\Common\Version::VERSION, '3.0.0-DEV', '>=')) {
|
||||
$this->_annotationsPrefix = 'ORM\\';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and write entity classes for the given array of ClassMetadataInfo instances
|
||||
*
|
||||
* @param array $metadatas
|
||||
* @param string $outputDirectory
|
||||
* @param string $outputDirectory
|
||||
* @return void
|
||||
*/
|
||||
public function generate(array $metadatas, $outputDirectory)
|
||||
@ -164,7 +173,7 @@ public function <methodName>()
|
||||
* Generated and write entity class to disk for the given ClassMetadataInfo instance
|
||||
*
|
||||
* @param ClassMetadataInfo $metadata
|
||||
* @param string $outputDirectory
|
||||
* @param string $outputDirectory
|
||||
* @return void
|
||||
*/
|
||||
public function writeEntityClass(ClassMetadataInfo $metadata, $outputDirectory)
|
||||
@ -201,7 +210,7 @@ public function <methodName>()
|
||||
/**
|
||||
* Generate a PHP5 Doctrine 2 entity class from the given ClassMetadataInfo instance
|
||||
*
|
||||
* @param ClassMetadataInfo $metadata
|
||||
* @param ClassMetadataInfo $metadata
|
||||
* @return string $code
|
||||
*/
|
||||
public function generateEntityClass(ClassMetadataInfo $metadata)
|
||||
@ -227,8 +236,8 @@ public function <methodName>()
|
||||
/**
|
||||
* Generate the updated code for the given ClassMetadataInfo and entity at path
|
||||
*
|
||||
* @param ClassMetadataInfo $metadata
|
||||
* @param string $path
|
||||
* @param ClassMetadataInfo $metadata
|
||||
* @param string $path
|
||||
* @return string $code;
|
||||
*/
|
||||
public function generateUpdatedEntityClass(ClassMetadataInfo $metadata, $path)
|
||||
@ -245,7 +254,7 @@ public function <methodName>()
|
||||
/**
|
||||
* Set the number of spaces the exported class should have
|
||||
*
|
||||
* @param integer $numSpaces
|
||||
* @param integer $numSpaces
|
||||
* @return void
|
||||
*/
|
||||
public function setNumSpaces($numSpaces)
|
||||
@ -257,7 +266,7 @@ public function <methodName>()
|
||||
/**
|
||||
* Set the extension to use when writing php files to disk
|
||||
*
|
||||
* @param string $extension
|
||||
* @param string $extension
|
||||
* @return void
|
||||
*/
|
||||
public function setExtension($extension)
|
||||
@ -278,7 +287,7 @@ public function <methodName>()
|
||||
/**
|
||||
* Set whether or not to generate annotations for the entity
|
||||
*
|
||||
* @param bool $bool
|
||||
* @param bool $bool
|
||||
* @return void
|
||||
*/
|
||||
public function setGenerateAnnotations($bool)
|
||||
@ -293,13 +302,16 @@ public function <methodName>()
|
||||
*/
|
||||
public function setAnnotationPrefix($prefix)
|
||||
{
|
||||
if (version_compare(\Doctrine\Common\Version::VERSION, '3.0.0-DEV', '>=')) {
|
||||
return;
|
||||
}
|
||||
$this->_annotationsPrefix = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not to try and update the entity if it already exists
|
||||
*
|
||||
* @param bool $bool
|
||||
* @param bool $bool
|
||||
* @return void
|
||||
*/
|
||||
public function setUpdateEntityIfExists($bool)
|
||||
@ -407,7 +419,7 @@ public function <methodName>()
|
||||
$tokens = token_get_all($src);
|
||||
$lastSeenNamespace = "";
|
||||
$lastSeenClass = false;
|
||||
|
||||
|
||||
$inNamespace = false;
|
||||
$inClass = false;
|
||||
for ($i = 0; $i < count($tokens); $i++) {
|
||||
@ -808,7 +820,7 @@ public function <methodName>()
|
||||
if ($associationMapping['isCascadeMerge']) $cascades[] = '"merge"';
|
||||
if ($associationMapping['isCascadeRefresh']) $cascades[] = '"refresh"';
|
||||
|
||||
$typeOptions[] = 'cascade={' . implode(',', $cascades) . '}';
|
||||
$typeOptions[] = 'cascade={' . implode(',', $cascades) . '}';
|
||||
}
|
||||
|
||||
if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']) {
|
||||
@ -862,7 +874,7 @@ public function <methodName>()
|
||||
$lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'OrderBy({';
|
||||
|
||||
foreach ($associationMapping['orderBy'] as $name => $direction) {
|
||||
$lines[] = $this->_spaces . ' * "' . $name . '"="' . $direction . '",';
|
||||
$lines[] = $this->_spaces . ' * "' . $name . '"="' . $direction . '",';
|
||||
}
|
||||
|
||||
$lines[count($lines) - 1] = substr($lines[count($lines) - 1], 0, strlen($lines[count($lines) - 1]) - 1);
|
||||
|
@ -652,6 +652,15 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
|
||||
$this->assertEquals(array('u'), $qb->getRootAliases());
|
||||
}
|
||||
|
||||
public function testGetRootEntities()
|
||||
{
|
||||
$qb = $this->_em->createQueryBuilder()
|
||||
->select('u')
|
||||
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u');
|
||||
|
||||
$this->assertEquals(array('Doctrine\Tests\Models\CMS\CmsUser'), $qb->getRootEntities());
|
||||
}
|
||||
|
||||
public function testGetSeveralRootAliases()
|
||||
{
|
||||
$qb = $this->_em->createQueryBuilder()
|
||||
|
Loading…
x
Reference in New Issue
Block a user