Completed
Pull Request — master (#1790)
by Andreas
21:41
created

SchemaManager   D

Complexity

Total Complexity 135

Size/Duplication

Total Lines 592
Duplicated Lines 12.16 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 88.29%

Importance

Changes 0
Metric Value
wmc 135
lcom 1
cbo 5
dl 72
loc 592
ccs 196
cts 222
cp 0.8829
rs 4.8717
c 0
b 0
f 0

24 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
B ensureIndexes() 9 10 5
B updateIndexes() 9 10 5
D updateDocumentIndexes() 0 41 9
A getDocumentIndexes() 0 5 1
C doGetDocumentIndexes() 0 58 15
B prepareIndexes() 0 27 4
D ensureDocumentIndexes() 0 28 9
B deleteIndexes() 9 10 5
A deleteDocumentIndexes() 9 9 4
B createCollections() 9 9 5
B createDocumentCollection() 0 24 5
B dropCollections() 9 10 5
B dropDocumentCollection() 0 15 5
B dropDatabases() 9 10 5
A dropDocumentDatabase() 9 9 4
D isMongoIndexEquivalentToDocumentIndex() 0 43 17
A ensureSharding() 0 10 4
C ensureDocumentSharding() 0 41 14
A enableShardingForDbByDocumentName() 0 13 4
A runShardCollectionCommand() 0 16 1
A ensureGridFSIndexes() 0 5 1
A ensureChunksIndex() 0 11 4
A ensureFilesIndex() 0 11 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like SchemaManager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SchemaManager, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ODM\MongoDB;
6
7
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
8
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory;
9
use MongoDB\Driver\Exception\RuntimeException;
10
use MongoDB\Model\IndexInfo;
11
use function array_filter;
12
use function array_unique;
13
use function iterator_to_array;
14
use function strpos;
15
16
class SchemaManager
17
{
18
    /** @var DocumentManager */
19
    protected $dm;
20
21
    /** @var ClassMetadataFactory */
22
    protected $metadataFactory;
23
24 1623
    public function __construct(DocumentManager $dm, ClassMetadataFactory $cmf)
25
    {
26 1623
        $this->dm = $dm;
27 1623
        $this->metadataFactory = $cmf;
28 1623
    }
29
30
    /**
31
     * Ensure indexes are created for all documents that can be loaded with the
32
     * metadata factory.
33
     *
34
     * @param int $timeout Timeout (ms) for acknowledged index creation
35
     */
36 1 View Code Duplication
    public function ensureIndexes($timeout = null)
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...
37
    {
38 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
39 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
40 1
                continue;
41
            }
42 1
43
            $this->ensureDocumentIndexes($class->name, $timeout);
44 1
        }
45
    }
46
47
    /**
48
     * Ensure indexes exist for all mapped document classes.
49
     *
50
     * Indexes that exist in MongoDB but not the document metadata will be
51
     * deleted.
52
     *
53
     * @param int $timeout Timeout (ms) for acknowledged index creation
54
     */
55 View Code Duplication
    public function updateIndexes($timeout = null)
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...
56
    {
57
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
58
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
59
                continue;
60
            }
61
62
            $this->updateDocumentIndexes($class->name, $timeout);
63
        }
64
    }
65
66
    /**
67
     * Ensure indexes exist for the mapped document class.
68
     *
69
     * Indexes that exist in MongoDB but not the document metadata will be
70
     * deleted.
71
     *
72
     * @param string $documentName
73
     * @param int    $timeout      Timeout (ms) for acknowledged index creation
74 3
     * @throws \InvalidArgumentException
75
     */
76 3
    public function updateDocumentIndexes($documentName, $timeout = null)
77
    {
78 3
        $class = $this->dm->getClassMetadata($documentName);
79
80
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
81
            throw new \InvalidArgumentException('Cannot update document indexes for mapped super classes, embedded documents or aggregation result documents.');
82 3
        }
83 3
84 3
        $documentIndexes = $this->getDocumentIndexes($documentName);
85
        $collection = $this->dm->getDocumentCollection($documentName);
86
        $mongoIndexes = iterator_to_array($collection->listIndexes());
87
88
        /* Determine which Mongo indexes should be deleted. Exclude the ID index
89 3
         * and those that are equivalent to any in the class metadata.
90
         */
91 1
        $self = $this;
92
        $mongoIndexes = array_filter($mongoIndexes, function (IndexInfo $mongoIndex) use ($documentIndexes, $self) {
93
            if ($mongoIndex['name'] === '_id_') {
94
                return false;
95 1
            }
96 1
97 1
            foreach ($documentIndexes as $documentIndex) {
98
                if ($self->isMongoIndexEquivalentToDocumentIndex($mongoIndex, $documentIndex)) {
99
                    return false;
100
                }
101 1
            }
102 3
103
            return true;
104
        });
105 3
106 1
        // Delete indexes that do not exist in class metadata
107
        foreach ($mongoIndexes as $mongoIndex) {
108
            if (! isset($mongoIndex['name'])) {
109
                continue;
110 1
            }
111
112
            $collection->dropIndex($mongoIndex['name']);
113 3
        }
114 3
115
        $this->ensureDocumentIndexes($documentName, $timeout);
116
    }
117
118
    /**
119
     * @param string $documentName
120 19
     * @return array
121
     */
122 19
    public function getDocumentIndexes($documentName)
123 19
    {
124
        $visited = [];
125
        return $this->doGetDocumentIndexes($documentName, $visited);
126
    }
127
128
    /**
129
     * @param string $documentName
130
     * @param array  $visited
131 19
     * @return array
132
     */
133 19
    private function doGetDocumentIndexes($documentName, array &$visited)
134 1
    {
135
        if (isset($visited[$documentName])) {
136
            return [];
137 19
        }
138
139 19
        $visited[$documentName] = true;
140 19
141 19
        $class = $this->dm->getClassMetadata($documentName);
142
        $indexes = $this->prepareIndexes($class);
143
        $embeddedDocumentIndexes = [];
144 19
145 19
        // Add indexes from embedded & referenced documents
146 3
        foreach ($class->fieldMappings as $fieldMapping) {
147 3
            if (isset($fieldMapping['embedded'])) {
148 2
                if (isset($fieldMapping['targetDocument'])) {
149 1
                    $possibleEmbeds = [$fieldMapping['targetDocument']];
150
                } elseif (isset($fieldMapping['discriminatorMap'])) {
151 1
                    $possibleEmbeds = array_unique($fieldMapping['discriminatorMap']);
152
                } else {
153 3
                    continue;
154 3
                }
155 2
156
                foreach ($possibleEmbeds as $embed) {
157 3
                    if (isset($embeddedDocumentIndexes[$embed])) {
158 3
                        $embeddedIndexes = $embeddedDocumentIndexes[$embed];
159
                    } else {
160 3
                        $embeddedIndexes = $this->doGetDocumentIndexes($embed, $visited);
161 2
                        $embeddedDocumentIndexes[$embed] = $embeddedIndexes;
162 2
                    }
163 2
164
                    foreach ($embeddedIndexes as $embeddedIndex) {
165 3
                        foreach ($embeddedIndex['keys'] as $key => $value) {
166
                            $embeddedIndex['keys'][$fieldMapping['name'] . '.' . $key] = $value;
167
                            unset($embeddedIndex['keys'][$key]);
168 19
                        }
169 8
170 8
                        $indexes[] = $embeddedIndex;
171 8
                    }
172 8
                }
173 2
            } elseif (isset($fieldMapping['reference']) && isset($fieldMapping['targetDocument'])) {
174
                foreach ($indexes as $idx => $index) {
175 8
                    $newKeys = [];
176
                    foreach ($index['keys'] as $key => $v) {
177 19
                        if ($key === $fieldMapping['name']) {
178
                            $key = ClassMetadata::getReferenceFieldName($fieldMapping['storeAs'], $key);
179
                        }
180
181 19
                        $newKeys[$key] = $v;
182
                    }
183
184
                    $indexes[$idx]['keys'] = $newKeys;
185
                }
186
            }
187 19
        }
188
189 19
        return $indexes;
190 19
    }
191 19
192
    /**
193 19
     * @return array
194
     */
195 19
    private function prepareIndexes(ClassMetadata $class)
196 19
    {
197
        $persister = $this->dm->getUnitOfWork()->getDocumentPersister($class->name);
198 19
        $indexes = $class->getIndexes();
199 19
        $newIndexes = [];
200 19
201 17
        foreach ($indexes as $index) {
202 17
            $newIndex = [
203
                'keys' => [],
204 19
                'options' => $index['options'],
205
            ];
206
207
            foreach ($index['keys'] as $key => $value) {
208 19
                $key = $persister->prepareFieldName($key);
209
                if ($class->hasField($key)) {
210
                    $mapping = $class->getFieldMapping($key);
211 19
                    $newIndex['keys'][$mapping['name']] = $value;
212
                } else {
213
                    $newIndex['keys'][$key] = $value;
214
                }
215
            }
216
217
            $newIndexes[] = $newIndex;
218
        }
219
220
        return $newIndexes;
221 15
    }
222
223 15
    /**
224 15
     * Ensure the given document's indexes are created.
225
     *
226
     * @param string $documentName
227
     * @param int    $timeout      Timeout (ms) for acknowledged index creation
228 15
     * @throws \InvalidArgumentException
229 15
     */
230 3
    public function ensureDocumentIndexes($documentName, $timeout = null)
231
    {
232
        $class = $this->dm->getClassMetadata($documentName);
233 15
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
234 15
            throw new \InvalidArgumentException('Cannot create document indexes for mapped super classes, embedded documents or query result documents.');
235 15
        }
236 15
237
        if ($class->isFile) {
238 15
            $this->ensureGridFSIndexes($class);
239 1
        }
240
241
        $indexes = $this->getDocumentIndexes($documentName);
242 15
        if (! $indexes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $indexes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
243
            return;
244 15
        }
245
246
        $collection = $this->dm->getDocumentCollection($class->name);
247
        foreach ($indexes as $index) {
248
            $keys = $index['keys'];
249
            $options = $index['options'];
250 1
251
            if (! isset($options['timeout']) && isset($timeout)) {
252 1
                $options['timeout'] = $timeout;
253 1
            }
254 1
255
            $collection->createIndex($keys, $options);
256 1
        }
257
    }
258 1
259
    /**
260
     * Delete indexes for all documents that can be loaded with the
261
     * metadata factory.
262
     */
263 View Code Duplication
    public function deleteIndexes()
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...
264
    {
265
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
266 2
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
267
                continue;
268 2
            }
269 2
270
            $this->deleteDocumentIndexes($class->name);
271
        }
272 2
    }
273 2
274
    /**
275
     * Delete the given document's indexes.
276
     *
277
     * @param string $documentName
278 1
     * @throws \InvalidArgumentException
279
     */
280 1 View Code Duplication
    public function deleteDocumentIndexes($documentName)
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...
281 1
    {
282 1
        $class = $this->dm->getClassMetadata($documentName);
283
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
284 1
            throw new \InvalidArgumentException('Cannot delete document indexes for mapped super classes, embedded documents or query result documents.');
285
        }
286 1
287
        $this->dm->getDocumentCollection($documentName)->dropIndexes();
288
    }
289
290
    /**
291
     * Create all the mapped document collections in the metadata factory.
292
     */
293 View Code Duplication
    public function createCollections()
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...
294 4
    {
295
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
296 4
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
297
                continue;
298 4
            }
299
            $this->createDocumentCollection($class->name);
300
        }
301
    }
302 4
303 4
    /**
304
     * Create the document collection for a mapped class.
305 4
     *
306 4
     * @param string $documentName
307 4
     * @throws \InvalidArgumentException
308
     */
309
    public function createDocumentCollection($documentName)
310 4
    {
311
        $class = $this->dm->getClassMetadata($documentName);
312
313
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
314
            throw new \InvalidArgumentException('Cannot create document collection for mapped super classes, embedded documents or query result documents.');
315 1
        }
316
317 1
        if ($class->isFile) {
318 1
            $this->dm->getDocumentDatabase($documentName)->createCollection($class->getCollection() . '.files');
319 1
            $this->dm->getDocumentDatabase($documentName)->createCollection($class->getCollection() . '.chunks');
320
321 1
            return;
322
        }
323 1
324
        $this->dm->getDocumentDatabase($documentName)->createCollection(
325
            $class->getCollection(),
326
            [
327
                'capped' => $class->getCollectionCapped(),
328
                'size' => $class->getCollectionSize(),
329
                'max' => $class->getCollectionMax(),
330
            ]
331 4
        );
332
    }
333 4
334 4
    /**
335
     * Drop all the mapped document collections in the metadata factory.
336
     */
337 4 View Code Duplication
    public function dropCollections()
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...
338 4
    {
339
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
340
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
341
                continue;
342
            }
343 1
344
            $this->dropDocumentCollection($class->name);
345 1
        }
346 1
    }
347 1
348
    /**
349 1
     * Drop the document collection for a mapped class.
350
     *
351 1
     * @param string $documentName
352
     * @throws \InvalidArgumentException
353
     */
354
    public function dropDocumentCollection($documentName)
355
    {
356
        $class = $this->dm->getClassMetadata($documentName);
357
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
358
            throw new \InvalidArgumentException('Cannot delete document indexes for mapped super classes, embedded documents or query result documents.');
359 2
        }
360
361 2
        $this->dm->getDocumentCollection($documentName)->drop();
362 2
363
        if (! $class->isFile) {
364
            return;
365 2
        }
366 2
367
        $this->dm->getDocumentBucket($documentName)->getChunksCollection()->drop();
368
    }
369
370
    /**
371
     * Drop all the mapped document databases in the metadata factory.
372
     */
373 View Code Duplication
    public function dropDatabases()
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...
374
    {
375
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
376
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
377
                continue;
378
            }
379
380
            $this->dropDocumentDatabase($class->name);
381
        }
382
    }
383
384
    /**
385
     * Drop the document database for a mapped class.
386
     *
387
     * @param string $documentName
388
     * @throws \InvalidArgumentException
389
     */
390 30 View Code Duplication
    public function dropDocumentDatabase($documentName)
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...
391
    {
392 30
        $class = $this->dm->getClassMetadata($documentName);
393
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
394 30
            throw new \InvalidArgumentException('Cannot drop document database for mapped super classes, embedded documents or query result documents.');
395 2
        }
396
397
        $this->dm->getDocumentDatabase($documentName)->drop();
398 28
    }
399 2
400
    /**
401
     * Determine if an index returned by MongoCollection::getIndexInfo() can be
402 26
     * considered equivalent to an index in class metadata.
403 2
     *
404
     * Indexes are considered different if:
405
     *
406 24
     *   (a) Key/direction pairs differ or are not in the same order
407 24
     *   (b) Sparse or unique options differ
408 1
     *   (c) Mongo index is unique without dropDups and mapped index is unique
409
     *       with dropDups
410
     *   (d) Geospatial options differ (bits, max, min)
411 23
     *   (e) The partialFilterExpression differs
412 23
     *
413 6
     * Regarding (c), the inverse case is not a reason to delete and
414
     * recreate the index, since dropDups only affects creation of
415
     * the unique index. Additionally, the background option is only
416 21
     * relevant to index creation and is not considered.
417 21
     *
418 21
     * @param array|IndexInfo $mongoIndex    Mongo index data.
419
     * @param array           $documentIndex Document index data.
420
     * @return bool True if the indexes are equivalent, otherwise false.
421
     */
422 14
    public function isMongoIndexEquivalentToDocumentIndex($mongoIndex, $documentIndex)
423 2
    {
424
        $documentIndexOptions = $documentIndex['options'];
425
426 12
        if ($mongoIndex['key'] !== $documentIndex['keys']) {
427 12
            return false;
428 1
        }
429
430
        if (empty($mongoIndex['sparse']) xor empty($documentIndexOptions['sparse'])) {
431 11
            return false;
432
        }
433
434
        if (empty($mongoIndex['unique']) xor empty($documentIndexOptions['unique'])) {
435
            return false;
436
        }
437
438
        if (! empty($mongoIndex['unique']) && empty($mongoIndex['dropDups']) &&
439
            ! empty($documentIndexOptions['unique']) && ! empty($documentIndexOptions['dropDups'])) {
440
            return false;
441
        }
442
443
        foreach (['bits', 'max', 'min'] as $option) {
444
            if (isset($mongoIndex[$option]) xor isset($documentIndexOptions[$option])) {
445
                return false;
446
            }
447
448
            if (isset($mongoIndex[$option]) && isset($documentIndexOptions[$option]) &&
449
                $mongoIndex[$option] !== $documentIndexOptions[$option]) {
450
                return false;
451
            }
452
        }
453
454
        if (empty($mongoIndex['partialFilterExpression']) xor empty($documentIndexOptions['partialFilterExpression'])) {
455
            return false;
456
        }
457
458
        if (isset($mongoIndex['partialFilterExpression']) && isset($documentIndexOptions['partialFilterExpression']) &&
459
            $mongoIndex['partialFilterExpression'] !== $documentIndexOptions['partialFilterExpression']) {
460
            return false;
461 2
        }
462
463 2
        return true;
464 2
    }
465
466
    /**
467
     * Ensure collections are sharded for all documents that can be loaded with the
468 2
     * metadata factory.
469
     *
470 2
     * @param array $indexOptions Options for `ensureIndex` command. It's performed on an existing collections
471
     *
472
     * @throws MongoDBException
473 2
     */
474 2
    public function ensureSharding(array $indexOptions = [])
475
    {
476
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
477 2
            if ($class->isMappedSuperclass || ! $class->isSharded()) {
478
                continue;
479
            }
480
481
            $this->ensureDocumentSharding($class->name, $indexOptions);
482
        }
483 2
    }
484
485 1
    /**
486 1
     * Ensure sharding for collection by document name.
487 1
     *
488
     * @param string $documentName
489
     * @param array  $indexOptions Options for `ensureIndex` command. It's performed on an existing collections.
490
     *
491
     * @throws MongoDBException
492 2
     */
493
    public function ensureDocumentSharding($documentName, array $indexOptions = [])
494
    {
495
        $class = $this->dm->getClassMetadata($documentName);
496 2
        if (! $class->isSharded()) {
497 2
            return;
498
        }
499
500
        $this->enableShardingForDbByDocumentName($documentName);
501
502
        $try = 0;
503
        do {
504
            try {
505
                $result = $this->runShardCollectionCommand($documentName);
506
                $done = true;
507
508
                // Need to check error message because MongoDB 3.0 does not return a code for this error
509
                if (! (bool) $result['ok'] && strpos($result['errmsg'], 'please create an index that starts') !== false) {
510 2
                    // The proposed key is not returned when using mongo-php-adapter with ext-mongodb.
511
                    // See https://github.com/mongodb/mongo-php-driver/issues/296 for details
512 2
                    $key = $result['proposedKey'] ?? $this->dm->getClassMetadata($documentName)->getShardKey()['keys'];
513 2
514
                    $this->dm->getDocumentCollection($documentName)->ensureIndex($key, $indexOptions);
515
                    $done = false;
516 2
                }
517 1
            } catch (RuntimeException $e) {
0 ignored issues
show
Bug introduced by
The class MongoDB\Driver\Exception\RuntimeException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
518 1
                if ($e->getCode() === 20 || $e->getCode() === 23 || $e->getMessage() === 'already sharded') {
519
                    return;
520
                }
521
522 2
                throw $e;
523
            }
524
        } while (! $done && $try < 2);
0 ignored issues
show
introduced by
It seems like the condition $try < 2 is always satisfied by any possible value of $try. Are you sure you do not have a deadlock here?
Loading history...
525
526
        // Starting with MongoDB 3.2, this command returns code 20 when a collection is already sharded.
527
        // For older MongoDB versions, check the error message
528
        if ((bool) $result['ok'] || (isset($result['code']) && $result['code'] === 20) || $result['errmsg'] === 'already sharded') {
529 2
            return;
530
        }
531 2
532 2
        throw MongoDBException::failedToEnsureDocumentSharding($documentName, $result['errmsg']);
533 2
    }
534 2
535
    /**
536 2
     * Enable sharding for database which contains documents with given name.
537
     *
538 2
     * @param string $documentName
539 2
     *
540
     * @throws MongoDBException
541 2
     */
542
    public function enableShardingForDbByDocumentName($documentName)
543 2
    {
544
        $dbName = $this->dm->getDocumentDatabase($documentName)->getDatabaseName();
545
        $adminDb = $this->dm->getClient()->selectDatabase('admin');
546
547
        try {
548
            $adminDb->command(['enableSharding' => $dbName]);
549
        } catch (RuntimeException $e) {
0 ignored issues
show
Bug introduced by
The class MongoDB\Driver\Exception\RuntimeException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
550
            if ($e->getCode() !== 23 || $e->getMessage() === 'already enabled') {
551
                throw MongoDBException::failedToEnableSharding($dbName, $e->getMessage());
552
            }
553
        }
554
    }
555
556
    /**
557
     * @param string $documentName
558
     *
559
     * @return array
560
     */
561
    private function runShardCollectionCommand($documentName)
562
    {
563
        $class = $this->dm->getClassMetadata($documentName);
564
        $dbName = $this->dm->getDocumentDatabase($documentName)->getDatabaseName();
565
        $shardKey = $class->getShardKey();
566
        $adminDb = $this->dm->getClient()->selectDatabase('admin');
567
568
        $result = $adminDb->command(
569
            [
570
                'shardCollection' => $dbName . '.' . $class->getCollection(),
571
                'key'             => $shardKey['keys'],
572
            ]
573
        )->toArray()[0];
574
575
        return $result;
576
    }
577
578
    private function ensureGridFSIndexes(ClassMetadata $class): void
579
    {
580
        $this->ensureChunksIndex($class);
581
        $this->ensureFilesIndex($class);
582
    }
583
584
    private function ensureChunksIndex(ClassMetadata $class): void
585
    {
586
        $chunksCollection = $this->dm->getDocumentBucket($class->getName())->getChunksCollection();
0 ignored issues
show
Bug introduced by
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
587
        foreach ($chunksCollection->listIndexes() as $index) {
588
            if ($index->isUnique() && $index->getKey() === ['files_id' => 1, 'n' => 1]) {
589
                return;
590
            }
591
        }
592
593
        $chunksCollection->createIndex(['files_id' => 1, 'n' => 1], ['unique' => true]);
594
    }
595
596
    private function ensureFilesIndex(ClassMetadata $class): void
597
    {
598
        $filesCollection = $this->dm->getDocumentCollection($class->getName());
0 ignored issues
show
Bug introduced by
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
599
        foreach ($filesCollection->listIndexes() as $index) {
600
            if ($index->getKey() === ['filename' => 1, 'uploadDate' => 1]) {
601
                return;
602
            }
603
        }
604
605
        $filesCollection->createIndex(['filename' => 1, 'uploadDate' => 1]);
606
    }
607
}
608