Skip to content

Commit

Permalink
Add deprecation layer for unlisted classes in discriminator map (doct…
Browse files Browse the repository at this point in the history
  • Loading branch information
alcaeus committed Jan 11, 2019
1 parent 058e09a commit 84eee94
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 25 deletions.
7 changes: 7 additions & 0 deletions UPGRADE-1.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,10 @@ in favor of `Doctrine\ODM\MongoDB\Mapping\ClassMetadata` and will be dropped in
will be dropped in 2.0. Use `write-concern` instead.
* The `fieldName` attribute in field mappings has been deprecated and will be
dropped in 2.0. Use `field-name` instead.

### Full discriminator maps required

When using a discriminator map on a reference or embedded relationship,
persisting or loading a class that is not in the map is deprecated and will
cause an exception in 2.0. The discriminator map must contain all possible
classes that can be referenced.
84 changes: 59 additions & 25 deletions lib/Doctrine/ODM/MongoDB/DocumentManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
use Doctrine\ODM\MongoDB\Proxy\ProxyFactory;
use Doctrine\ODM\MongoDB\Query\FilterCollection;
use Doctrine\ODM\MongoDB\Repository\RepositoryFactory;
use const E_USER_DEPRECATED;
use function sprintf;
use function trigger_error;

/**
* The DocumentManager class is the central access point for managing the
Expand Down Expand Up @@ -729,39 +732,70 @@ public function createReference($document, array $referenceMapping)
throw new \InvalidArgumentException("Reference type {$storeAs} is invalid.");
}

/* If the class has a discriminator (field and value), use it. A child
* 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] = isset($class->discriminatorValue)
? $class->discriminatorValue
: $class->name;
return $reference + $this->getDiscriminatorData($referenceMapping, $class);
}

/**
* Build discriminator portion of reference for specified reference mapping and class metadata.
*
* @param array $referenceMapping Mappings of reference for which discriminator data is created.
* @param ClassMetadata $class Metadata of reference document class.
*
* @return array with next structure [{discriminator field} => {discriminator value}]
*
* @throws MappingException When discriminator map is present and reference class in not registered in it.
*/
private function getDiscriminatorData(array $referenceMapping, ClassMetadata $class)
{
$discriminatorField = null;
$discriminatorValue = null;
$discriminatorData = [];

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 = isset($class->discriminatorValue) ? $class->discriminatorValue : $class->name;
}

/* Add a discriminator value if the referenced document is not mapped
* explicitly to a targetDocument class.
*/
if (! isset($referenceMapping['targetDocument'])) {
if ($discriminatorField !== null) {
if ($discriminatorValue === null) {
@trigger_error(sprintf('Document class "%s" is unlisted in the discriminator map for reference "%s". This is deprecated and will throw an exception in 2.0.', $class->name, $referenceMapping['name']), E_USER_DEPRECATED);
$discriminatorValue = $class->name;
}

$discriminatorData = [$discriminatorField => $discriminatorValue];
} elseif (! isset($referenceMapping['targetDocument'])) {
$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 ($discriminatorMap === null) {
$discriminatorValue = $class->name;
} else {
$discriminatorValue = array_search($class->name, $discriminatorMap);

if ($discriminatorValue === false) {
@trigger_error(sprintf('Document class "%s" is unlisted in the discriminator map for reference "%s". This is deprecated and will throw an exception in 2.0.', $class->name, $referenceMapping['name']), E_USER_DEPRECATED);
$discriminatorValue = $class->name;
}
}

$reference[$discriminatorField] = $discriminatorValue;
$discriminatorData = [$discriminatorField => $discriminatorValue];
}

return $reference;
return $discriminatorData;
}

/**
Expand Down

0 comments on commit 84eee94

Please sign in to comment.