Skip to content

Commit c7868c5

Browse files
authored
Merge pull request #1962 from alcaeus/fix-wrong-discriminator-inheritance
Fix wrong usage of discriminator map in complex document inheritance chains
2 parents 496ab53 + ed67722 commit c7868c5

File tree

4 files changed

+146
-20
lines changed

4 files changed

+146
-20
lines changed

lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataInfo.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,10 @@ public function setDiscriminatorField($discriminatorField)
784784
*/
785785
public function setDiscriminatorMap(array $map)
786786
{
787+
$this->subClasses = [];
788+
$this->discriminatorMap = [];
789+
$this->discriminatorValue = null;
790+
787791
foreach ($map as $value => $className) {
788792
if (strpos($className, '\\') === false && strlen($this->namespace)) {
789793
$className = $this->namespace . '\\' . $className;

lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -944,27 +944,35 @@ public function prepareFieldName($fieldName)
944944
}
945945

946946
/**
947-
* Adds discriminator criteria to an already-prepared query.
947+
* Adds a discriminator criteria to an already-prepared query if necessary.
948+
*
949+
* If the class we're querying has a discriminator field set, we add all
950+
* possible discriminator values to the query. The list of possible
951+
* discriminator values is based on the discriminatorValue of the class
952+
* itself as well as those of all its subclasses.
948953
*
949954
* This method should be used once for query criteria and not be used for
950955
* nested expressions. It should be called before
951956
* {@link DocumentPerister::addFilterToPreparedQuery()}.
952957
*
953-
* @param array $preparedQuery
954958
* @return array
955959
*/
956960
public function addDiscriminatorToPreparedQuery(array $preparedQuery)
957961
{
958-
/* If the class has a discriminator field, which is not already in the
959-
* criteria, inject it now. The field/values need no preparation.
960-
*/
961-
if ($this->class->hasDiscriminator() && ! isset($preparedQuery[$this->class->discriminatorField])) {
962-
$discriminatorValues = $this->getClassDiscriminatorValues($this->class);
963-
if (count($discriminatorValues) === 1) {
964-
$preparedQuery[$this->class->discriminatorField] = $discriminatorValues[0];
965-
} else {
966-
$preparedQuery[$this->class->discriminatorField] = array('$in' => $discriminatorValues);
967-
}
962+
if (isset($preparedQuery[$this->class->discriminatorField]) || $this->class->discriminatorField === null) {
963+
return $preparedQuery;
964+
}
965+
966+
$discriminatorValues = $this->getClassDiscriminatorValues($this->class);
967+
968+
if ($discriminatorValues === []) {
969+
return $preparedQuery;
970+
}
971+
972+
if (count($discriminatorValues) === 1) {
973+
$preparedQuery[$this->class->discriminatorField] = $discriminatorValues[0];
974+
} else {
975+
$preparedQuery[$this->class->discriminatorField] = ['$in' => $discriminatorValues];
968976
}
969977

970978
return $preparedQuery;
@@ -1333,14 +1341,19 @@ private function hasQueryOperators($value)
13331341
}
13341342

13351343
/**
1336-
* Gets the array of discriminator values for the given ClassMetadata
1344+
* Returns the list of discriminator values for the given ClassMetadata
13371345
*
13381346
* @param ClassMetadata $metadata
13391347
* @return array
13401348
*/
13411349
private function getClassDiscriminatorValues(ClassMetadata $metadata)
13421350
{
1343-
$discriminatorValues = array($metadata->discriminatorValue);
1351+
$discriminatorValues = [];
1352+
1353+
if ($metadata->discriminatorValue !== null) {
1354+
$discriminatorValues[] = $metadata->discriminatorValue;
1355+
}
1356+
13441357
foreach ($metadata->subClasses as $className) {
13451358
if ($key = array_search($className, $metadata->discriminatorMap)) {
13461359
$discriminatorValues[] = $key;
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
namespace Doctrine\ODM\MongoDB\Tests\Functional\Ticket;
4+
5+
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
6+
use Doctrine\ODM\MongoDB\Tests\BaseTest;
7+
8+
class GH1962Test extends BaseTest
9+
{
10+
public function testDiscriminatorMaps()
11+
{
12+
$metadata = $this->dm->getClassMetadata(GH1962Superclass::class);
13+
self::assertCount(3, $metadata->discriminatorMap);
14+
15+
$metadata = $this->dm->getClassMetadata(GH1962BarSuperclass::class);
16+
self::assertCount(2, $metadata->discriminatorMap);
17+
}
18+
19+
public function testFetchingDiscriminatedDocuments()
20+
{
21+
$foo = new GH1962FooDocument();
22+
$bar = new GH1962BarDocument();
23+
$baz = new GH1962BazDocument();
24+
25+
$this->dm->persist($foo);
26+
$this->dm->persist($bar);
27+
$this->dm->persist($baz);
28+
29+
$this->dm->flush();
30+
$this->dm->clear();
31+
32+
self::assertCount(3, $this->dm->getRepository(GH1962Superclass::class)->findAll());
33+
self::assertCount(2, $this->dm->getRepository(GH1962BarSuperclass::class)->findAll());
34+
35+
self::assertCount(1, $this->dm->getRepository(GH1962FooDocument::class)->findAll());
36+
self::assertCount(1, $this->dm->getRepository(GH1962BarDocument::class)->findAll());
37+
self::assertCount(1, $this->dm->getRepository(GH1962BazDocument::class)->findAll());
38+
}
39+
}
40+
41+
/**
42+
* @ODM\MappedSuperclass()
43+
* @ODM\DiscriminatorField("type")
44+
* @ODM\InheritanceType("SINGLE_COLLECTION")
45+
* @ODM\DiscriminatorMap({
46+
* "foo"=GH1962FooDocument::class,
47+
* "bar"=GH1962BarDocument::class,
48+
* "baz"=GH1962BazDocument::class
49+
* })
50+
*/
51+
class GH1962Superclass
52+
{
53+
/** @ODM\Id */
54+
public $id;
55+
}
56+
57+
/** @ODM\Document */
58+
class GH1962FooDocument extends GH1962Superclass
59+
{
60+
}
61+
62+
/**
63+
* @ODM\MappedSuperclass()
64+
* @ODM\DiscriminatorMap({
65+
* "bar"=GH1962BarDocument::class,
66+
* "baz"=GH1962BazDocument::class
67+
* })
68+
*/
69+
class GH1962BarSuperclass extends GH1962Superclass
70+
{
71+
}
72+
73+
/** @ODM\Document */
74+
class GH1962BarDocument extends GH1962BarSuperclass
75+
{
76+
}
77+
78+
/** @ODM\Document */
79+
class GH1962BazDocument extends GH1962BarSuperclass
80+
{
81+
}

tests/Doctrine/ODM/MongoDB/Tests/Query/BuilderTest.php

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,25 @@ public function testReferencesGoesThroughDiscriminatorMap()
2525
->field('featureFull')->references($f)
2626
->getQuery()->debug();
2727

28-
$this->assertEquals([ 'featureFull.$id' => new \MongoId($f->id) ], $q1['query']);
28+
$this->assertEquals(
29+
[
30+
'featureFull.$id' => new \MongoId($f->id),
31+
'type' => ['$in' => ['ca', 'cb', 'cc']],
32+
],
33+
$q1['query']
34+
);
2935

3036
$q2 = $this->dm->createQueryBuilder(ParentClass::class)
3137
->field('featureSimple')->references($f)
3238
->getQuery()->debug();
3339

34-
$this->assertEquals([ 'featureSimple' => new \MongoId($f->id) ], $q2['query']);
40+
$this->assertEquals(
41+
[
42+
'featureSimple' => new \MongoId($f->id),
43+
'type' => ['$in' => ['ca', 'cb', 'cc']],
44+
],
45+
$q2['query']
46+
);
3547

3648
$q3 = $this->dm->createQueryBuilder(ParentClass::class)
3749
->field('featurePartial')->references($f)
@@ -41,6 +53,7 @@ public function testReferencesGoesThroughDiscriminatorMap()
4153
[
4254
'featurePartial.$id' => new \MongoId($f->id),
4355
'featurePartial.$ref' => 'Feature',
56+
'type' => ['$in' => ['ca', 'cb', 'cc']],
4457
],
4558
$q3['query']
4659
);
@@ -83,13 +96,27 @@ public function testIncludesReferenceToGoesThroughDiscriminatorMap()
8396
->field('featureFullMany')->includesReferenceTo($f)
8497
->getQuery()->debug();
8598

86-
$this->assertEquals([ 'featureFullMany' => [ '$elemMatch' => [ '$id' => new \MongoId($f->id) ] ] ], $q1['query']);
99+
$this->assertEquals(
100+
[
101+
'featureFullMany' => [
102+
'$elemMatch' => ['$id' => new \MongoId($f->id)],
103+
],
104+
'type' => ['$in' => ['ca', 'cb', 'cc']],
105+
],
106+
$q1['query']
107+
);
87108

88109
$q2 = $this->dm->createQueryBuilder(ParentClass::class)
89110
->field('featureSimpleMany')->includesReferenceTo($f)
90111
->getQuery()->debug();
91112

92-
$this->assertEquals([ 'featureSimpleMany' => new \MongoId($f->id) ], $q2['query']);
113+
$this->assertEquals(
114+
[
115+
'featureSimpleMany' => new \MongoId($f->id),
116+
'type' => ['$in' => ['ca', 'cb', 'cc']],
117+
],
118+
$q2['query']
119+
);
93120

94121
$q3 = $this->dm->createQueryBuilder(ParentClass::class)
95122
->field('featurePartialMany')->includesReferenceTo($f)
@@ -101,8 +128,9 @@ public function testIncludesReferenceToGoesThroughDiscriminatorMap()
101128
'$elemMatch' => [
102129
'$id' => new \MongoId($f->id),
103130
'$ref' => 'Feature',
104-
]
105-
]
131+
],
132+
],
133+
'type' => ['$in' => ['ca', 'cb', 'cc']],
106134
],
107135
$q3['query']
108136
);

0 commit comments

Comments
 (0)