Skip to content

Commit e5e3166

Browse files
committed
#11090 - Fix obtaining an identifier in cases where the hydration has not yet fully completed on eagerLoadCollections
1 parent 1d218ba commit e5e3166

File tree

8 files changed

+271
-1
lines changed

8 files changed

+271
-1
lines changed

src/UnitOfWork.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3252,7 +3252,17 @@ private function eagerLoadCollections(array $collections, array $mapping): void
32523252
foreach ($found as $targetValue) {
32533253
$sourceEntity = $targetProperty->getValue($targetValue);
32543254

3255-
$id = $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($sourceEntity));
3255+
// In cases where the hydration $targetValue has not yet fully completed
3256+
if ($sourceEntity === null && isset($targetClass->associationMappings[$mappedBy]['joinColumns'])) {
3257+
$data = $this->getOriginalEntityData($targetValue);
3258+
$id = [];
3259+
foreach ($targetClass->associationMappings[$mappedBy]['joinColumns'] as $joinColumn) {
3260+
$id[] = $data[$joinColumn['name']];
3261+
}
3262+
} else {
3263+
$id = $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($sourceEntity));
3264+
}
3265+
32563266
$idHash = implode(' ', $id);
32573267

32583268
if (isset($mapping['indexBy'])) {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Tests\Models\AbstractFetchEager\WithFetchEager;
6+
7+
use Doctrine\Common\Collections\ArrayCollection;
8+
use Doctrine\Common\Collections\Collection;
9+
use Doctrine\ORM\Mapping as ORM;
10+
11+
/**
12+
* @ORM\Entity()
13+
* @ORM\InheritanceType("SINGLE_TABLE")
14+
* @ORM\DiscriminatorColumn(name="type", type="string")
15+
* @ORM\DiscriminatorMap({"mobile"="MobileRemoteControl"})
16+
*/
17+
abstract class AbstractRemoveControl
18+
{
19+
/**
20+
* @ORM\Id
21+
* @ORM\GeneratedValue
22+
* @ORM\Column(type="integer")
23+
*
24+
* @var int
25+
*/
26+
public $id;
27+
28+
/**
29+
* /**
30+
*
31+
* @ORM\Column(type="string")
32+
*
33+
* @var string
34+
*/
35+
public $name;
36+
37+
/**
38+
* @ORM\OneToMany(targetEntity="User", mappedBy="remoteControl", fetch="EAGER")
39+
*
40+
* @var Collection<User>
41+
*/
42+
public $users;
43+
44+
public function __construct(string $name)
45+
{
46+
$this->name = $name;
47+
$this->users = new ArrayCollection();
48+
}
49+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Tests\Models\AbstractFetchEager\WithFetchEager;
6+
7+
use Doctrine\ORM\Mapping as ORM;
8+
9+
/**
10+
* @ORM\Entity()
11+
*/
12+
class MobileRemoteControl extends AbstractRemoveControl
13+
{
14+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Tests\Models\AbstractFetchEager\WithFetchEager;
6+
7+
use Doctrine\ORM\Mapping as ORM;
8+
9+
/**
10+
* @ORM\Entity()
11+
*/
12+
class User
13+
{
14+
/**
15+
* @ORM\Id
16+
* @ORM\GeneratedValue
17+
* @ORM\Column(type="integer")
18+
*
19+
* @var int
20+
*/
21+
public $id;
22+
23+
/**
24+
* @ORM\ManyToOne(targetEntity="AbstractRemoveControl", inversedBy="users")
25+
* @ORM\JoinColumn(nullable=false)
26+
*
27+
* @var AbstractRemoveControl
28+
*/
29+
public $remoteControl;
30+
31+
public function __construct(AbstractRemoveControl $control)
32+
{
33+
$this->remoteControl = $control;
34+
}
35+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Tests\Models\AbstractFetchEager\WithoutFetchEager;
6+
7+
use Doctrine\Common\Collections\ArrayCollection;
8+
use Doctrine\Common\Collections\Collection;
9+
use Doctrine\ORM\Mapping as ORM;
10+
11+
/**
12+
* @ORM\Entity()
13+
* @ORM\InheritanceType("SINGLE_TABLE")
14+
* @ORM\DiscriminatorColumn(name="type", type="string")
15+
* @ORM\DiscriminatorMap({"mobile"="MobileRemoteControl"})
16+
*/
17+
abstract class AbstractRemoveControl
18+
{
19+
/**
20+
* @ORM\Id
21+
* @ORM\GeneratedValue
22+
* @ORM\Column(type="integer")
23+
*
24+
* @var int
25+
*/
26+
public $id;
27+
28+
/**
29+
* /**
30+
*
31+
* @ORM\Column(type="string")
32+
*
33+
* @var string
34+
*/
35+
public $name;
36+
37+
/**
38+
* @ORM\OneToMany(targetEntity="User", mappedBy="remoteControl")
39+
*
40+
* @var Collection<User>
41+
*/
42+
public $users;
43+
44+
public function __construct(string $name)
45+
{
46+
$this->name = $name;
47+
$this->users = new ArrayCollection();
48+
}
49+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Tests\Models\AbstractFetchEager\WithoutFetchEager;
6+
7+
use Doctrine\ORM\Mapping as ORM;
8+
9+
/**
10+
* @ORM\Entity()
11+
*/
12+
class MobileRemoteControl extends AbstractRemoveControl
13+
{
14+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Tests\Models\AbstractFetchEager\WithoutFetchEager;
6+
7+
use Doctrine\ORM\Mapping as ORM;
8+
9+
/**
10+
* @ORM\Entity()
11+
*/
12+
class User
13+
{
14+
/**
15+
* @ORM\Id
16+
* @ORM\GeneratedValue
17+
* @ORM\Column(type="integer")
18+
*
19+
* @var int
20+
*/
21+
public $id;
22+
23+
/**
24+
* @ORM\ManyToOne(targetEntity="AbstractRemoveControl", inversedBy="users")
25+
* @ORM\JoinColumn(nullable=false)
26+
*
27+
* @var AbstractRemoveControl
28+
*/
29+
public $remoteControl;
30+
31+
public function __construct(AbstractRemoveControl $control)
32+
{
33+
$this->remoteControl = $control;
34+
}
35+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Tests\ORM\Functional;
6+
7+
use Doctrine\Tests\Models\AbstractFetchEager\WithFetchEager\AbstractRemoveControl;
8+
use Doctrine\Tests\Models\AbstractFetchEager\WithFetchEager\MobileRemoteControl;
9+
use Doctrine\Tests\Models\AbstractFetchEager\WithFetchEager\User;
10+
use Doctrine\Tests\Models\AbstractFetchEager\WithoutFetchEager\AbstractRemoveControl as AbstractRemoveControlWithoutFetchEager;
11+
use Doctrine\Tests\Models\AbstractFetchEager\WithoutFetchEager\MobileRemoteControl as MobileRemoteControlWithoutFetchEager;
12+
use Doctrine\Tests\Models\AbstractFetchEager\WithoutFetchEager\User as UserWithoutFetchEager;
13+
use Doctrine\Tests\OrmFunctionalTestCase;
14+
15+
final class AbstractFetchEagerTest extends OrmFunctionalTestCase
16+
{
17+
public function testWithAbstractFetchEager(): void
18+
{
19+
$this->createSchemaForModels(
20+
AbstractRemoveControl::class,
21+
User::class
22+
);
23+
24+
$control = new MobileRemoteControl('smart');
25+
$user = new User($control);
26+
27+
$entityManage = $this->getEntityManager();
28+
29+
$entityManage->persist($control);
30+
$entityManage->persist($user);
31+
$entityManage->flush();
32+
$entityManage->clear();
33+
34+
$user = $entityManage->find(User::class, $user->id);
35+
36+
self::assertNotNull($user);
37+
self::assertEquals('smart', $user->remoteControl->name);
38+
self::assertTrue($user->remoteControl->users->contains($user));
39+
}
40+
41+
public function testWithoutAbstractFetchEager(): void
42+
{
43+
$this->createSchemaForModels(
44+
AbstractRemoveControlWithoutFetchEager::class,
45+
UserWithoutFetchEager::class
46+
);
47+
48+
$control = new MobileRemoteControlWithoutFetchEager('smart');
49+
$user = new UserWithoutFetchEager($control);
50+
51+
$entityManage = $this->getEntityManager();
52+
53+
$entityManage->persist($control);
54+
$entityManage->persist($user);
55+
$entityManage->flush();
56+
$entityManage->clear();
57+
58+
$user = $entityManage->find(UserWithoutFetchEager::class, $user->id);
59+
60+
self::assertNotNull($user);
61+
self::assertEquals('smart', $user->remoteControl->name);
62+
self::assertTrue($user->remoteControl->users->contains($user));
63+
}
64+
}

0 commit comments

Comments
 (0)