Merge branch 'DDC-2138'
This commit is contained in:
commit
935842845b
@ -42,6 +42,7 @@ use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Stefano Rodriguez <stefano.rodriguez@fubles.com>
|
||||
*/
|
||||
class SchemaTool
|
||||
{
|
||||
@ -142,6 +143,9 @@ class SchemaTool
|
||||
$metadataSchemaConfig->setExplicitForeignKeyIndexes(false);
|
||||
$schema = new Schema(array(), array(), $metadataSchemaConfig);
|
||||
|
||||
$addedFks = array();
|
||||
$blacklistedFks = array();
|
||||
|
||||
foreach ($classes as $class) {
|
||||
if ($this->processingNotRequired($class, $processedClasses)) {
|
||||
continue;
|
||||
@ -150,8 +154,8 @@ class SchemaTool
|
||||
$table = $schema->createTable($this->quoteStrategy->getTableName($class, $this->platform));
|
||||
|
||||
if ($class->isInheritanceTypeSingleTable()) {
|
||||
$this->gatherColumns($class, $table);
|
||||
$this->gatherRelationsSql($class, $table, $schema);
|
||||
$columns = $this->gatherColumns($class, $table);
|
||||
$this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks);
|
||||
|
||||
// Add the discriminator column
|
||||
$this->addDiscriminatorColumnDefinition($class, $table);
|
||||
@ -165,7 +169,7 @@ class SchemaTool
|
||||
foreach ($class->subClasses as $subClassName) {
|
||||
$subClass = $this->em->getClassMetadata($subClassName);
|
||||
$this->gatherColumns($subClass, $table);
|
||||
$this->gatherRelationsSql($subClass, $table, $schema);
|
||||
$this->gatherRelationsSql($subClass, $table, $schema, $addedFks, $blacklistedFks);
|
||||
$processedClasses[$subClassName] = true;
|
||||
}
|
||||
} elseif ($class->isInheritanceTypeJoined()) {
|
||||
@ -182,7 +186,7 @@ class SchemaTool
|
||||
}
|
||||
}
|
||||
|
||||
$this->gatherRelationsSql($class, $table, $schema);
|
||||
$this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks);
|
||||
|
||||
// Add the discriminator column only to the root table
|
||||
if ($class->name == $class->rootEntityName) {
|
||||
@ -211,7 +215,7 @@ class SchemaTool
|
||||
throw ORMException::notSupported();
|
||||
} else {
|
||||
$this->gatherColumns($class, $table);
|
||||
$this->gatherRelationsSql($class, $table, $schema);
|
||||
$this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks);
|
||||
}
|
||||
|
||||
$pkColumns = array();
|
||||
@ -432,9 +436,11 @@ class SchemaTool
|
||||
* @param ClassMetadata $class
|
||||
* @param \Doctrine\DBAL\Schema\Table $table
|
||||
* @param \Doctrine\DBAL\Schema\Schema $schema
|
||||
* @param array $addedFks
|
||||
* @param array $blacklistedFks
|
||||
* @return void
|
||||
*/
|
||||
private function gatherRelationsSql($class, Table $table, $schema)
|
||||
private function gatherRelationsSql($class, $table, $schema, &$addedFks, &$blacklistedFks)
|
||||
{
|
||||
foreach ($class->associationMappings as $mapping) {
|
||||
if (isset($mapping['inherited'])) {
|
||||
@ -446,7 +452,7 @@ class SchemaTool
|
||||
if ($mapping['type'] & ClassMetadata::TO_ONE && $mapping['isOwningSide']) {
|
||||
$primaryKeyColumns = $uniqueConstraints = array(); // PK is unnecessary for this relation-type
|
||||
|
||||
$this->_gatherRelationJoinColumns($mapping['joinColumns'], $table, $foreignClass, $mapping, $primaryKeyColumns, $uniqueConstraints);
|
||||
$this->_gatherRelationJoinColumns($mapping['joinColumns'], $table, $foreignClass, $mapping, $primaryKeyColumns, $uniqueConstraints, $addedFks, $blacklistedFks);
|
||||
|
||||
foreach($uniqueConstraints as $indexName => $unique) {
|
||||
$table->addUniqueIndex($unique['columns'], is_numeric($indexName) ? null : $indexName);
|
||||
@ -463,10 +469,10 @@ class SchemaTool
|
||||
$primaryKeyColumns = $uniqueConstraints = array();
|
||||
|
||||
// Build first FK constraint (relation table => source table)
|
||||
$this->_gatherRelationJoinColumns($joinTable['joinColumns'], $theJoinTable, $class, $mapping, $primaryKeyColumns, $uniqueConstraints);
|
||||
$this->_gatherRelationJoinColumns($joinTable['joinColumns'], $theJoinTable, $class, $mapping, $primaryKeyColumns, $uniqueConstraints, $addedFks, $blacklistedFks);
|
||||
|
||||
// Build second FK constraint (relation table => target table)
|
||||
$this->_gatherRelationJoinColumns($joinTable['inverseJoinColumns'], $theJoinTable, $foreignClass, $mapping, $primaryKeyColumns, $uniqueConstraints);
|
||||
$this->_gatherRelationJoinColumns($joinTable['inverseJoinColumns'], $theJoinTable, $foreignClass, $mapping, $primaryKeyColumns, $uniqueConstraints, $addedFks, $blacklistedFks);
|
||||
|
||||
$theJoinTable->setPrimaryKey($primaryKeyColumns);
|
||||
|
||||
@ -522,8 +528,10 @@ class SchemaTool
|
||||
* @param array $mapping
|
||||
* @param array $primaryKeyColumns
|
||||
* @param array $uniqueConstraints
|
||||
* @param array $addedFks
|
||||
* @param array $blacklistedFks
|
||||
*/
|
||||
private function _gatherRelationJoinColumns($joinColumns, $theJoinTable, $class, $mapping, &$primaryKeyColumns, &$uniqueConstraints)
|
||||
private function _gatherRelationJoinColumns($joinColumns, $theJoinTable, $class, $mapping, &$primaryKeyColumns, &$uniqueConstraints, &$addedFks, &$blacklistedFks)
|
||||
{
|
||||
$localColumns = array();
|
||||
$foreignColumns = array();
|
||||
@ -587,9 +595,27 @@ class SchemaTool
|
||||
}
|
||||
}
|
||||
|
||||
$theJoinTable->addForeignKeyConstraint(
|
||||
$foreignTableName, $localColumns, $foreignColumns, $fkOptions
|
||||
);
|
||||
$compositeName = $theJoinTable->getName().'.'.implode('', $localColumns);
|
||||
if (isset($addedFks[$compositeName])
|
||||
&& ($foreignTableName != $addedFks[$compositeName]['foreignTableName']
|
||||
|| 0 < count(array_diff($foreignColumns, $addedFks[$compositeName]['foreignColumns'])))
|
||||
) {
|
||||
foreach ($theJoinTable->getForeignKeys() as $fkName => $key) {
|
||||
if (0 === count(array_diff($key->getLocalColumns(), $localColumns))
|
||||
&& (($key->getForeignTableName() != $foreignTableName)
|
||||
|| 0 < count(array_diff($key->getForeignColumns(), $foreignColumns)))
|
||||
) {
|
||||
$theJoinTable->removeForeignKey($fkName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
$blacklistedFks[$compositeName] = true;
|
||||
} elseif (!isset($blacklistedFks[$compositeName])) {
|
||||
$addedFks[$compositeName] = array('foreignTableName' => $foreignTableName, 'foreignColumns' => $foreignColumns);
|
||||
$theJoinTable->addUnnamedForeignKeyConstraint(
|
||||
$foreignTableName, $localColumns, $foreignColumns, $fkOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
317
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2138Test.php
Normal file
317
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2138Test.php
Normal file
@ -0,0 +1,317 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
use Doctrine\ORM\Tools\SchemaTool;
|
||||
|
||||
class DDC2138Test extends OrmFunctionalTestCase
|
||||
{
|
||||
/**
|
||||
* @group DDC-2138
|
||||
*/
|
||||
public function testForeignKeyOnSTIWithMultipleMapping()
|
||||
{
|
||||
$em = $this->_em;
|
||||
$schemaTool = new SchemaTool($em);
|
||||
|
||||
$classes = array(
|
||||
$em->getClassMetadata(__NAMESPACE__ . '\DDC2138User'),
|
||||
$em->getClassMetadata(__NAMESPACE__ . '\DDC2138Structure'),
|
||||
$em->getClassMetadata(__NAMESPACE__ . '\DDC2138UserFollowedObject'),
|
||||
$em->getClassMetadata(__NAMESPACE__ . '\DDC2138UserFollowedStructure'),
|
||||
$em->getClassMetadata(__NAMESPACE__ . '\DDC2138UserFollowedUser')
|
||||
);
|
||||
|
||||
$schema = $schemaTool->getSchemaFromMetadata($classes);
|
||||
$this->assertTrue($schema->hasTable('users_followed_objects'), "Table users_followed_objects should exist.");
|
||||
|
||||
/* @var $table \Doctrine\DBAL\Schema\Table */
|
||||
$table = ($schema->getTable('users_followed_objects'));
|
||||
$this->assertTrue($table->columnsAreIndexed(array('object_id')));
|
||||
$this->assertTrue($table->columnsAreIndexed(array('user_id')));
|
||||
$foreignKeys = $table->getForeignKeys();
|
||||
$this->assertCount(1, $foreignKeys, 'user_id column has to have FK, but not object_id');
|
||||
|
||||
/* @var $fk \Doctrine\DBAL\Schema\ForeignKeyConstraint */
|
||||
$fk = reset($foreignKeys);
|
||||
$this->assertEquals('users', $fk->getForeignTableName());
|
||||
|
||||
$localColumns = $fk->getLocalColumns();
|
||||
$this->assertContains('user_id', $localColumns);
|
||||
$this->assertCount(1, $localColumns);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @Table(name="structures")
|
||||
* @Entity
|
||||
*/
|
||||
class DDC2138Structure
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="integer")
|
||||
* @GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @Column(type="string", length=32, nullable=true)
|
||||
*/
|
||||
protected $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="users_followed_objects")
|
||||
* @InheritanceType("SINGLE_TABLE")
|
||||
* @DiscriminatorColumn(name="object_type", type="smallint")
|
||||
* @DiscriminatorMap({4 = "DDC2138UserFollowedUser", 3 = "DDC2138UserFollowedStructure"})
|
||||
*/
|
||||
abstract class DDC2138UserFollowedObject
|
||||
{
|
||||
/**
|
||||
* @var integer $id
|
||||
*
|
||||
* @Column(name="id", type="integer")
|
||||
* @Id
|
||||
* @GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* Get id
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC2138UserFollowedStructure extends DDC2138UserFollowedObject
|
||||
{
|
||||
/**
|
||||
* @ManyToOne(targetEntity="DDC2138User", inversedBy="followedStructures")
|
||||
* @JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
|
||||
* @var User $user
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="DDC2138Structure")
|
||||
* @JoinColumn(name="object_id", referencedColumnName="id", nullable=false)
|
||||
* @var Structure $followedStructure
|
||||
*/
|
||||
private $followedStructure;
|
||||
|
||||
/**
|
||||
* Construct a UserFollowedStructure entity
|
||||
*
|
||||
* @param User $user
|
||||
* @param Structure $followedStructure
|
||||
*/
|
||||
public function __construct(User $user, Structure $followedStructure)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->followedStructure = $followedStructure;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets followed structure
|
||||
*
|
||||
* @return Structure
|
||||
*/
|
||||
public function getFollowedStructure()
|
||||
{
|
||||
return $this->followedStructure;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC2138UserFollowedUser extends DDC2138UserFollowedObject
|
||||
{
|
||||
/**
|
||||
* @ManyToOne(targetEntity="DDC2138User", inversedBy="followedUsers")
|
||||
* @JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
|
||||
* @var User $user
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="DDC2138User")
|
||||
* @JoinColumn(name="object_id", referencedColumnName="id", nullable=false)
|
||||
* @var User $user
|
||||
*/
|
||||
private $followedUser;
|
||||
|
||||
/**
|
||||
* Construct a UserFollowedUser entity
|
||||
*
|
||||
* @param User $user
|
||||
* @param User $followedUser
|
||||
* @param bool $giveAgency
|
||||
*/
|
||||
public function __construct(User $user, User $followedUser)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->followedUser = $followedUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets followed user
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function getFollowedUser()
|
||||
{
|
||||
return $this->followedUser;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Table(name="users")
|
||||
* @Entity
|
||||
*/
|
||||
class DDC2138User
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="integer")
|
||||
* @GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @Column(type="string", length=32, nullable=true)
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var ArrayCollection $followedUsers
|
||||
* @OneToMany(targetEntity="DDC2138UserFollowedUser", mappedBy="user", cascade={"persist"}, orphanRemoval=true)
|
||||
*/
|
||||
protected $followedUsers;
|
||||
|
||||
/**
|
||||
* @var ArrayCollection $followedStructures
|
||||
* @OneToMany(targetEntity="DDC2138UserFollowedStructure", mappedBy="user", cascade={"persist"}, orphanRemoval=true)
|
||||
*/
|
||||
protected $followedStructures;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->followedUsers = new ArrayCollection();
|
||||
$this->followedStructures = new ArrayCollection();
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove followers
|
||||
*
|
||||
* @param UserFollowedUser $followers
|
||||
*/
|
||||
private function removeFollower(UserFollowedUser $followers)
|
||||
{
|
||||
$this->followers->removeElement($followers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add followedUsers
|
||||
*
|
||||
* @param UserFollowedUser $followedUsers
|
||||
* @return User
|
||||
*/
|
||||
public function addFollowedUser(UserFollowedUser $followedUsers)
|
||||
{
|
||||
$this->followedUsers[] = $followedUsers;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove followedUsers
|
||||
*
|
||||
* @param UserFollowedUser $followedUsers
|
||||
* @return User
|
||||
*/
|
||||
public function removeFollowedUser(UserFollowedUser $followedUsers)
|
||||
{
|
||||
$this->followedUsers->removeElement($followedUsers);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get followedUsers
|
||||
*
|
||||
* @return Doctrine\Common\Collections\Collection
|
||||
*/
|
||||
public function getFollowedUsers()
|
||||
{
|
||||
return $this->followedUsers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add followedStructures
|
||||
*
|
||||
* @param UserFollowedStructure $followedStructures
|
||||
* @return User
|
||||
*/
|
||||
public function addFollowedStructure(UserFollowedStructure $followedStructures)
|
||||
{
|
||||
$this->followedStructures[] = $followedStructures;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove followedStructures
|
||||
*
|
||||
* @param UserFollowedStructure $followedStructures
|
||||
* @return User
|
||||
*/
|
||||
public function removeFollowedStructure(UserFollowedStructure $followedStructures)
|
||||
{
|
||||
$this->followedStructures->removeElement($followedStructures);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get followedStructures
|
||||
*
|
||||
* @return Doctrine\Common\Collections\Collection
|
||||
*/
|
||||
public function getFollowedStructures()
|
||||
{
|
||||
return $this->followedStructures;
|
||||
}
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
<?php
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
require_once __DIR__ . '/../../../TestInit.php';
|
||||
|
||||
class DDC258Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
@ -139,4 +137,4 @@ class DDC258Class3 extends DDC258Super
|
||||
* @Column(name="content", type="string", length=500)
|
||||
*/
|
||||
public $bananas;
|
||||
}
|
||||
}
|
||||
|
@ -132,4 +132,4 @@ class GenerateSchemaEventListener
|
||||
{
|
||||
$this->schemaCalled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user