Completed
Push — master ( 09b86b...ba0798 )
by Andreas
20:05 queued 12s
created

ResolveTargetDocumentListener   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 85
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 97.22%

Importance

Changes 0
Metric Value
wmc 12
lcom 1
cbo 3
dl 0
loc 85
ccs 35
cts 36
cp 0.9722
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A getSubscribedEvents() 0 7 1
A addResolveTargetDocument() 0 5 1
A onClassMetadataNotFound() 0 12 2
A loadClassMetadata() 0 12 3
A remapAssociation() 0 25 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ODM\MongoDB\Tools;
6
7
use Doctrine\Common\EventSubscriber;
8
use Doctrine\ODM\MongoDB\Event\LoadClassMetadataEventArgs;
9
use Doctrine\ODM\MongoDB\Event\OnClassMetadataNotFoundEventArgs;
10
use Doctrine\ODM\MongoDB\Events;
11
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
12
use function array_key_exists;
13
use function array_replace_recursive;
14
use function ltrim;
15
16
/**
17
 * ResolveTargetDocumentListener
18
 *
19
 * Mechanism to overwrite document interfaces or classes specified as association targets.
20
 */
21
class ResolveTargetDocumentListener implements EventSubscriber
22
{
23
    /** @var array */
24
    private $resolveTargetDocuments = [];
25
26
    /**
27
     * {@inheritDoc}
28
     */
29 2
    public function getSubscribedEvents()
30
    {
31
        return [
32 2
            Events::loadClassMetadata,
33
            Events::onClassMetadataNotFound,
34
        ];
35
    }
36
37
    /**
38
     * Add a target-document class name to resolve to a new class name.
39
     */
40 3
    public function addResolveTargetDocument(string $originalDocument, string $newDocument, array $mapping) : void
41
    {
42 3
        $mapping['targetDocument']                                    = ltrim($newDocument, '\\');
43 3
        $this->resolveTargetDocuments[ltrim($originalDocument, '\\')] = $mapping;
44 3
    }
45
46
    /**
47
     * @internal this is an event callback, and should not be called directly
48
     *
49
     * @return void
50
     */
51 2
    public function onClassMetadataNotFound(OnClassMetadataNotFoundEventArgs $args)
52
    {
53 2
        if (! array_key_exists($args->getClassName(), $this->resolveTargetDocuments)) {
54
            return;
55
        }
56
57 2
        $args->setFoundMetadata(
58
            $args
59 2
                ->getDocumentManager()
60 2
                ->getClassMetadata($this->resolveTargetDocuments[$args->getClassName()]['targetDocument'])
61
        );
62 2
    }
63
64
    /**
65
     * Process event and resolve new target document names.
66
     */
67 3
    public function loadClassMetadata(LoadClassMetadataEventArgs $args) : void
68
    {
69
        /** @var ClassMetadata $cm */
70 3
        $cm = $args->getClassMetadata();
71 3
        foreach ($cm->associationMappings as $mapping) {
72 3
            if (! isset($this->resolveTargetDocuments[$mapping['targetDocument']])) {
73 2
                continue;
74
            }
75
76 2
            $this->remapAssociation($cm, $mapping);
77
        }
78 3
    }
79
80 2
    private function remapAssociation(ClassMetadata $classMetadata, array $mapping) : void
81
    {
82 2
        $newMapping              = $this->resolveTargetDocuments[$mapping['targetDocument']];
83 2
        $newMapping              = array_replace_recursive($mapping, $newMapping);
84 2
        $newMapping['fieldName'] = $mapping['fieldName'];
85
86
        // clear reference case of duplicate exception
87 2
        unset($classMetadata->fieldMappings[$mapping['fieldName']]);
88 2
        unset($classMetadata->associationMappings[$mapping['fieldName']]);
89
90 2
        switch ($mapping['association']) {
91
            case ClassMetadata::REFERENCE_ONE:
92 2
                $classMetadata->mapOneReference($newMapping);
93 2
                break;
94
            case ClassMetadata::REFERENCE_MANY:
95 1
                $classMetadata->mapManyReference($newMapping);
96 1
                break;
97
            case ClassMetadata::EMBED_ONE:
98 2
                $classMetadata->mapOneEmbedded($newMapping);
99 2
                break;
100
            case ClassMetadata::EMBED_MANY:
101 1
                $classMetadata->mapManyEmbedded($newMapping);
102 1
                break;
103
        }
104 2
    }
105
}
106