Skip to content

Commit 9fe8ce4

Browse files
authored
Merge pull request #12373 from tomme87/12225-fix-hydration-issue
12225 Fix hydration issue when using indexBy, SQL filter, and inheritance mapping
1 parent a46ff16 commit 9fe8ce4

6 files changed

Lines changed: 216 additions & 1 deletion

File tree

src/Persisters/Entity/AbstractEntityInheritancePersister.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,21 @@ protected function getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r'
4545
{
4646
$tableAlias = $alias === 'r' ? '' : $alias;
4747
$fieldMapping = $class->fieldMappings[$field];
48-
$columnAlias = $this->getSQLColumnAlias($fieldMapping['columnName']);
4948
$sql = sprintf(
5049
'%s.%s',
5150
$this->getSQLTableAlias($class->name, $tableAlias),
5251
$this->quoteStrategy->getColumnName($field, $class, $this->platform)
5352
);
5453

54+
$columnAlias = null;
55+
if ($this->currentPersisterContext->rsm->hasColumnAliasByField($alias, $field)) {
56+
$columnAlias = $this->currentPersisterContext->rsm->getColumnAliasByField($alias, $field);
57+
}
58+
59+
if ($columnAlias === null) {
60+
$columnAlias = $this->getSQLColumnAlias($fieldMapping['columnName']);
61+
}
62+
5563
$this->currentPersisterContext->rsm->addFieldResult($alias, $columnAlias, $field, $class->name);
5664

5765
if (isset($fieldMapping['requireSQLConversion'])) {

src/Query/ResultSetMapping.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,10 @@ public function addFieldResult($alias, $columnName, $fieldName, $declaringClass
356356

357357
public function hasColumnAliasByField(string $alias, string $fieldName): bool
358358
{
359+
if (! isset($this->aliasMap[$alias])) {
360+
return false;
361+
}
362+
359363
$declaringClass = $this->aliasMap[$alias];
360364

361365
return isset($this->columnAliasMappings[$declaringClass][$alias][$fieldName]);
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Tests\ORM\Functional\Ticket\GH12225;
6+
7+
use DateTimeImmutable;
8+
use Doctrine\Common\Collections\ArrayCollection;
9+
use Doctrine\Common\Collections\Collection;
10+
use Doctrine\ORM\Mapping\Column;
11+
use Doctrine\ORM\Mapping\DiscriminatorColumn;
12+
use Doctrine\ORM\Mapping\DiscriminatorMap;
13+
use Doctrine\ORM\Mapping\Entity;
14+
use Doctrine\ORM\Mapping\GeneratedValue;
15+
use Doctrine\ORM\Mapping\Id;
16+
use Doctrine\ORM\Mapping\Index;
17+
use Doctrine\ORM\Mapping\InheritanceType;
18+
use Doctrine\ORM\Mapping\ManyToOne;
19+
use Doctrine\ORM\Mapping\OneToMany;
20+
use Doctrine\ORM\Mapping\Table;
21+
22+
/**
23+
* @Entity
24+
* @Table(name="gh_12225_directory", indexes={@Index(columns={"dir_key"})})
25+
* @InheritanceType("SINGLE_TABLE")
26+
* @DiscriminatorColumn(name="type", type="string")
27+
* @DiscriminatorMap({"main" = ConcreteDirectory::class})
28+
*/
29+
#[Entity]
30+
#[Table(name: 'gh_12225_directory')]
31+
#[Index(columns: ['dir_key'])]
32+
#[InheritanceType('SINGLE_TABLE')]
33+
#[DiscriminatorColumn(name: 'type', type: 'string')]
34+
#[DiscriminatorMap(['main' => ConcreteDirectory::class])]
35+
class AbstractDirectory
36+
{
37+
/**
38+
* @var int
39+
* @Id
40+
* @GeneratedValue
41+
* @Column(name="id", type="integer")
42+
*/
43+
#[Id]
44+
#[GeneratedValue]
45+
#[Column(name: 'id', type: 'integer')]
46+
private $id;
47+
48+
/**
49+
* @var string
50+
* @Column(name="dir_key", type="string")
51+
*/
52+
#[Column(name: 'dir_key', type: 'string')]
53+
private $dirKey;
54+
55+
/**
56+
* @var DateTimeImmutable|null
57+
* @Column(name="deleted_at", type="datetime_immutable", nullable=true)
58+
*/
59+
#[Column(name: 'deleted_at', type: 'datetime_immutable', nullable: true)]
60+
private $deletedAt = null;
61+
62+
/**
63+
* @var AbstractDirectory|null
64+
* @ManyToOne(targetEntity=AbstractDirectory::class, fetch="LAZY", inversedBy="directories")
65+
*/
66+
#[ManyToOne(targetEntity: self::class, fetch: 'LAZY', inversedBy: 'directories')]
67+
private $parent = null;
68+
69+
/**
70+
* @var Collection<string, self>
71+
* @OneToMany(mappedBy="parent", targetEntity=AbstractDirectory::class, fetch="EXTRA_LAZY", indexBy="dirKey")
72+
*/
73+
#[OneToMany(mappedBy: 'parent', targetEntity: self::class, fetch: 'EXTRA_LAZY', indexBy: 'dirKey')]
74+
private $children;
75+
76+
public function __construct(string $dirKey)
77+
{
78+
$this->dirKey = $dirKey;
79+
$this->children = new ArrayCollection();
80+
}
81+
82+
public function getId(): int
83+
{
84+
return $this->id;
85+
}
86+
87+
public function getDirKey(): string
88+
{
89+
return $this->dirKey;
90+
}
91+
92+
public function getDeletedAt(): ?DateTimeImmutable
93+
{
94+
return $this->deletedAt;
95+
}
96+
97+
public function setDeletedAt(?DateTimeImmutable $deletedAt): AbstractDirectory
98+
{
99+
$this->deletedAt = $deletedAt;
100+
101+
return $this;
102+
}
103+
104+
public function getParent(): ?AbstractDirectory
105+
{
106+
return $this->parent;
107+
}
108+
109+
public function setParent(?AbstractDirectory $parent): AbstractDirectory
110+
{
111+
$this->parent = $parent;
112+
113+
return $this;
114+
}
115+
116+
/**
117+
* @return Collection<string, self>
118+
*/
119+
public function getChildren(): Collection
120+
{
121+
return $this->children;
122+
}
123+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Tests\ORM\Functional\Ticket\GH12225;
6+
7+
use Doctrine\ORM\Mapping\Entity;
8+
9+
/**
10+
* @Entity
11+
*/
12+
#[Entity]
13+
class ConcreteDirectory extends AbstractDirectory
14+
{
15+
}
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\ORM\Functional\Ticket\GH12225;
6+
7+
use Doctrine\Tests\OrmFunctionalTestCase;
8+
9+
class GH12225Test extends OrmFunctionalTestCase
10+
{
11+
protected function setUp(): void
12+
{
13+
parent::setUp();
14+
15+
$this->setUpEntitySchema([
16+
AbstractDirectory::class,
17+
ConcreteDirectory::class,
18+
]);
19+
}
20+
21+
public function testHydrateWithIndexByFilterAndInheritanceMapping(): void
22+
{
23+
// Enable the filter
24+
$this->_em->getConfiguration()->addFilter('my_filter', MyFilter::class);
25+
$this->_em->getFilters()->enable('my_filter');
26+
27+
// Load entities into database
28+
$parent = new ConcreteDirectory('parent');
29+
$child = (new ConcreteDirectory('child'))->setParent($parent);
30+
$this->_em->persist($parent);
31+
$this->_em->persist($child);
32+
$this->_em->flush();
33+
$this->_em->clear();
34+
35+
$repository = $this->_em->getRepository(AbstractDirectory::class);
36+
37+
// Fetch entities from database while changing filters
38+
$this->_em->getFilters()->suspend('my_filter');
39+
$directories = $repository->findBy(['parent' => null]);
40+
$this->_em->getFilters()->restore('my_filter');
41+
42+
// Ensure we got the parent directory
43+
self::assertCount(1, $directories);
44+
self::assertEquals('parent', $directories[0]->getDirKey());
45+
46+
// Try to hydrate all children of the parent directory (toArray is important here to initialize the collection)
47+
self::assertCount(1, $directories[0]->getChildren()->toArray());
48+
}
49+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Tests\ORM\Functional\Ticket\GH12225;
6+
7+
use Doctrine\ORM\Mapping\ClassMetadata;
8+
use Doctrine\ORM\Query\Filter\SQLFilter;
9+
10+
class MyFilter extends SQLFilter
11+
{
12+
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string
13+
{
14+
return $targetTableAlias . '.deleted_at IS NULL';
15+
}
16+
}

0 commit comments

Comments
 (0)