1
0
mirror of synced 2024-12-13 14:56:01 +03:00

Merge pull request #486 from FabioBatSilva/DDC-2084

Fix DDC-2084
This commit is contained in:
Guilherme Blanco 2012-11-05 16:38:35 -08:00
commit d6d5c341e2
5 changed files with 185 additions and 35 deletions

View File

@ -26,6 +26,7 @@ use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\ORMInvalidArgumentException;
/**
* Base contract for ORM queries. Base class for Query and NativeQuery.
@ -247,44 +248,21 @@ abstract class AbstractQuery
*/
public function processParameterValue($value)
{
switch (true) {
case is_array($value):
foreach ($value as $key => $paramValue) {
$paramValue = $this->processParameterValue($paramValue);
$value[$key] = is_array($paramValue) ? $paramValue[key($paramValue)] : $paramValue;
}
if (is_array($value)) {
foreach ($value as $key => $paramValue) {
$paramValue = $this->processParameterValue($paramValue);
$value[$key] = is_array($paramValue) ? reset($paramValue) : $paramValue;
}
return $value;
case is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value)):
return $this->convertObjectParameterToScalarValue($value);
default:
return $value;
}
}
private function convertObjectParameterToScalarValue($value)
{
$class = $this->_em->getClassMetadata(get_class($value));
if ($class->isIdentifierComposite) {
throw new \InvalidArgumentException(
"Binding an entity with a composite primary key to a query is not supported. " .
"You should split the parameter into the explicit fields and bind them seperately."
);
return $value;
}
$values = ($this->_em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED)
? $this->_em->getUnitOfWork()->getEntityIdentifier($value)
: $class->getIdentifierValues($value);
if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) {
$value = $this->_em->getUnitOfWork()->getSingleIdentifierValue($value);
$value = $values[$class->getSingleIdentifierFieldName()];
if (null === $value) {
throw new \InvalidArgumentException(
"Binding entities to query parameters only allowed for entities that have an identifier."
);
if ($value === null) {
throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
}
}
return $value;

View File

@ -29,6 +29,7 @@ use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\Proxy\ProxyFactory;
use Doctrine\ORM\Query\FilterCollection;
use Doctrine\Common\Util\ClassUtils;
/**
* The EntityManager is the central access point to ORM functionality.
@ -354,7 +355,7 @@ class EntityManager implements ObjectManager
$this->unitOfWork->commit($entity);
}
/**
* Finds an Entity by its identifier.
*
@ -369,6 +370,14 @@ class EntityManager implements ObjectManager
{
$class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
if (is_object($id) && $this->metadataFactory->hasMetadataFor(ClassUtils::getClass($id))) {
$id = $this->unitOfWork->getSingleIdentifierValue($id);
if ($id === null) {
throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
}
}
if ( ! is_array($id)) {
$id = array($class->identifier[0] => $id);
}

View File

@ -102,6 +102,17 @@ class ORMInvalidArgumentException extends \InvalidArgumentException
' to be an entity object, '. gettype($given) . ' given.');
}
public static function invalidCompositeIdentifier()
{
return new self("Binding an entity with a composite primary key to a query is not supported. " .
"You should split the parameter into the explicit fields and bind them seperately.");
}
public static function invalidIdentifierBindingEntity()
{
return new self("Binding entities to query parameters only allowed for entities that have an identifier.");
}
/**
* Helper method to show an object as string.
*

View File

@ -2737,6 +2737,30 @@ class UnitOfWork implements PropertyChangedListener
return $this->entityIdentifiers[spl_object_hash($entity)];
}
/**
* Process an entity instance to extract their identifier values.
*
* @param object $entity The entity instance.
*
* @return scalar
*
* @throws \Doctrine\ORM\ORMInvalidArgumentException
*/
public function getSingleIdentifierValue($entity)
{
$class = $this->em->getClassMetadata(get_class($entity));
if ($class->isIdentifierComposite) {
throw ORMInvalidArgumentException::invalidCompositeIdentifier();
}
$values = ($this->getEntityState($entity) === UnitOfWork::STATE_MANAGED)
? $this->getEntityIdentifier($entity)
: $class->getIdentifierValues($entity);
return isset($values[$class->identifier[0]]) ? $values[$class->identifier[0]] : null;
}
/**
* Tries to find an entity with the given identifier in the identity map of
* this UnitOfWork.

View File

@ -0,0 +1,128 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
/**
* @group DDC-2084
*/
class DDC2084Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2084\MyEntity1'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2084\MyEntity2'),
));
} catch (\Exception $exc) {
}
}
public function loadFixture()
{
$e2 = new DDC2084\MyEntity2('Foo');
$e1 = new DDC2084\MyEntity1($e2);
$this->_em->persist($e2);
$this->_em->flush();
$this->_em->persist($e1);
$this->_em->flush();
$this->_em->clear();
return $e1;
}
public function testIssue()
{
$e1 = $this->loadFixture();
$e2 = $e1->getMyEntity2();
$e = $this->_em->find(__NAMESPACE__ . '\DDC2084\MyEntity1', $e2);
$this->assertInstanceOf(__NAMESPACE__ . '\DDC2084\MyEntity1', $e);
$this->assertInstanceOf(__NAMESPACE__ . '\DDC2084\MyEntity2', $e->getMyEntity2());
$this->assertEquals('Foo', $e->getMyEntity2()->getValue());
}
/**
* @expectedException \Doctrine\ORM\ORMInvalidArgumentException
* @expectedExceptionMessage Binding entities to query parameters only allowed for entities that have an identifier.
*/
public function testinvalidIdentifierBindingEntityException()
{
$this->_em->find(__NAMESPACE__ . '\DDC2084\MyEntity1', new DDC2084\MyEntity2('Foo'));
}
}
namespace Doctrine\Tests\ORM\Functional\Ticket\DDC2084;
/**
* @Entity
* @Table(name="DDC2084_ENTITY1")
*/
class MyEntity1
{
/**
* @Id
* @OneToOne(targetEntity="MyEntity2")
* @JoinColumn(name="entity2_id", referencedColumnName="id", nullable=false)
*/
private $entity2;
public function __construct(MyEntity2 $myEntity2)
{
$this->entity2 = $myEntity2;
}
public function setMyEntity2(MyEntity2 $myEntity2)
{
$this->entity2 = $myEntity2;
}
public function getMyEntity2()
{
return $this->entity2;
}
}
/**
* @Entity
* @Table(name="DDC2084_ENTITY2")
*/
class MyEntity2
{
/**
* @Id
* @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @Column
*/
private $value;
public function __construct($value)
{
$this->value = $value;
}
public function getId()
{
return $this->id;
}
public function getValue()
{
return $this->value;
}
public function setValue($value)
{
$this->value = $value;
}
}