Skip to content

Commit a8632ac

Browse files
authored
Keep the declared mapping information when using attribute overrides (doctrine#11135)
When using `AttributeOverride` to override mapping information inherited from a parent class (a mapped superclass), make sure to keep information about where the field was originally declared. This is important for `private` fields: Without the correct `declared` information, it will lead to errors when cached mapping information is loaded, reflection wakes up and looks for the private field in the wrong class.
1 parent 3dd3d38 commit a8632ac

File tree

3 files changed

+103
-0
lines changed

3 files changed

+103
-0
lines changed

lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php

+4
Original file line numberDiff line numberDiff line change
@@ -2558,6 +2558,10 @@ public function setAttributeOverride($fieldName, array $overrideMapping)
25582558
$overrideMapping['id'] = $mapping['id'];
25592559
}
25602560

2561+
if (isset($mapping['declared'])) {
2562+
$overrideMapping['declared'] = $mapping['declared'];
2563+
}
2564+
25612565
if (! isset($overrideMapping['type'])) {
25622566
$overrideMapping['type'] = $mapping['type'];
25632567
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Tests\ORM\Functional\Ticket;
6+
7+
use Doctrine\ORM\Mapping as ORM;
8+
use Doctrine\Tests\OrmFunctionalTestCase;
9+
10+
class GH11135Test extends OrmFunctionalTestCase
11+
{
12+
protected function setUp(): void
13+
{
14+
parent::setUp();
15+
16+
$this->setUpEntitySchema([
17+
GH11135MappedSuperclass::class,
18+
GH11135EntityWithOverride::class,
19+
GH11135EntityWithoutOverride::class,
20+
]);
21+
}
22+
23+
public function testOverrideInheritsDeclaringClass(): void
24+
{
25+
$cm1 = $this->_em->getClassMetadata(GH11135EntityWithOverride::class);
26+
$cm2 = $this->_em->getClassMetadata(GH11135EntityWithoutOverride::class);
27+
28+
self::assertSame($cm1->getFieldMapping('id')['declared'], $cm2->getFieldMapping('id')['declared']);
29+
self::assertSame($cm1->getAssociationMapping('ref')['declared'], $cm2->getAssociationMapping('ref')['declared']);
30+
}
31+
}
32+
33+
/**
34+
* @ORM\MappedSuperclass
35+
*/
36+
class GH11135MappedSuperclass
37+
{
38+
/**
39+
* @ORM\Id
40+
* @ORM\Column(type="integer")
41+
* @ORM\GeneratedValue
42+
*
43+
* @var int
44+
*/
45+
private $id;
46+
47+
/**
48+
* @ORM\ManyToOne(targetEntity="GH11135EntityWithoutOverride")
49+
*
50+
* @var GH11135EntityWithoutOverride
51+
*/
52+
private $ref;
53+
}
54+
55+
/**
56+
* @ORM\Entity()
57+
* @ORM\AttributeOverrides({
58+
* @ORM\AttributeOverride(name="id", column=@ORM\Column(name="id_overridden"))
59+
* })
60+
* @ORM\AssociationOverrides({
61+
* @ORM\AssociationOverride(name="ref", joinColumns=@ORM\JoinColumn(name="ref_overridden", referencedColumnName="id"))
62+
* })
63+
*/
64+
class GH11135EntityWithOverride extends GH11135MappedSuperclass
65+
{
66+
}
67+
68+
/**
69+
* @ORM\Entity()
70+
*/
71+
class GH11135EntityWithoutOverride extends GH11135MappedSuperclass
72+
{
73+
}

tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php

+26
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
use Doctrine\Tests\Models\DDC6412\DDC6412File;
3030
use Doctrine\Tests\Models\DDC964\DDC964Admin;
3131
use Doctrine\Tests\Models\DDC964\DDC964Guest;
32+
use Doctrine\Tests\Models\DirectoryTree\AbstractContentItem;
33+
use Doctrine\Tests\Models\DirectoryTree\Directory;
3234
use Doctrine\Tests\Models\Routing\RoutingLeg;
3335
use Doctrine\Tests\Models\TypedProperties;
3436
use Doctrine\Tests\ORM\Mapping\TypedFieldMapper\CustomIntAsStringTypedFieldMapper;
@@ -1186,6 +1188,30 @@ public function testInvalidOverrideAttributeFieldTypeException(): void
11861188
$cm->setAttributeOverride('name', ['type' => 'date']);
11871189
}
11881190

1191+
public function testAttributeOverrideKeepsDeclaringClass(): void
1192+
{
1193+
$cm = new ClassMetadata(Directory::class);
1194+
$cm->mapField(['fieldName' => 'id', 'type' => 'integer', 'declared' => AbstractContentItem::class]);
1195+
$cm->setAttributeOverride('id', ['columnName' => 'new_id']);
1196+
1197+
$mapping = $cm->getFieldMapping('id');
1198+
1199+
self::assertArrayHasKey('declared', $mapping);
1200+
self::assertSame(AbstractContentItem::class, $mapping['declared']);
1201+
}
1202+
1203+
public function testAssociationOverrideKeepsDeclaringClass(): void
1204+
{
1205+
$cm = new ClassMetadata(Directory::class);
1206+
$cm->mapManyToOne(['fieldName' => 'parentDirectory', 'targetEntity' => Directory::class, 'cascade' => ['remove'], 'declared' => Directory::class]);
1207+
$cm->setAssociationOverride('parentDirectory', ['cascade' => '']);
1208+
1209+
$mapping = $cm->getAssociationMapping('parentDirectory');
1210+
1211+
self::assertArrayHasKey('declared', $mapping);
1212+
self::assertSame(Directory::class, $mapping['declared']);
1213+
}
1214+
11891215
/** @group DDC-1955 */
11901216
public function testInvalidEntityListenerClassException(): void
11911217
{

0 commit comments

Comments
 (0)