Completed
Pull Request — master (#1654)
by Andreas
08:52
created

GraphLookup::connectFromField()   C

Complexity

Conditions 10
Paths 10

Size

Total Lines 51
Code Lines 30

Duplication

Lines 18
Ratio 35.29 %

Code Coverage

Tests 14
CRAP Score 15.9914

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 18
loc 51
ccs 14
cts 23
cp 0.6087
rs 6
cc 10
eloc 30
nc 10
nop 1
crap 15.9914

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ODM\MongoDB\Aggregation\Stage;
21
22
use Doctrine\Common\Persistence\Mapping\MappingException as BaseMappingException;
23
use Doctrine\MongoDB\Aggregation\Stage as BaseStage;
24
use Doctrine\ODM\MongoDB\Aggregation\Builder;
25
use Doctrine\ODM\MongoDB\DocumentManager;
26
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
27
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo;
28
use Doctrine\ODM\MongoDB\Mapping\MappingException;
29
use Doctrine\ODM\MongoDB\Types\Type;
30
31
class GraphLookup extends BaseStage\GraphLookup
32
{
33
    /**
34
     * @var DocumentManager
35
     */
36
    private $dm;
37
38
    /**
39
     * @var ClassMetadata
40
     */
41
    private $class;
42
43
    /**
44
     * @var ClassMetadata
45
     */
46
    private $targetClass;
47
48
    /**
49
     * @param Builder $builder
50
     * @param string $from Target collection for the $graphLookup operation to
51
     * search, recursively matching the connectFromField to the connectToField.
52
     * @param DocumentManager $documentManager
53
     * @param ClassMetadata $class
54
     */
55 8
    public function __construct(Builder $builder, $from, DocumentManager $documentManager, ClassMetadata $class)
56
    {
57 8
        $this->dm = $documentManager;
58 8
        $this->class = $class;
59
60 8
        parent::__construct($builder, $from);
61 7
    }
62
63
    /**
64
     * @param string $from
65
     * @return $this
66
     */
67 8 View Code Duplication
    public function from($from)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
68
    {
69
        // $from can either be
70
        // a) a field name indicating a reference to a different document. Currently, only REFERENCE_STORE_AS_ID is supported
71
        // b) a Class name
72
        // c) a collection name
73
        // In cases b) and c) the local and foreign fields need to be filled
74 8
        if ($this->class->hasReference($from)) {
75 6
            return $this->fromReference($from);
76
        }
77
78
        // Check if mapped class with given name exists
79
        try {
80 2
            $this->targetClass = $this->dm->getClassMetadata($from);
81 1
        } catch (BaseMappingException $e) {
82 1
            return parent::from($from);
83
        }
84
85 1
        if ($this->targetClass->isSharded()) {
86 1
            throw MappingException::cannotUseShardedCollectionInLookupStages($this->targetClass->name);
87
        }
88
89
        return parent::from($this->targetClass->getCollection());
90
    }
91
92 7
    public function connectFromField($connectFromField)
93
    {
94
        // No targetClass mapping - simply use field name as is
95 7
        if (!$this->targetClass) {
96 1
            return parent::connectFromField($connectFromField);
97
        }
98
99
        // connectFromField doesn't have to be a reference - in this case, just convert the field name
100 6
        if (!$this->targetClass->hasReference($connectFromField)) {
101 2
            return parent::connectFromField($this->convertTargetFieldName($connectFromField));
102
        }
103
104
        // connectFromField is a reference - do a sanity check
105 4
        $referenceMapping = $this->targetClass->getFieldMapping($connectFromField);
106 4
        if ($referenceMapping['targetDocument'] !== $this->targetClass->name) {
107 1
            throw MappingException::connectFromFieldMustReferenceSameDocument($connectFromField);
108
        }
109
110 3
        if ($referenceMapping['isOwningSide']) {
111 3
            switch ($referenceMapping['storeAs']) {
112
                case ClassMetadataInfo::REFERENCE_STORE_AS_ID:
113
                case ClassMetadataInfo::REFERENCE_STORE_AS_REF:
114 3
                    $referencedFieldName = ClassMetadataInfo::getReferenceFieldName($referenceMapping['storeAs'], $referenceMapping['name']);
115 3
                    break;
116
117
                default:
118
                    throw MappingException::cannotLookupDbRefReference($this->class->name, $connectFromField);
119
            }
120
121 3
            parent::connectFromField($referencedFieldName);
122 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
123
            if (isset($referenceMapping['repositoryMethod'])) {
124
                throw MappingException::repositoryMethodLookupNotAllowed($this->class->name, $connectFromField);
125
            }
126
127
            $mappedByMapping = $this->targetClass->getFieldMapping($referenceMapping['mappedBy']);
128
            switch ($mappedByMapping['storeAs']) {
129
                case ClassMetadataInfo::REFERENCE_STORE_AS_ID:
130
                case ClassMetadataInfo::REFERENCE_STORE_AS_REF:
131
                    $referencedFieldName = ClassMetadataInfo::getReferenceFieldName($mappedByMapping['storeAs'], $mappedByMapping['name']);
132
                    break;
133
134
                default:
135
                    throw MappingException::cannotLookupDbRefReference($this->class->name, $connectFromField);
136
            }
137
138
            parent::connectFromField($referencedFieldName);
139
        }
140
141 3
        return $this;
142
    }
143
144 7
    public function connectToField($connectToField)
145
    {
146 7
        return parent::connectToField($this->convertTargetFieldName($connectToField));
147
    }
148
149
    /**
150
     * @param string $fieldName
151
     * @return $this
152
     * @throws MappingException
153
     */
154 6
    private function fromReference($fieldName)
155
    {
156 6
        if (! $this->class->hasReference($fieldName)) {
157
            MappingException::referenceMappingNotFound($this->class->name, $fieldName);
158
        }
159
160 6
        $referenceMapping = $this->class->getFieldMapping($fieldName);
161 6
        $this->targetClass = $this->dm->getClassMetadata($referenceMapping['targetDocument']);
162 6
        if ($this->targetClass->isSharded()) {
163
            throw MappingException::cannotUseShardedCollectionInLookupStages($this->targetClass->name);
164
        }
165
166 6
        parent::from($this->targetClass->getCollection());
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (from() instead of fromReference()). Are you sure this is correct? If so, you might want to change this to $this->from().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
167
168 6
        if ($referenceMapping['isOwningSide']) {
169 5
            switch ($referenceMapping['storeAs']) {
170
                case ClassMetadataInfo::REFERENCE_STORE_AS_ID:
171
                case ClassMetadataInfo::REFERENCE_STORE_AS_REF:
172 5
                    $referencedFieldName = ClassMetadataInfo::getReferenceFieldName($referenceMapping['storeAs'], $referenceMapping['name']);
173 5
                    break;
174
175
                default:
176
                    throw MappingException::cannotLookupDbRefReference($this->class->name, $fieldName);
177
            }
178
179
            $this
180 5
                ->startWith('$' . $referencedFieldName)
181 5
                ->connectToField('_id');
182
183
            // A self-reference indicates that we can also fill the "connectFromField" accordingly
184 5
            if ($this->targetClass->name === $this->class->name) {
185 5
                $this->connectFromField($referencedFieldName);
186
            }
187
        } else {
188 1
            if (isset($referenceMapping['repositoryMethod'])) {
189
                throw MappingException::repositoryMethodLookupNotAllowed($this->class->name, $fieldName);
190
            }
191
192 1
            $mappedByMapping = $this->targetClass->getFieldMapping($referenceMapping['mappedBy']);
193 1
            switch ($mappedByMapping['storeAs']) {
194
                case ClassMetadataInfo::REFERENCE_STORE_AS_ID:
195
                case ClassMetadataInfo::REFERENCE_STORE_AS_REF:
196 1
                    $referencedFieldName = ClassMetadataInfo::getReferenceFieldName($mappedByMapping['storeAs'], $mappedByMapping['name']);
197 1
                    break;
198
199
                default:
200
                    throw MappingException::cannotLookupDbRefReference($this->class->name, $fieldName);
201
            }
202
203
            $this
204 1
                ->startWith('$' . $referencedFieldName)
205 1
                ->connectToField('_id');
206
207
            // A self-reference indicates that we can also fill the "connectFromField" accordingly
208 1
            if ($this->targetClass->name === $this->class->name) {
209 1
                $this->connectFromField($referencedFieldName);
210
            }
211
        }
212
213 6
        return $this;
214
    }
215
216 6
    protected function convertExpression($expression)
217
    {
218 6
        if (is_array($expression)) {
219
            return array_map([$this, 'convertExpression'], $expression);
220 6
        } elseif (is_string($expression) && substr($expression, 0, 1) === '$') {
221 6
            return '$' . $this->getDocumentPersister($this->class)->prepareFieldName(substr($expression, 1));
222
        } else {
223
            return Type::convertPHPToDatabaseValue(parent::convertExpression($expression));
224
        }
225
    }
226
227 7
    protected function convertTargetFieldName($fieldName)
228
    {
229 7
        if (is_array($fieldName)) {
230
            return array_map([$this, 'convertTargetFieldName'], $fieldName);
231
        }
232
233 7
        if (!$this->targetClass) {
234 1
            return $fieldName;
235
        }
236
237 6
        return $this->getDocumentPersister($this->targetClass)->prepareFieldName($fieldName);
238
    }
239
240
    /**
241
     * @param ClassMetadata $class
242
     * @return \Doctrine\ODM\MongoDB\Persisters\DocumentPersister
243
     */
244 7
    private function getDocumentPersister(ClassMetadata $class)
245
    {
246 7
        return $this->dm->getUnitOfWork()->getDocumentPersister($class->name);
247
    }
248
}
249