Skip to content

Commit

Permalink
Fix ambiguous case where an entity is also a Traversable (#8371)
Browse files Browse the repository at this point in the history
* Fix ambiguous case where an entity is also a Traversable

* Address phpcs violations.

* Address phpcs violations.

* Address phpcs violations.

Co-authored-by: Laurent VOULLEMIER <laurent.voullemier@gmail.com>
  • Loading branch information
beberlei and l-vo authored Dec 4, 2020
1 parent da225a0 commit 242cf1a
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 6 deletions.
46 changes: 41 additions & 5 deletions lib/Doctrine/ORM/AbstractQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
namespace Doctrine\ORM;

use Countable;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\ORM\Mapping\MappingException as ORMMappingException;
Expand Down Expand Up @@ -421,15 +422,12 @@ public function processParameterValue($value)
return $value;
}

if ($value instanceof Traversable) {
if ($value instanceof Collection) {
$value = iterator_to_array($value);
}

if (is_array($value)) {
foreach ($value as $key => $paramValue) {
$paramValue = $this->processParameterValue($paramValue);
$value[$key] = is_array($paramValue) ? reset($paramValue) : $paramValue;
}
$value = $this->processArrayParameterValue($value);

return $value;
}
Expand All @@ -452,9 +450,47 @@ public function processParameterValue($value)
// Silence any mapping exceptions. These can occur if the object in
// question is not a mapped entity, in which case we just don't do
// any preparation on the value.

$value = $this->potentiallyProcessIterable($value);
} catch (MappingException $e) {
// as previous, but depending on MappingDriver this exception from Persistence
// is thrown and not the ORM one.

$value = $this->potentiallyProcessIterable($value);
}

return $value;
}

/**
* If no mapping is detected, trying to resolve the value as a Traversable
*
* @param mixed $value
*
* @return mixed
*/
private function potentiallyProcessIterable($value)
{
if ($value instanceof Traversable) {
$value = iterator_to_array($value);
$value = $this->processArrayParameterValue($value);
}

return $value;
}

/**
* Process a parameter value which was previously identified as an array
*
* @param mixed[] $value
*
* @return mixed[]
*/
private function processArrayParameterValue(array $value): array
{
foreach ($value as $key => $paramValue) {
$paramValue = $this->processParameterValue($paramValue);
$value[$key] = is_array($paramValue) ? reset($paramValue) : $paramValue;
}

return $value;
Expand Down
19 changes: 18 additions & 1 deletion tests/Doctrine/Tests/Models/CMS/CmsGroup.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@

namespace Doctrine\Tests\Models\CMS;

use Doctrine\Common\Collections\ArrayCollection;
use IteratorAggregate;
use Traversable;

/**
* Description of CmsGroup
*
* @author robo
* @Entity
* @Table(name="cms_groups")
*/
class CmsGroup
class CmsGroup implements IteratorAggregate
{
/**
* @Id
Expand All @@ -26,6 +30,11 @@ class CmsGroup
*/
public $users;

public function __construct()
{
$this->users = new ArrayCollection();
}

public function setName($name) {
$this->name = $name;
}
Expand All @@ -41,5 +50,13 @@ public function addUser(CmsUser $user) {
public function getUsers() {
return $this->users;
}

/**
* @return ArrayCollection|Traversable
*/
public function getIterator()
{
return $this->getUsers();
}
}

10 changes: 10 additions & 0 deletions tests/Doctrine/Tests/ORM/Query/QueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Doctrine\Tests\Mocks\EntityManagerMock;
use Doctrine\Tests\Mocks\StatementArrayMock;
use Doctrine\Tests\Models\CMS\CmsAddress;
use Doctrine\Tests\Models\CMS\CmsGroup;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\Generic\DateTimeModel;
use Doctrine\Tests\OrmTestCase;
Expand Down Expand Up @@ -240,6 +241,15 @@ public function testProcessParameterValueIterable(iterable $cities) : void
);
}

public function testProcessParameterValueWithIterableEntityShouldNotBeTreatedAsIterable(): void
{
$group = new CmsGroup();
$group->id = 1;

$query = $this->_em->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.group IN (:group)');
self::assertEquals(1, $query->processParameterValue($group));
}

/**
* @group DDC-2224
*/
Expand Down

0 comments on commit 242cf1a

Please sign in to comment.