commit
d6d5c341e2
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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.
|
||||
|
128
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2084Test.php
Normal file
128
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2084Test.php
Normal 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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user