Skip to content

Commit

Permalink
Updated persisting logic to throw exception of class is not listed in…
Browse files Browse the repository at this point in the history
… discriminator map.
  • Loading branch information
watari committed Nov 9, 2018
1 parent dcbff78 commit 804b1c2
Show file tree
Hide file tree
Showing 12 changed files with 150 additions and 156 deletions.
48 changes: 34 additions & 14 deletions lib/Doctrine/ODM/MongoDB/DocumentManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -734,27 +734,47 @@ public function createReference(object $document, array $referenceMapping)
* class that is not defined in the discriminator map may only have a
* discriminator field and no value, so default to the full class name.
*/
if (isset($class->discriminatorField)) {
$reference[$class->discriminatorField] = $class->discriminatorValue ?? $class->name;
$discriminatorField = null;
$discriminatorValue = null;
if (isset($referenceMapping['discriminatorField'])) {
$discriminatorField = $referenceMapping['discriminatorField'];
if (isset($referenceMapping['discriminatorMap'])) {
$pos = array_search($class->name, $referenceMapping['discriminatorMap']);
if ($pos !== false) {
$discriminatorValue = $pos;
}
} else {
$discriminatorValue = $class->name;
}
} else {
$discriminatorField = $class->discriminatorField;
$discriminatorValue = $class->discriminatorValue;
}

if (isset($discriminatorField)) {
if ($discriminatorValue === null) {
throw MappingException::unlistedClassInDiscriminatorMap($class->name);
}
$reference[$discriminatorField] = $discriminatorValue;

/* Add a discriminator value if the referenced document is not mapped
* explicitly to a targetDocument class.
*/
if (! isset($referenceMapping['targetDocument'])) {
} elseif (! isset($referenceMapping['targetDocument']) && $storeAs !== 'dbRefWithDb' && isset($referenceMapping['discriminatorField'])) { /* ??? && isset($referenceMapping['discriminatorValue'] */
$discriminatorField = $referenceMapping['discriminatorField'];
$discriminatorValue = isset($referenceMapping['discriminatorMap'])
? array_search($class->name, $referenceMapping['discriminatorMap'])
: $class->name;

/* If the discriminator value was not found in the map, use the full
* class name. In the future, it may be preferable to throw an
* exception here (perhaps based on some strictness option).
*
* @see PersistenceBuilder::prepareEmbeddedDocumentValue()
*/
if ($discriminatorValue === false) {

$discriminatorMap = null;
if (isset($referenceMapping['discriminatorMap'])) {
$discriminatorMap = $referenceMapping['discriminatorMap'];
}
if (! isset($discriminatorMap)) {
$discriminatorValue = $class->name;
} else {
$discriminatorValue = array_search($class->name, $discriminatorMap);

if ($discriminatorValue === false) {
throw MappingException::unlistedClassInDiscriminatorMap($class->name);
}
}

$reference[$discriminatorField] = $discriminatorValue;
Expand Down
23 changes: 23 additions & 0 deletions lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
use function count;
use function get_class;
use function in_array;
use function interface_exists;
use function is_array;
use function is_string;
use function is_subclass_of;
Expand Down Expand Up @@ -1902,6 +1903,28 @@ public function mapField(array $mapping) : array
$mapping['discriminatorField'] = self::DEFAULT_DISCRIMINATOR_FIELD;
}

if (isset($mapping['targetDocument']) && ! class_exists($mapping['targetDocument']) && ! interface_exists($mapping['targetDocument'])) {
$fullClassName = $this->reflClass->getNamespaceName() . '\\' . $mapping['targetDocument'];
if (! class_exists($fullClassName) && ! interface_exists($mapping['targetDocument'])) {
throw MappingException::nonExistingClass($mapping['targetDocument']);
}

$mapping['targetDocument'] = $fullClassName;
}

if (isset($mapping['discriminatorMap'])) {
foreach ($mapping['discriminatorMap'] as $value => $targetDocument) {
if (class_exists($targetDocument) || interface_exists($targetDocument)) {
continue;
}
$fullClassName = $this->reflClass->getNamespaceName() . '\\' . $targetDocument;
if (! class_exists($fullClassName) && ! interface_exists($targetDocument)) {
throw MappingException::nonExistingClass($fullClassName);
}
$mapping['discriminatorMap'][$value] = $fullClassName;
}
}

if (isset($mapping['version'])) {
$mapping['notSaved'] = true;
$this->setVersionMapping($mapping);
Expand Down
5 changes: 5 additions & 0 deletions lib/Doctrine/ODM/MongoDB/Mapping/MappingException.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ public static function invalidClassInDiscriminatorMap(string $className, string
return new self(sprintf("Document class '%s' used in the discriminator map of class '%s' does not exist.", $className, $owningClass));
}

public static function unlistedClassInDiscriminatorMap(string $className) : self
{
return new self(sprintf('Document class "%s" is unlisted in the discriminator map.', $className));
}

public static function invalidDiscriminatorValue(string $value, string $owningClass) : self
{
return new self(sprintf("Discriminator value '%s' used in the declaration of class '%s' does not exist.", $value, $owningClass));
Expand Down
45 changes: 30 additions & 15 deletions lib/Doctrine/ODM/MongoDB/Persisters/PersistenceBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
use Doctrine\ODM\MongoDB\Mapping\MappingException;
use Doctrine\ODM\MongoDB\PersistentCollection\PersistentCollectionInterface;
use Doctrine\ODM\MongoDB\Types\Type;
use Doctrine\ODM\MongoDB\UnitOfWork;
Expand Down Expand Up @@ -96,7 +97,13 @@ public function prepareInsertData($document)

// add discriminator if the class has one
if (isset($class->discriminatorField)) {
$insertData[$class->discriminatorField] = $class->discriminatorValue ?? $class->name;
if ($class->discriminatorValue === null) {
if (! empty($class->discriminatorMap)) {
throw MappingException::unlistedClassInDiscriminatorMap($class->name);
}
$class->discriminatorValue = $class->name;
}
$insertData[$class->discriminatorField] = $class->discriminatorValue;
}

return $insertData;
Expand Down Expand Up @@ -277,7 +284,13 @@ public function prepareUpsertData($document)

// add discriminator if the class has one
if (isset($class->discriminatorField)) {
$updateData['$set'][$class->discriminatorField] = $class->discriminatorValue ?? $class->name;
if ($class->discriminatorValue === null) {
if (! empty($class->discriminatorMap)) {
throw MappingException::unlistedClassInDiscriminatorMap($class->name);
}
$class->discriminatorValue = $class->name;
}
$updateData['$set'][$class->discriminatorField] = $class->discriminatorValue;
}

return $updateData;
Expand Down Expand Up @@ -377,19 +390,15 @@ public function prepareEmbeddedDocumentValue(array $embeddedMapping, $embeddedDo
/* Add a discriminator value if the embedded document is not mapped
* explicitly to a targetDocument class.
*/
if (! isset($embeddedMapping['targetDocument'])) {
if (! isset($embeddedMapping['targetDocument']) && isset($embeddedMapping['discriminatorField'])) {
$discriminatorField = $embeddedMapping['discriminatorField'];
$discriminatorValue = isset($embeddedMapping['discriminatorMap'])
? array_search($class->name, $embeddedMapping['discriminatorMap'])
: $class->name;

/* If the discriminator value was not found in the map, use the full
* class name. In the future, it may be preferable to throw an
* exception here (perhaps based on some strictness option).
*
* @see DocumentManager::createDBRef()
*/
if ($discriminatorValue === false) {
if (! empty($embeddedMapping['discriminatorMap'])) {
$discriminatorValue = array_search($class->name, $embeddedMapping['discriminatorMap']);

if ($discriminatorValue === false) {
throw MappingException::unlistedClassInDiscriminatorMap($class->name);
}
} else {
$discriminatorValue = $class->name;
}

Expand All @@ -401,7 +410,13 @@ public function prepareEmbeddedDocumentValue(array $embeddedMapping, $embeddedDo
* discriminator field and no value, so default to the full class name.
*/
if (isset($class->discriminatorField)) {
$embeddedDocumentValue[$class->discriminatorField] = $class->discriminatorValue ?? $class->name;
if ($class->discriminatorValue === null) {
if (! empty($class->discriminatorMap)) {
throw MappingException::unlistedClassInDiscriminatorMap($class->name);
}
$class->discriminatorValue = $class->name;
}
$embeddedDocumentValue[$class->discriminatorField] = $class->discriminatorValue;
}

// Ensure empty embedded documents are stored as BSON objects
Expand Down
4 changes: 2 additions & 2 deletions tests/Doctrine/ODM/MongoDB/Tests/Functional/EmbeddedTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ public function testEmbeddedDocumentWithDifferentFieldNameAnnotation()
$embedded = new EmbeddedDocumentWithId();
$embedded->id = (string) new ObjectId();

$firstEmbedded = new EmbedDocumentWithAnotherEmbed();
$firstEmbedded = new EmbeddedDocumentWithAnotherEmbedded();
$firstEmbedded->embed = $embedded;

$secondEmbedded = clone $firstEmbedded;
Expand Down Expand Up @@ -668,7 +668,7 @@ class ChangeEmbeddedWithNameAnnotationTest
/**
* @ODM\EmbeddedDocument
*/
class EmbedDocumentWithAnotherEmbed
class EmbeddedDocumentWithAnotherEmbedded
{
/** @ODM\EmbedOne(targetDocument=EmbeddedDocumentWithId::class, name="m_id") */
public $embed;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class GH267User
/** @ODM\Field(type="string") */
protected $name;

/** @ODM\ReferenceOne(name="company", targetDocument=GH267Company::class, discriminatorMap={"seller"="SellerCompany", "buyer"="BuyerCompany"}, inversedBy="users") */
/** @ODM\ReferenceOne(name="company", targetDocument=GH267Company::class, discriminatorMap={"seller"="GH267SellerCompany", "buyer"="GH267BuyerCompany"}, inversedBy="users") */
protected $company;

public function __construct($name)
Expand Down
Loading

0 comments on commit 804b1c2

Please sign in to comment.