Completed
Push — master ( 08b9e1...e0c601 )
by Andreas
13s
created

SchemaManager   F

Complexity

Total Complexity 124

Size/Duplication

Total Lines 530
Duplicated Lines 14.72 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 88.29%

Importance

Changes 0
Metric Value
wmc 124
lcom 1
cbo 5
dl 78
loc 530
ccs 196
cts 222
cp 0.8829
rs 2
c 0
b 0
f 0

21 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A ensureIndexes() 9 9 5
A updateIndexes() 9 9 5
B updateDocumentIndexes() 0 41 9
A getDocumentIndexes() 0 5 1
C doGetDocumentIndexes() 0 52 15
A prepareIndexes() 0 26 4
B ensureDocumentIndexes() 0 24 8
A deleteIndexes() 9 9 5
A deleteDocumentIndexes() 8 8 4
A createCollections() 9 9 5
A createDocumentCollection() 0 17 4
A dropCollections() 9 9 5
A dropDocumentCollection() 8 8 4
A dropDatabases() 9 9 5
A dropDocumentDatabase() 8 8 4
C isMongoIndexEquivalentToDocumentIndex() 0 43 17
A ensureSharding() 0 10 4
C ensureDocumentSharding() 0 41 14
A enableShardingForDbByDocumentName() 0 13 4
A runShardCollectionCommand() 0 16 1

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 1627
    public function __construct(DocumentManager $dm, ClassMetadataFactory $cmf)
25
    {
26 1627
        $this->dm = $dm;
27 1627
        $this->metadataFactory = $cmf;
28 1627
    }
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
            $this->ensureDocumentIndexes($class->name, $timeout);
43
        }
44 1
    }
45
46
    /**
47
     * Ensure indexes exist for all mapped document classes.
48
     *
49
     * Indexes that exist in MongoDB but not the document metadata will be
50
     * deleted.
51
     *
52
     * @param int $timeout Timeout (ms) for acknowledged index creation
53
     */
54 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...
55
    {
56
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
57
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
58
                continue;
59
            }
60
            $this->updateDocumentIndexes($class->name, $timeout);
61
        }
62
    }
63
64
    /**
65
     * Ensure indexes exist for the mapped document class.
66
     *
67
     * Indexes that exist in MongoDB but not the document metadata will be
68
     * deleted.
69
     *
70
     * @param string $documentName
71
     * @param int    $timeout      Timeout (ms) for acknowledged index creation
72
     * @throws \InvalidArgumentException
73
     */
74 3
    public function updateDocumentIndexes($documentName, $timeout = null)
75
    {
76 3
        $class = $this->dm->getClassMetadata($documentName);
77
78 3
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
79
            throw new \InvalidArgumentException('Cannot update document indexes for mapped super classes, embedded documents or aggregation result documents.');
80
        }
81
82 3
        $documentIndexes = $this->getDocumentIndexes($documentName);
83 3
        $collection = $this->dm->getDocumentCollection($documentName);
84 3
        $mongoIndexes = iterator_to_array($collection->listIndexes());
85
86
        /* Determine which Mongo indexes should be deleted. Exclude the ID index
87
         * and those that are equivalent to any in the class metadata.
88
         */
89 3
        $self = $this;
90
        $mongoIndexes = array_filter($mongoIndexes, function (IndexInfo $mongoIndex) use ($documentIndexes, $self) {
91 1
            if ($mongoIndex['name'] === '_id_') {
92
                return false;
93
            }
94
95 1
            foreach ($documentIndexes as $documentIndex) {
96 1
                if ($self->isMongoIndexEquivalentToDocumentIndex($mongoIndex, $documentIndex)) {
97 1
                    return false;
98
                }
99
            }
100
101 1
            return true;
102 3
        });
103
104
        // Delete indexes that do not exist in class metadata
105 3
        foreach ($mongoIndexes as $mongoIndex) {
106 1
            if (! isset($mongoIndex['name'])) {
107
                continue;
108
            }
109
110 1
            $collection->dropIndex($mongoIndex['name']);
111
        }
112
113 3
        $this->ensureDocumentIndexes($documentName, $timeout);
114 3
    }
115
116
    /**
117
     * @param string $documentName
118
     * @return array
119
     */
120 19
    public function getDocumentIndexes($documentName)
121
    {
122 19
        $visited = [];
123 19
        return $this->doGetDocumentIndexes($documentName, $visited);
124
    }
125
126
    /**
127
     * @param string $documentName
128
     * @param array  $visited
129
     * @return array
130
     */
131 19
    private function doGetDocumentIndexes($documentName, array &$visited)
132
    {
133 19
        if (isset($visited[$documentName])) {
134 1
            return [];
135
        }
136
137 19
        $visited[$documentName] = true;
138
139 19
        $class = $this->dm->getClassMetadata($documentName);
140 19
        $indexes = $this->prepareIndexes($class);
141 19
        $embeddedDocumentIndexes = [];
142
143
        // Add indexes from embedded & referenced documents
144 19
        foreach ($class->fieldMappings as $fieldMapping) {
145 19
            if (isset($fieldMapping['embedded'])) {
146 3
                if (isset($fieldMapping['targetDocument'])) {
147 3
                    $possibleEmbeds = [$fieldMapping['targetDocument']];
148 2
                } elseif (isset($fieldMapping['discriminatorMap'])) {
149 1
                    $possibleEmbeds = array_unique($fieldMapping['discriminatorMap']);
150
                } else {
151 1
                    continue;
152
                }
153 3
                foreach ($possibleEmbeds as $embed) {
154 3
                    if (isset($embeddedDocumentIndexes[$embed])) {
155 2
                        $embeddedIndexes = $embeddedDocumentIndexes[$embed];
156
                    } else {
157 3
                        $embeddedIndexes = $this->doGetDocumentIndexes($embed, $visited);
158 3
                        $embeddedDocumentIndexes[$embed] = $embeddedIndexes;
159
                    }
160 3
                    foreach ($embeddedIndexes as $embeddedIndex) {
161 2
                        foreach ($embeddedIndex['keys'] as $key => $value) {
162 2
                            $embeddedIndex['keys'][$fieldMapping['name'] . '.' . $key] = $value;
163 2
                            unset($embeddedIndex['keys'][$key]);
164
                        }
165 3
                        $indexes[] = $embeddedIndex;
166
                    }
167
                }
168 19
            } elseif (isset($fieldMapping['reference']) && isset($fieldMapping['targetDocument'])) {
169 8
                foreach ($indexes as $idx => $index) {
170 8
                    $newKeys = [];
171 8
                    foreach ($index['keys'] as $key => $v) {
172 8
                        if ($key === $fieldMapping['name']) {
173 2
                            $key = ClassMetadata::getReferenceFieldName($fieldMapping['storeAs'], $key);
174
                        }
175 8
                        $newKeys[$key] = $v;
176
                    }
177 19
                    $indexes[$idx]['keys'] = $newKeys;
178
                }
179
            }
180
        }
181 19
        return $indexes;
182
    }
183
184
    /**
185
     * @return array
186
     */
187 19
    private function prepareIndexes(ClassMetadata $class)
188
    {
189 19
        $persister = $this->dm->getUnitOfWork()->getDocumentPersister($class->name);
190 19
        $indexes = $class->getIndexes();
191 19
        $newIndexes = [];
192
193 19
        foreach ($indexes as $index) {
194
            $newIndex = [
195 19
                'keys' => [],
196 19
                'options' => $index['options'],
197
            ];
198 19
            foreach ($index['keys'] as $key => $value) {
199 19
                $key = $persister->prepareFieldName($key);
200 19
                if ($class->hasField($key)) {
201 17
                    $mapping = $class->getFieldMapping($key);
202 17
                    $newIndex['keys'][$mapping['name']] = $value;
203
                } else {
204 19
                    $newIndex['keys'][$key] = $value;
205
                }
206
            }
207
208 19
            $newIndexes[] = $newIndex;
209
        }
210
211 19
        return $newIndexes;
212
    }
213
214
    /**
215
     * Ensure the given document's indexes are created.
216
     *
217
     * @param string $documentName
218
     * @param int    $timeout      Timeout (ms) for acknowledged index creation
219
     * @throws \InvalidArgumentException
220
     */
221 15
    public function ensureDocumentIndexes($documentName, $timeout = null)
222
    {
223 15
        $class = $this->dm->getClassMetadata($documentName);
224 15
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
225
            throw new \InvalidArgumentException('Cannot create document indexes for mapped super classes, embedded documents or query result documents.');
226
        }
227
228 15
        $indexes = $this->getDocumentIndexes($documentName);
229 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...
230 3
            return;
231
        }
232
233 15
        $collection = $this->dm->getDocumentCollection($class->name);
234 15
        foreach ($indexes as $index) {
235 15
            $keys = $index['keys'];
236 15
            $options = $index['options'];
237
238 15
            if (! isset($options['timeout']) && isset($timeout)) {
239 1
                $options['timeout'] = $timeout;
240
            }
241
242 15
            $collection->createIndex($keys, $options);
243
        }
244 15
    }
245
246
    /**
247
     * Delete indexes for all documents that can be loaded with the
248
     * metadata factory.
249
     */
250 1 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...
251
    {
252 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
253 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
254 1
                continue;
255
            }
256 1
            $this->deleteDocumentIndexes($class->name);
257
        }
258 1
    }
259
260
    /**
261
     * Delete the given document's indexes.
262
     *
263
     * @param string $documentName
264
     * @throws \InvalidArgumentException
265
     */
266 2 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...
267
    {
268 2
        $class = $this->dm->getClassMetadata($documentName);
269 2
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
270
            throw new \InvalidArgumentException('Cannot delete document indexes for mapped super classes, embedded documents or query result documents.');
271
        }
272 2
        $this->dm->getDocumentCollection($documentName)->dropIndexes();
273 2
    }
274
275
    /**
276
     * Create all the mapped document collections in the metadata factory.
277
     */
278 1 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...
279
    {
280 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
281 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
282 1
                continue;
283
            }
284 1
            $this->createDocumentCollection($class->name);
285
        }
286 1
    }
287
288
    /**
289
     * Create the document collection for a mapped class.
290
     *
291
     * @param string $documentName
292
     * @throws \InvalidArgumentException
293
     */
294 4
    public function createDocumentCollection($documentName)
295
    {
296 4
        $class = $this->dm->getClassMetadata($documentName);
297
298 4
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
299
            throw new \InvalidArgumentException('Cannot create document collection for mapped super classes, embedded documents or query result documents.');
300
        }
301
302 4
        $this->dm->getDocumentDatabase($documentName)->createCollection(
303 4
            $class->getCollection(),
304
            [
305 4
                'capped' => $class->getCollectionCapped(),
306 4
                'size' => $class->getCollectionSize(),
307 4
                'max' => $class->getCollectionMax(),
308
            ]
309
        );
310 4
    }
311
312
    /**
313
     * Drop all the mapped document collections in the metadata factory.
314
     */
315 1 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...
316
    {
317 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
318 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
319 1
                continue;
320
            }
321 1
            $this->dropDocumentCollection($class->name);
322
        }
323 1
    }
324
325
    /**
326
     * Drop the document collection for a mapped class.
327
     *
328
     * @param string $documentName
329
     * @throws \InvalidArgumentException
330
     */
331 4 View Code Duplication
    public function dropDocumentCollection($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...
332
    {
333 4
        $class = $this->dm->getClassMetadata($documentName);
334 4
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
335
            throw new \InvalidArgumentException('Cannot delete document indexes for mapped super classes, embedded documents or query result documents.');
336
        }
337 4
        $this->dm->getDocumentCollection($documentName)->drop();
338 4
    }
339
340
    /**
341
     * Drop all the mapped document databases in the metadata factory.
342
     */
343 1 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...
344
    {
345 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
346 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
347 1
                continue;
348
            }
349 1
            $this->dropDocumentDatabase($class->name);
350
        }
351 1
    }
352
353
    /**
354
     * Drop the document database for a mapped class.
355
     *
356
     * @param string $documentName
357
     * @throws \InvalidArgumentException
358
     */
359 2 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...
360
    {
361 2
        $class = $this->dm->getClassMetadata($documentName);
362 2
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
363
            throw new \InvalidArgumentException('Cannot drop document database for mapped super classes, embedded documents or query result documents.');
364
        }
365 2
        $this->dm->getDocumentDatabase($documentName)->drop();
366 2
    }
367
368
    /**
369
     * Determine if an index returned by MongoCollection::getIndexInfo() can be
370
     * considered equivalent to an index in class metadata.
371
     *
372
     * Indexes are considered different if:
373
     *
374
     *   (a) Key/direction pairs differ or are not in the same order
375
     *   (b) Sparse or unique options differ
376
     *   (c) Mongo index is unique without dropDups and mapped index is unique
377
     *       with dropDups
378
     *   (d) Geospatial options differ (bits, max, min)
379
     *   (e) The partialFilterExpression differs
380
     *
381
     * Regarding (c), the inverse case is not a reason to delete and
382
     * recreate the index, since dropDups only affects creation of
383
     * the unique index. Additionally, the background option is only
384
     * relevant to index creation and is not considered.
385
     *
386
     * @param array|IndexInfo $mongoIndex    Mongo index data.
387
     * @param array           $documentIndex Document index data.
388
     * @return bool True if the indexes are equivalent, otherwise false.
389
     */
390 30
    public function isMongoIndexEquivalentToDocumentIndex($mongoIndex, $documentIndex)
391
    {
392 30
        $documentIndexOptions = $documentIndex['options'];
393
394 30
        if ($mongoIndex['key'] !== $documentIndex['keys']) {
395 2
            return false;
396
        }
397
398 28
        if (empty($mongoIndex['sparse']) xor empty($documentIndexOptions['sparse'])) {
399 2
            return false;
400
        }
401
402 26
        if (empty($mongoIndex['unique']) xor empty($documentIndexOptions['unique'])) {
403 2
            return false;
404
        }
405
406 24
        if (! empty($mongoIndex['unique']) && empty($mongoIndex['dropDups']) &&
407 24
            ! empty($documentIndexOptions['unique']) && ! empty($documentIndexOptions['dropDups'])) {
408 1
            return false;
409
        }
410
411 23
        foreach (['bits', 'max', 'min'] as $option) {
412 23
            if (isset($mongoIndex[$option]) xor isset($documentIndexOptions[$option])) {
413 6
                return false;
414
            }
415
416 21
            if (isset($mongoIndex[$option]) && isset($documentIndexOptions[$option]) &&
417 21
                $mongoIndex[$option] !== $documentIndexOptions[$option]) {
418 21
                return false;
419
            }
420
        }
421
422 14
        if (empty($mongoIndex['partialFilterExpression']) xor empty($documentIndexOptions['partialFilterExpression'])) {
423 2
            return false;
424
        }
425
426 12
        if (isset($mongoIndex['partialFilterExpression']) && isset($documentIndexOptions['partialFilterExpression']) &&
427 12
            $mongoIndex['partialFilterExpression'] !== $documentIndexOptions['partialFilterExpression']) {
428 1
            return false;
429
        }
430
431 11
        return true;
432
    }
433
434
    /**
435
     * Ensure collections are sharded for all documents that can be loaded with the
436
     * metadata factory.
437
     *
438
     * @param array $indexOptions Options for `ensureIndex` command. It's performed on an existing collections
439
     *
440
     * @throws MongoDBException
441
     */
442
    public function ensureSharding(array $indexOptions = [])
443
    {
444
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
445
            if ($class->isMappedSuperclass || ! $class->isSharded()) {
446
                continue;
447
            }
448
449
            $this->ensureDocumentSharding($class->name, $indexOptions);
450
        }
451
    }
452
453
    /**
454
     * Ensure sharding for collection by document name.
455
     *
456
     * @param string $documentName
457
     * @param array  $indexOptions Options for `ensureIndex` command. It's performed on an existing collections.
458
     *
459
     * @throws MongoDBException
460
     */
461 2
    public function ensureDocumentSharding($documentName, array $indexOptions = [])
462
    {
463 2
        $class = $this->dm->getClassMetadata($documentName);
464 2
        if (! $class->isSharded()) {
465
            return;
466
        }
467
468 2
        $this->enableShardingForDbByDocumentName($documentName);
469
470 2
        $try = 0;
471
        do {
472
            try {
473 2
                $result = $this->runShardCollectionCommand($documentName);
474 2
                $done = true;
475
476
                // Need to check error message because MongoDB 3.0 does not return a code for this error
477 2
                if (! (bool) $result['ok'] && strpos($result['errmsg'], 'please create an index that starts') !== false) {
478
                    // The proposed key is not returned when using mongo-php-adapter with ext-mongodb.
479
                    // See https://github.com/mongodb/mongo-php-driver/issues/296 for details
480
                    $key = $result['proposedKey'] ?? $this->dm->getClassMetadata($documentName)->getShardKey()['keys'];
481
482
                    $this->dm->getDocumentCollection($documentName)->ensureIndex($key, $indexOptions);
483 2
                    $done = false;
484
                }
485 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...
486 1
                if ($e->getCode() === 20 || $e->getCode() === 23 || $e->getMessage() === 'already sharded') {
487 1
                    return;
488
                }
489
490
                throw $e;
491
            }
492 2
        } 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...
493
494
        // Starting with MongoDB 3.2, this command returns code 20 when a collection is already sharded.
495
        // For older MongoDB versions, check the error message
496 2
        if ((bool) $result['ok'] || (isset($result['code']) && $result['code'] === 20) || $result['errmsg'] === 'already sharded') {
497 2
            return;
498
        }
499
500
        throw MongoDBException::failedToEnsureDocumentSharding($documentName, $result['errmsg']);
501
    }
502
503
    /**
504
     * Enable sharding for database which contains documents with given name.
505
     *
506
     * @param string $documentName
507
     *
508
     * @throws MongoDBException
509
     */
510 2
    public function enableShardingForDbByDocumentName($documentName)
511
    {
512 2
        $dbName = $this->dm->getDocumentDatabase($documentName)->getDatabaseName();
513 2
        $adminDb = $this->dm->getClient()->selectDatabase('admin');
514
515
        try {
516 2
            $adminDb->command(['enableSharding' => $dbName]);
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() !== 23 || $e->getMessage() === 'already enabled') {
519
                throw MongoDBException::failedToEnableSharding($dbName, $e->getMessage());
520
            }
521
        }
522 2
    }
523
524
    /**
525
     * @param string $documentName
526
     *
527
     * @return array
528
     */
529 2
    private function runShardCollectionCommand($documentName)
530
    {
531 2
        $class = $this->dm->getClassMetadata($documentName);
532 2
        $dbName = $this->dm->getDocumentDatabase($documentName)->getDatabaseName();
533 2
        $shardKey = $class->getShardKey();
534 2
        $adminDb = $this->dm->getClient()->selectDatabase('admin');
535
536 2
        $result = $adminDb->command(
537
            [
538 2
                'shardCollection' => $dbName . '.' . $class->getCollection(),
539 2
                'key'             => $shardKey['keys'],
540
            ]
541 2
        )->toArray()[0];
542
543 2
        return $result;
544
    }
545
}
546