1
0
mirror of synced 2025-02-20 22:23:14 +03:00

Merge branch 'DDC-2138'

This commit is contained in:
Benjamin Eberlei 2012-11-12 15:01:30 +01:00
commit 935842845b
4 changed files with 358 additions and 17 deletions

View File

@ -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
);
}
}
/**

View 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;
}
}

View File

@ -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;
}
}

View File

@ -132,4 +132,4 @@ class GenerateSchemaEventListener
{
$this->schemaCalled = true;
}
}
}