GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#130)
by
unknown
03:08
created

DbRefRelationManager::extractMongoIdFromDbRef()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 12
rs 9.2
cc 4
eloc 7
nc 3
nop 1
1
<?php
2
3
namespace Sokil\Mongo\Document;
4
5
use Sokil\Mongo\Collection;
6
use Sokil\Mongo\Document;
7
8
class DbRefRelationManager implements RelationManagerInterface
9
{
10
    private $relations;
11
12
    /**
13
     *
14
     * @var \Sokil\Mongo\Document
15
     */
16
    private $document;
17
18
    private $resolvedRelationIds = array();
19
20
    /**
21
     * @param Document $document
22
     * @param bool $excludeDb If "false" the DBRef will include the database name. When removing a reference, due to
23
     * DB inconsistency, the presence of database name creates some issues (database name doesn't match the real DB)
24
     * @return array
25
     */
26
    public static function generateDbRef($document, $excludeDb = false)
27
    {
28
        $collection = $document->getCollection();
29
        $databaseName = $collection->getDatabase()->getName();
30
31
        if (!$excludeDb) {
32
            $dbRef = \MongoDBRef::create($collection->getName(), $document->getId(), $databaseName);
33
        } else {
34
            $dbRef = \MongoDBRef::create($collection->getName(), $document->getId());
35
        }
36
37
        return $dbRef;
38
    }
39
40
    /**
41
     * Is required for creating the references on Many to Many relations, otherwise, the keys "$ref", "$id", "$db" will be transformed in numeric keys.
42
     *
43
     * @param Document $document
44
     * @return \MongoDBRef
45
     */
46
    protected function generateMongoDBRef(Document $document)
47
    {
48
        $ref = '$ref';
49
        $id = '$id';
50
        $db = '$db';
51
52
        $collection = $document->getCollection();
53
        $database = $collection->getDatabase();
54
55
        $dbRef = new \MongoDBRef();
56
        $dbRef->$ref = $collection->getName();
57
        $dbRef->$id = $document->getId();
58
        $dbRef->$db = $database->getName();
59
60
        return $dbRef;
61
    }
62
63
    public function __construct(Document $document = null)
64
    {
65
        $this->document = $document;
66
        $this->relations = $document->getRelationDefinition();
0 ignored issues
show
Bug introduced by
It seems like $document is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
67
    }
68
69
    /**
70
     * Check if relation with specified name is configured
71
     * @param string $name
72
     * @return boolean
73
     */
74
    public function isRelationExists($name)
75
    {
76
        return isset($this->relations[$name]);
77
    }
78
79
    /**
80
     * @param $relationName
81
     * @return array|null|Document
82
     * @throws \Sokil\Mongo\Exception
83
     */
84
    public function getRelated($relationName)
85
    {
86
        // check if relation exists
87
        if (!$this->isRelationExists($relationName)) {
88
            throw new \Sokil\Mongo\Exception('Relation with name "'.$relationName.'" not found');
89
        }
90
91
        // get relation metadata
92
        $relation = $this->relations[$relationName];
93
94
        $relationType = $relation[0];
95
        $targetCollectionName = $relation[1];
96
97
        // get target collection
98
        $foreignCollection = $this->document
99
            ->getCollection()
100
            ->getDatabase()
101
            ->getCollection($targetCollectionName);
102
103
        // check if relation already resolved
104 View Code Duplication
        if (isset($this->resolvedRelationIds[$relationName])) {
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...
105
            if (is_array($this->resolvedRelationIds[$relationName])) {
106
                // has_many, many_many
107
                return $foreignCollection->getDocuments($this->resolvedRelationIds[$relationName]);
108
            } else {
109
                //has_one, belongs
110
                return $foreignCollection->getDocument($this->resolvedRelationIds[$relationName]);
111
            }
112
        }
113
114
        switch ($relationType) {
115
116 View Code Duplication
            case Document::RELATION_HAS_ONE:
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...
117
                $foreignKey = $relation[2];
118
119
                $documentDbRef = self::generateDbRef($this->document);
120
121
                $document = $foreignCollection
0 ignored issues
show
Bug introduced by
The method where does only exist in Sokil\Mongo\Expression, but not in Sokil\Mongo\Cursor.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
122
                    ->find()
123
                    ->where($foreignKey.'.$id', $this->extractMongoIdFromDbRef($documentDbRef))
124
                    ->findOne();
125
126
                if ($document) {
127
                    $this->resolvedRelationIds[$relationName] = (string)$document->getId();
128
                }
129
130
                return $document;
131
132 View Code Duplication
            case Document::RELATION_BELONGS:
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...
133
                $document = null;
0 ignored issues
show
Unused Code introduced by
$document is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
134
135
                $localKey = $relation[2];
136
137
                $dbRef = $this->document->get($localKey);
138
                $document = $foreignCollection->getDocument($this->extractMongoIdFromDbRef($dbRef));
139
140
                if ($document) {
141
                    $this->resolvedRelationIds[$relationName] = (string)$document->getId();
142
                }
143
144
                return $document;
145
146 View Code Duplication
            case Document::RELATION_HAS_MANY:
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...
147
                $foreignKey = $relation[2];
148
149
                $documentDbRef = self::generateDbRef($this->document);
150
                $documents = $foreignCollection
151
                    ->find()
152
                    ->where($foreignKey.'.$id', $this->extractMongoIdFromDbRef($documentDbRef))
153
                    ->findAll();
154
155
                foreach ($documents as $document) {
156
                    $this->resolvedRelationIds[$relationName][] = (string)$document->getId();
157
                }
158
159
                return $documents;
160
161
            case Document::RELATION_MANY_MANY:
162
                $isRelationListStoredInternally = isset($relation[3]) && $relation[3];
163
                $reference = $relation[2];
164
165
                if ($isRelationListStoredInternally) {
166
                    // relation list stored in this document
167
                    $relatedDbRefsList = $this->document->get($reference);
168
169
                    if (!$relatedDbRefsList) {
170
                        return array();
171
                    }
172
                    $relationsMongoIds = array();
173
                    foreach ($relatedDbRefsList as $relatedDbRef) {
174
                        $relationsMongoIds[] = $this->extractMongoIdFromDbRef($relatedDbRef);
175
                    }
176
177
                    $documents = $foreignCollection
0 ignored issues
show
Bug introduced by
The method whereIn does only exist in Sokil\Mongo\Expression, but not in Sokil\Mongo\Cursor.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
178
                        ->find()
179
                        ->whereIn('_id', $relationsMongoIds)
180
                        ->findAll();
181
182
                } else {
183
                    // relation list stored in external document
184
                    $documents = $foreignCollection
185
                        ->find()
186
                        ->where($reference.'.$id', $this->document->getId())
187
                        ->findAll();
188
                }
189
190
                foreach ($documents as $document) {
191
                    $this->resolvedRelationIds[$relationName][] = (string)$document->getId();
192
                }
193
194
                return $documents;
195
196
            default:
197
                throw new \Sokil\Mongo\Exception(
198
                    'Unsupported relation type "'.$relationType.'" when resolve relation "'.$relationName.'"'
199
                );
200
        }
201
    }
202
203
    public function addRelation($relationName, Document $document)
204
    {
205
        if (!$this->isRelationExists($relationName)) {
206
            throw new \Exception('Relation "'.$relationName.'" not configured');
207
        }
208
209
        $relation = $this->relations[$relationName];
210
        list($relationType, $relatedCollectionName, $field) = $relation;
211
212
        $relatedCollection = $this->document
213
            ->getCollection()
214
            ->getDatabase()
215
            ->getCollection($relatedCollectionName);
216
217
        if (!$relatedCollection->hasDocument($document)) {
218
            throw new \Sokil\Mongo\Exception('Document must belongs to related collection');
219
        }
220
221
        switch ($relationType) {
222
223 View Code Duplication
            case Document::RELATION_BELONGS:
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...
224
                if (!$document->isStored()) {
225
                    throw new \Sokil\Mongo\Exception(
226
                        'Document '.get_class($document).' must be saved before adding relation'
227
                    );
228
                }
229
                $documentDbRef = self::generateDbRef($document);
230
                $this->document->set($field, $documentDbRef);
231
                break;
232
233 View Code Duplication
            case Document::RELATION_HAS_ONE;
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...
Coding Style introduced by
CASE statements must be defined using a colon

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
234
                if (!$this->document->isStored()) {
235
                    throw new \Sokil\Mongo\Exception(
236
                        'Document '.get_class($this).' must be saved before adding relation'
237
                    );
238
                }
239
                $documentDbRef = self::generateDbRef($this->document);
240
                $document->set($field, $documentDbRef)->save();
241
                break;
242
243 View Code Duplication
            case Document::RELATION_HAS_MANY:
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...
244
                if (!$this->document->isStored()) {
245
                    throw new \Sokil\Mongo\Exception(
246
                        'Document '.get_class($this).' must be saved before adding relation'
247
                    );
248
                }
249
                $documentDbRef = self::generateDbRef($this->document);
250
                $document->set($field, $documentDbRef)->save();
251
                break;
252
253
            case Document::RELATION_MANY_MANY:
254
                $isRelationListStoredInternally = isset($relation[3]) && $relation[3];
255 View Code Duplication
                if ($isRelationListStoredInternally) {
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...
256
                    $documentDbRef = self::generateMongoDBRef($document);
257
                    $this->document->push($field, $documentDbRef)->save();
258
                } else {
259
                    $documentDbRef = self::generateMongoDBRef($this->document);
260
                    $document->push($field, $documentDbRef)->save();
261
                }
262
                break;
263
264
            default:
265
                throw new \Sokil\Mongo\Exception(
266
                    'Unsupported relation type "'.$relationType.'" when resolve relation "'.$relationName.'"'
267
                );
268
        }
269
270
        return $this;
271
    }
272
273
    public function removeRelation($relationName, Document $document = null)
274
    {
275
        if (!$this->isRelationExists($relationName)) {
276
            throw new \Exception('Relation '.$relationName.' not configured');
277
        }
278
279
        $relation = $this->relations[$relationName];
280
281
        list($relationType, $relatedCollectionName, $field) = $relation;
282
283
        $relatedCollection = $this->document
284
            ->getCollection()
285
            ->getDatabase()
286
            ->getCollection($relatedCollectionName);
287
288
        if ($document && !$relatedCollection->hasDocument($document)) {
289
            throw new \Sokil\Mongo\Exception('Document must belongs to related collection');
290
        }
291
292
        switch ($relationType) {
293
294
            case Document::RELATION_BELONGS:
295
                $this->document->unsetField($field)->save();
296
                break;
297
298 View Code Duplication
            case Document::RELATION_HAS_ONE;
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...
Coding Style introduced by
CASE statements must be defined using a colon

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
299
                $document = $this->getRelated($relationName);
300
                if (!$document) {
301
                    // relation not exists
302
                    return $this;
303
                }
304
                $document->unsetField($field)->save();
305
                break;
306
307
            case Document::RELATION_HAS_MANY:
308
                if (!$document) {
309
                    throw new \Sokil\Mongo\Exception('Related document must be defined');
310
                }
311
                $document->unsetField($field)->save();
312
                break;
313
314
315
            case Document::RELATION_MANY_MANY:
316
                if (!$document) {
317
                    throw new \Sokil\Mongo\Exception('Related document must be defined');
318
                }
319
                $isRelationListStoredInternally = isset($relation[3]) && $relation[3];
320 View Code Duplication
                if ($isRelationListStoredInternally) {
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...
321
                    $documentDbRef = self::generateDbRef($document, true);
322
                    $this->document->pull($field, $documentDbRef)->save();
323
                } else {
324
                    $documentDbRef = self::generateDbRef($this->document, true);
325
                    $document->pull($field, $documentDbRef)->save();
326
                }
327
                break;
328
329
            default:
330
                throw new \Sokil\Mongo\Exception(
331
                    'Unsupported relation type "'.$relationType.'" when resolve relation "'.$relationName.'"'
332
                );
333
        }
334
335
        return $this;
336
    }
337
338
    /**
339
     * @param $dbRef
340
     * @return mixed
341
     */
342
    protected function extractMongoIdFromDbRef($dbRef)
343
    {
344
        if (is_array($dbRef) && array_key_exists('$id', $dbRef)) {
345
            return $dbRef['$id'];
346
        } else {
347
            if (get_class($dbRef) === 'MongoDBRef') {
348
                $id = '$id';
349
350
                return $dbRef->$id;
351
            }
352
        }
353
    }
354
}
355