diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index 3fdbe171e..3213b8670 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -777,6 +777,7 @@ class BasicEntityPersister $criteria = array(); $sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']); if ($assoc['isOwningSide']) { + $quotedJoinTable = $sourceClass->getQuotedJoinTableName($assoc, $this->_platform); foreach ($assoc['relationToSourceKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) { if ($sourceClass->containsForeignIdentifier) { $field = $sourceClass->getFieldForColumn($sourceKeyColumn); @@ -785,9 +786,10 @@ class BasicEntityPersister $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); $value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]]; } - $criteria[$relationKeyColumn] = $value; + + $criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value; } else if (isset($sourceClass->fieldNames[$sourceKeyColumn])) { - $criteria[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); + $criteria[$quotedJoinTable . "." . $relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); } else { throw MappingException::joinColumnMustPointToMappedField( $sourceClass->name, $sourceKeyColumn @@ -796,6 +798,7 @@ class BasicEntityPersister } } else { $owningAssoc = $this->_em->getClassMetadata($assoc['targetEntity'])->associationMappings[$assoc['mappedBy']]; + $quotedJoinTable = $sourceClass->getQuotedJoinTableName($owningAssoc, $this->_platform); // TRICKY: since the association is inverted source and target are flipped foreach ($owningAssoc['relationToTargetKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) { if ($sourceClass->containsForeignIdentifier) { @@ -805,9 +808,9 @@ class BasicEntityPersister $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); $value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]]; } - $criteria[$relationKeyColumn] = $value; + $criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value; } else if (isset($sourceClass->fieldNames[$sourceKeyColumn])) { - $criteria[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); + $criteria[$quotedJoinTable . "." . $relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); } else { throw MappingException::joinColumnMustPointToMappedField( $sourceClass->name, $sourceKeyColumn @@ -1194,14 +1197,12 @@ class BasicEntityPersister } $conditionSql .= $this->_class->associationMappings[$field]['joinColumns'][0]['name']; - } else if ($assoc !== null) { - if ($assoc['type'] == ClassMetadata::MANY_TO_MANY) { - $owningAssoc = $assoc['isOwningSide'] ? $assoc : $this->_em->getClassMetadata($assoc['targetEntity']) - ->associationMappings[$assoc['mappedBy']]; - $conditionSql .= $this->_class->getQuotedJoinTableName($owningAssoc, $this->_platform) . '.' . $field; - } else { - $conditionSql .= $field; - } + } else if ($assoc !== null && strpos($field, " ") === false && strpos($field, "(") === false) { + // very careless developers could potentially open up this normally hidden api for userland attacks, + // therefore checking for spaces and function calls which are not allowed. + + // found a join column condition, not really a "field" + $conditionSql .= $field; } else { throw ORMException::unrecognizedField($field); } @@ -1254,6 +1255,11 @@ class BasicEntityPersister $criteria = array(); $owningAssoc = $this->_class->associationMappings[$assoc['mappedBy']]; $sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']); + + $tableAlias = isset($owningAssoc['inherited']) ? + $this->_getSQLTableAlias($owningAssoc['inherited']) + : $this->_getSQLTableAlias($this->_class->name); + foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) { if ($sourceClass->containsForeignIdentifier) { $field = $sourceClass->getFieldForColumn($sourceKeyColumn); @@ -1262,9 +1268,9 @@ class BasicEntityPersister $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); $value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]]; } - $criteria[$targetKeyColumn] = $value; + $criteria[$tableAlias . "." . $targetKeyColumn] = $value; } else { - $criteria[$targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); + $criteria[$tableAlias . "." . $targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); } } diff --git a/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php b/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php index 099afeb8a..0fbff503e 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php @@ -410,4 +410,29 @@ class ClassTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase $ref = $this->_em->getReference('Doctrine\Tests\Models\Company\CompanyManager', $manager->getId()); $this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $ref, "A proxy can be generated only if no subclasses exists for the requested reference."); } + + /** + * @group DDC-992 + */ + public function testGetSubClassManyToManyCollection() + { + $manager = new CompanyManager(); + $manager->setName('gblanco'); + $manager->setSalary(1234); + $manager->setTitle('Awesome!'); + $manager->setDepartment('IT'); + + $person = new CompanyPerson(); + $person->setName('friend'); + + $manager->addFriend($person); + + $this->_em->persist($manager); + $this->_em->persist($person); + $this->_em->flush(); + $this->_em->clear(); + + $manager = $this->_em->find('Doctrine\Tests\Models\Company\CompanyManager', $manager->getId()); + $this->assertEquals(1, count($manager->getFriends())); + } } diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC992Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC992Test.php new file mode 100644 index 000000000..36d9a392f --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC992Test.php @@ -0,0 +1,147 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC992Role'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC992Parent'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC992Child'), + )); + } catch(\Exception $e) { + + } + } + + public function testIssue() + { + $role = new DDC992Role(); + $role->name = "Parent"; + $child = new DDC992Role(); + $child->name = "child"; + + $role->extendedBy[] = $child; + $child->extends[] = $role; + + $this->_em->persist($role); + $this->_em->persist($child); + $this->_em->flush(); + $this->_em->clear(); + + $child = $this->_em->getRepository(get_class($role))->find($child->roleID); + $parents = count($child->extends); + $this->assertEquals(1, $parents); + foreach ($child->extends AS $parent) { + $this->assertEquals($role->getRoleID(), $parent->getRoleID()); + } + } + + public function testOneToManyChild() + { + $parent = new DDC992Parent(); + $child = new DDC992Child(); + $child->parent = $parent; + $parent->childs[] = $child; + + $this->_em->persist($parent); + $this->_em->persist($child); + $this->_em->flush(); + $this->_em->clear(); + + $parentRepository = $this->_em->getRepository(get_class($parent)); + $childRepository = $this->_em->getRepository(get_class($child)); + + $parent = $parentRepository->find($parent->id); + $this->assertEquals(1, count($parent->childs)); + $this->assertEquals(0, count($parent->childs[0]->childs())); + + $child = $parentRepository->findOneBy(array("id" => $child->id)); + $this->assertSame($parent->childs[0], $child); + + $this->_em->clear(); + + $child = $parentRepository->find($child->id); + $this->assertEquals(0, count($child->childs)); + + $this->_em->clear(); + + $child = $childRepository->find($child->id); + $this->assertEquals(0, count($child->childs)); + } +} + +/** + * @Entity + * @InheritanceType("JOINED") + * @DiscriminatorMap({"child" = "DDC992Child", "parent" = "DDC992Parent"}) + */ +class DDC992Parent +{ + /** @Id @GeneratedValue @Column(type="integer") */ + public $id; + /** @ManyToOne(targetEntity="DDC992Parent", inversedBy="childs") */ + public $parent; + /** @OneToMany(targetEntity="DDC992Child", mappedBy="parent") */ + public $childs; +} + +/** + * @Entity + */ +class DDC992Child extends DDC992Parent +{ + public function childs() + { + return $this->childs; + } +} + +/** + * @Entity + */ +class DDC992Role +{ + public function getRoleID() + { + return $this->roleID; + } + + /** + * @Id @Column(name="roleID", type="integer") + * @GeneratedValue(strategy="AUTO") + */ + public $roleID; + /** + * @Column (name="name", type="string", length="45") + */ + public $name; + /** + * @ManyToMany (targetEntity="DDC992Role", mappedBy="extends") + */ + public $extendedBy; + /** + * @ManyToMany (targetEntity="DDC992Role", inversedBy="extendedBy") + * @JoinTable (name="RoleRelations", + * joinColumns={@JoinColumn(name="roleID", referencedColumnName="roleID")}, + * inverseJoinColumns={@JoinColumn(name="extendsRoleID", referencedColumnName="roleID")} + * ) + */ + public $extends; + + public function __construct() { + $this->extends = new ArrayCollection; + $this->extendedBy = new ArrayCollection; + } +} \ No newline at end of file