Completed
Push — master ( ce8c34...f078de )
by Andreas
49:52 queued 47:00
created

SchemaManager::createDocumentDatabase()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 0
cts 9
cp 0
rs 9.2
c 0
b 0
f 0
cc 4
eloc 8
nc 2
nop 1
crap 20
1
<?php
2
3
namespace Doctrine\ODM\MongoDB;
4
5
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
6
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory;
7
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo;
8
use MongoDB\Driver\Exception\RuntimeException;
9
use MongoDB\Driver\WriteConcern;
10
use MongoDB\Model\IndexInfo;
11
12
class SchemaManager
13
{
14
    /**
15
     * @var DocumentManager
16
     */
17
    protected $dm;
18
19
    /**
20
     *
21
     * @var ClassMetadataFactory
22
     */
23
    protected $metadataFactory;
24
25
    /**
26
     * @param DocumentManager $dm
27
     * @param ClassMetadataFactory $cmf
28
     */
29 1670
    public function __construct(DocumentManager $dm, ClassMetadataFactory $cmf)
30
    {
31 1670
        $this->dm = $dm;
32 1670
        $this->metadataFactory = $cmf;
33 1670
    }
34
35
    /**
36
     * Ensure indexes are created for all documents that can be loaded with the
37
     * metadata factory.
38
     *
39
     * @param integer $timeout Timeout (ms) for acknowledged index creation
40
     */
41 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...
42
    {
43 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
44 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
45 1
                continue;
46
            }
47 1
            $this->ensureDocumentIndexes($class->name, $timeout);
48
        }
49 1
    }
50
51
    /**
52
     * Ensure indexes exist for all mapped document classes.
53
     *
54
     * Indexes that exist in MongoDB but not the document metadata will be
55
     * deleted.
56
     *
57
     * @param integer $timeout Timeout (ms) for acknowledged index creation
58
     */
59 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...
60
    {
61
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
62
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
63
                continue;
64
            }
65
            $this->updateDocumentIndexes($class->name, $timeout);
66
        }
67
    }
68
69
    /**
70
     * Ensure indexes exist for the mapped document class.
71
     *
72
     * Indexes that exist in MongoDB but not the document metadata will be
73
     * deleted.
74
     *
75
     * @param string $documentName
76
     * @param integer $timeout Timeout (ms) for acknowledged index creation
77
     * @throws \InvalidArgumentException
78
     */
79 3
    public function updateDocumentIndexes($documentName, $timeout = null)
80
    {
81 3
        $class = $this->dm->getClassMetadata($documentName);
82
83 3
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
84
            throw new \InvalidArgumentException('Cannot update document indexes for mapped super classes, embedded documents or aggregation result documents.');
85
        }
86
87 3
        $documentIndexes = $this->getDocumentIndexes($documentName);
88 3
        $collection = $this->dm->getDocumentCollection($documentName);
89 3
        $mongoIndexes = iterator_to_array($collection->listIndexes());
90
91
        /* Determine which Mongo indexes should be deleted. Exclude the ID index
92
         * and those that are equivalent to any in the class metadata.
93
         */
94 3
        $self = $this;
95 3
        $mongoIndexes = array_filter($mongoIndexes, function (IndexInfo $mongoIndex) use ($documentIndexes, $self) {
96 1
            if ('_id_' === $mongoIndex['name']) {
97
                return false;
98
            }
99
100 1
            foreach ($documentIndexes as $documentIndex) {
101 1
                if ($self->isMongoIndexEquivalentToDocumentIndex($mongoIndex, $documentIndex)) {
102 1
                    return false;
103
                }
104
            }
105
106 1
            return true;
107 3
        });
108
109
        // Delete indexes that do not exist in class metadata
110 3
        foreach ($mongoIndexes as $mongoIndex) {
111 1
            if (! isset($mongoIndex['name'])) {
112
                continue;
113
            }
114
115 1
            $collection->dropIndex($mongoIndex['name']);
116
        }
117
118 3
        $this->ensureDocumentIndexes($documentName, $timeout);
119 3
    }
120
121
    /**
122
     * @param string $documentName
123
     * @return array
124
     */
125 19
    public function getDocumentIndexes($documentName)
126
    {
127 19
        $visited = array();
128 19
        return $this->doGetDocumentIndexes($documentName, $visited);
129
    }
130
131
    /**
132
     * @param string $documentName
133
     * @param array $visited
134
     * @return array
135
     */
136 19
    private function doGetDocumentIndexes($documentName, array &$visited)
137
    {
138 19
        if (isset($visited[$documentName])) {
139 1
            return array();
140
        }
141
142 19
        $visited[$documentName] = true;
143
144 19
        $class = $this->dm->getClassMetadata($documentName);
145 19
        $indexes = $this->prepareIndexes($class);
146 19
        $embeddedDocumentIndexes = array();
147
148
        // Add indexes from embedded & referenced documents
149 19
        foreach ($class->fieldMappings as $fieldMapping) {
150 19
            if (isset($fieldMapping['embedded'])) {
151 3
                if (isset($fieldMapping['targetDocument'])) {
152 3
                    $possibleEmbeds = array($fieldMapping['targetDocument']);
153 2
                } elseif (isset($fieldMapping['discriminatorMap'])) {
154 1
                    $possibleEmbeds = array_unique($fieldMapping['discriminatorMap']);
155
                } else {
156 1
                    continue;
157
                }
158 3
                foreach ($possibleEmbeds as $embed) {
159 3
                    if (isset($embeddedDocumentIndexes[$embed])) {
160 2
                        $embeddedIndexes = $embeddedDocumentIndexes[$embed];
161
                    } else {
162 3
                        $embeddedIndexes = $this->doGetDocumentIndexes($embed, $visited);
163 3
                        $embeddedDocumentIndexes[$embed] = $embeddedIndexes;
164
                    }
165 3
                    foreach ($embeddedIndexes as $embeddedIndex) {
166 2
                        foreach ($embeddedIndex['keys'] as $key => $value) {
167 2
                            $embeddedIndex['keys'][$fieldMapping['name'] . '.' . $key] = $value;
168 2
                            unset($embeddedIndex['keys'][$key]);
169
                        }
170 3
                        $indexes[] = $embeddedIndex;
171
                    }
172
                }
173 19
            } elseif (isset($fieldMapping['reference']) && isset($fieldMapping['targetDocument'])) {
174 8
                foreach ($indexes as $idx => $index) {
175 8
                    $newKeys = array();
176 8
                    foreach ($index['keys'] as $key => $v) {
177 8
                        if ($key == $fieldMapping['name']) {
178 2
                            $key = ClassMetadataInfo::getReferenceFieldName($fieldMapping['storeAs'], $key);
179
                        }
180 8
                        $newKeys[$key] = $v;
181
                    }
182 19
                    $indexes[$idx]['keys'] = $newKeys;
183
                }
184
            }
185
        }
186 19
        return $indexes;
187
    }
188
189
    /**
190
     * @param ClassMetadata $class
191
     * @return array
192
     */
193 19
    private function prepareIndexes(ClassMetadata $class)
194
    {
195 19
        $persister = $this->dm->getUnitOfWork()->getDocumentPersister($class->name);
196 19
        $indexes = $class->getIndexes();
197 19
        $newIndexes = array();
198
199 19
        foreach ($indexes as $index) {
200
            $newIndex = array(
201 19
                'keys' => array(),
202 19
                'options' => $index['options']
203
            );
204 19
            foreach ($index['keys'] as $key => $value) {
205 19
                $key = $persister->prepareFieldName($key);
206 19
                if ($class->hasField($key)) {
207 17
                    $mapping = $class->getFieldMapping($key);
208 17
                    $newIndex['keys'][$mapping['name']] = $value;
209
                } else {
210 19
                    $newIndex['keys'][$key] = $value;
211
                }
212
            }
213
214 19
            $newIndexes[] = $newIndex;
215
        }
216
217 19
        return $newIndexes;
218
    }
219
220
    /**
221
     * Ensure the given document's indexes are created.
222
     *
223
     * @param string $documentName
224
     * @param integer $timeout Timeout (ms) for acknowledged index creation
225
     * @throws \InvalidArgumentException
226
     */
227 15
    public function ensureDocumentIndexes($documentName, $timeout = null)
228
    {
229 15
        $class = $this->dm->getClassMetadata($documentName);
230 15
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
231
            throw new \InvalidArgumentException('Cannot create document indexes for mapped super classes, embedded documents or query result documents.');
232
        }
233 15
        if ($indexes = $this->getDocumentIndexes($documentName)) {
234 15
            $collection = $this->dm->getDocumentCollection($class->name);
235 15
            foreach ($indexes as $index) {
236 15
                $keys = $index['keys'];
237 15
                $options = $index['options'];
238
239 15
                if ( ! isset($options['timeout']) && isset($timeout)) {
240 1
                    $options['timeout'] = $timeout;
241
                }
242
243 15
                $collection->createIndex($keys, $options);
244
            }
245
        }
246 15
    }
247
248
    /**
249
     * Delete indexes for all documents that can be loaded with the
250
     * metadata factory.
251
     */
252 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...
253
    {
254 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
255 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
256 1
                continue;
257
            }
258 1
            $this->deleteDocumentIndexes($class->name);
259
        }
260 1
    }
261
262
    /**
263
     * Delete the given document's indexes.
264
     *
265
     * @param string $documentName
266
     * @throws \InvalidArgumentException
267
     */
268 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...
269
    {
270 2
        $class = $this->dm->getClassMetadata($documentName);
271 2
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
272
            throw new \InvalidArgumentException('Cannot delete document indexes for mapped super classes, embedded documents or query result documents.');
273
        }
274 2
        $this->dm->getDocumentCollection($documentName)->dropIndexes();
275 2
    }
276
277
    /**
278
     * Create all the mapped document collections in the metadata factory.
279
     */
280 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...
281
    {
282 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
283 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
284 1
                continue;
285
            }
286 1
            $this->createDocumentCollection($class->name);
287
        }
288 1
    }
289
290
    /**
291
     * Create the document collection for a mapped class.
292
     *
293
     * @param string $documentName
294
     * @throws \InvalidArgumentException
295
     */
296 3
    public function createDocumentCollection($documentName)
297
    {
298 3
        $class = $this->dm->getClassMetadata($documentName);
299
300 3
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
301
            throw new \InvalidArgumentException('Cannot create document collection for mapped super classes, embedded documents or query result documents.');
302
        }
303
304 3
        $this->dm->getDocumentDatabase($documentName)->createCollection(
305 3
            $class->getCollection(),
306
            [
307 3
                'capped' => $class->getCollectionCapped(),
308 3
                'size' => $class->getCollectionSize(),
309 3
                'max' => $class->getCollectionMax(),
310
            ]
311
        );
312 3
    }
313
314
    /**
315
     * Drop all the mapped document collections in the metadata factory.
316
     */
317 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...
318
    {
319 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
320 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
321 1
                continue;
322
            }
323 1
            $this->dropDocumentCollection($class->name);
324
        }
325 1
    }
326
327
    /**
328
     * Drop the document collection for a mapped class.
329
     *
330
     * @param string $documentName
331
     * @throws \InvalidArgumentException
332
     */
333 3 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...
334
    {
335 3
        $class = $this->dm->getClassMetadata($documentName);
336 3
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
337
            throw new \InvalidArgumentException('Cannot delete document indexes for mapped super classes, embedded documents or query result documents.');
338
        }
339 3
        $this->dm->getDocumentCollection($documentName)->drop();
340 3
    }
341
342
    /**
343
     * Drop all the mapped document databases in the metadata factory.
344
     */
345 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...
346
    {
347 1
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
348 1
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
349 1
                continue;
350
            }
351 1
            $this->dropDocumentDatabase($class->name);
352
        }
353 1
    }
354
355
    /**
356
     * Drop the document database for a mapped class.
357
     *
358
     * @param string $documentName
359
     * @throws \InvalidArgumentException
360
     */
361 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...
362
    {
363 2
        $class = $this->dm->getClassMetadata($documentName);
364 2
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
365
            throw new \InvalidArgumentException('Cannot drop document database for mapped super classes, embedded documents or query result documents.');
366
        }
367 2
        $this->dm->getDocumentDatabase($documentName)->drop();
368 2
    }
369
370
    /**
371
     * Create all the mapped document databases in the metadata factory.
372
     *
373
     * @deprecated Databases are created automatically by MongoDB (>= 3.0). Deprecated since ODM 1.2, to be removed in ODM 2.0.
374
     */
375
    public function createDatabases()
376
    {
377
        @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
378
            sprintf('%s was deprecated in version 1.2 - databases are created automatically by MongoDB (>= 3.0).', __METHOD__),
379
            E_USER_DEPRECATED
380
        );
381
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
382
            if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
383
                continue;
384
            }
385
            $this->createDocumentDatabase($class->name);
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\ODM\MongoDB\Sch...reateDocumentDatabase() has been deprecated with message: A database is created automatically by MongoDB (>= 3.0). Deprecated since ODM 1.2, to be removed in ODM 2.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
386
        }
387
    }
388
389
    /**
390
     * Create the document database for a mapped class.
391
     *
392
     * @param string $documentName
393
     * @throws \InvalidArgumentException
394
     *
395
     * @deprecated A database is created automatically by MongoDB (>= 3.0). Deprecated since ODM 1.2, to be removed in ODM 2.0.
396
     */
397
    public function createDocumentDatabase($documentName)
398
    {
399
        @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
400
            sprintf('%s was deprecated in version 1.2 - databases are created automatically by MongoDB (>= 3.0).', __METHOD__),
401
            E_USER_DEPRECATED
402
        );
403
        $class = $this->dm->getClassMetadata($documentName);
404
        if ($class->isMappedSuperclass || $class->isEmbeddedDocument || $class->isQueryResultDocument) {
405
            throw new \InvalidArgumentException('Cannot create databases for mapped super classes, embedded documents or query result documents.');
406
        }
407
408
        $this->dm->getDocumentDatabase($documentName)->execute('function() { return true; }');
0 ignored issues
show
Bug introduced by
The method execute() does not seem to exist on object<MongoDB\Database>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
409
    }
410
411
    /**
412
     * Determine if an index returned by MongoCollection::getIndexInfo() can be
413
     * considered equivalent to an index in class metadata.
414
     *
415
     * Indexes are considered different if:
416
     *
417
     *   (a) Key/direction pairs differ or are not in the same order
418
     *   (b) Sparse or unique options differ
419
     *   (c) Mongo index is unique without dropDups and mapped index is unique
420
     *       with dropDups
421
     *   (d) Geospatial options differ (bits, max, min)
422
     *   (e) The partialFilterExpression differs
423
     *
424
     * Regarding (c), the inverse case is not a reason to delete and
425
     * recreate the index, since dropDups only affects creation of
426
     * the unique index. Additionally, the background option is only
427
     * relevant to index creation and is not considered.
428
     *
429
     * @param array|IndexInfo $mongoIndex Mongo index data.
430
     * @param array $documentIndex Document index data.
431
     * @return bool True if the indexes are equivalent, otherwise false.
432
     */
433 30
    public function isMongoIndexEquivalentToDocumentIndex($mongoIndex, $documentIndex)
434
    {
435 30
        $documentIndexOptions = $documentIndex['options'];
436
437 30
        if ($mongoIndex['key'] != $documentIndex['keys']) {
438 2
            return false;
439
        }
440
441 28
        if (empty($mongoIndex['sparse']) xor empty($documentIndexOptions['sparse'])) {
442 2
            return false;
443
        }
444
445 26
        if (empty($mongoIndex['unique']) xor empty($documentIndexOptions['unique'])) {
446 2
            return false;
447
        }
448
449 24
        if ( ! empty($mongoIndex['unique']) && empty($mongoIndex['dropDups']) &&
450 24
            ! empty($documentIndexOptions['unique']) && ! empty($documentIndexOptions['dropDups'])) {
451
452 1
            return false;
453
        }
454
455 23
        foreach (array('bits', 'max', 'min') as $option) {
456 23
            if (isset($mongoIndex[$option]) xor isset($documentIndexOptions[$option])) {
457 6
                return false;
458
            }
459
460 21
            if (isset($mongoIndex[$option]) && isset($documentIndexOptions[$option]) &&
461 21
                $mongoIndex[$option] !== $documentIndexOptions[$option]) {
462
463 21
                return false;
464
            }
465
        }
466
467 14
        if (empty($mongoIndex['partialFilterExpression']) xor empty($documentIndexOptions['partialFilterExpression'])) {
468 2
            return false;
469
        }
470
471 12
        if (isset($mongoIndex['partialFilterExpression']) && isset($documentIndexOptions['partialFilterExpression']) &&
472 12
            $mongoIndex['partialFilterExpression'] !== $documentIndexOptions['partialFilterExpression']) {
473
474 1
            return false;
475
        }
476
477 11
        return true;
478
    }
479
480
    /**
481
     * Ensure collections are sharded for all documents that can be loaded with the
482
     * metadata factory.
483
     *
484
     * @param array $indexOptions Options for `ensureIndex` command. It's performed on an existing collections
485
     *
486
     * @throws MongoDBException
487
     */
488
    public function ensureSharding(array $indexOptions = array())
489
    {
490
        foreach ($this->metadataFactory->getAllMetadata() as $class) {
491
            if ($class->isMappedSuperclass || !$class->isSharded()) {
492
                continue;
493
            }
494
495
            $this->ensureDocumentSharding($class->name, $indexOptions);
496
        }
497
    }
498
499
    /**
500
     * Ensure sharding for collection by document name.
501
     *
502
     * @param string $documentName
503
     * @param array  $indexOptions Options for `ensureIndex` command. It's performed on an existing collections.
504
     *
505
     * @throws MongoDBException
506
     */
507 2
    public function ensureDocumentSharding($documentName, array $indexOptions = array())
508
    {
509 2
        $class = $this->dm->getClassMetadata($documentName);
510 2
        if ( ! $class->isSharded()) {
511
            return;
512
        }
513
514 2
        $this->enableShardingForDbByDocumentName($documentName);
515
516 2
        $try = 0;
517
        do {
518
            try {
519 2
                $result = $this->runShardCollectionCommand($documentName);
520 2
                $done = true;
521
522
                // Need to check error message because MongoDB 3.0 does not return a code for this error
523 2
                if ($result['ok'] != 1 && strpos($result['errmsg'], 'please create an index that starts') !== false) {
524
                    // The proposed key is not returned when using mongo-php-adapter with ext-mongodb.
525
                    // See https://github.com/mongodb/mongo-php-driver/issues/296 for details
526
                    $key = $result['proposedKey'] ?? $this->dm->getClassMetadata($documentName)->getShardKey()['keys'];
527
528
                    $this->dm->getDocumentCollection($documentName)->ensureIndex($key, $indexOptions);
0 ignored issues
show
Bug introduced by
The method ensureIndex() does not seem to exist on object<MongoDB\Collection>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
529 2
                    $done = false;
530
                }
531 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...
532 1
                if ($e->getCode() === 20 || $e->getMessage() == 'already sharded') {
533 1
                    return;
534
                }
535
536
                throw $e;
537
            }
538 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...
539
540
        // Starting with MongoDB 3.2, this command returns code 20 when a collection is already sharded.
541
        // For older MongoDB versions, check the error message
542 2
        if ($result['ok'] == 1 || (isset($result['code']) && $result['code'] == 20) || $result['errmsg'] == 'already sharded') {
543 2
            return;
544
        }
545
546
        throw MongoDBException::failedToEnsureDocumentSharding($documentName, $result['errmsg']);
547
    }
548
549
    /**
550
     * Enable sharding for database which contains documents with given name.
551
     *
552
     * @param string $documentName
553
     *
554
     * @throws MongoDBException
555
     */
556 2
    public function enableShardingForDbByDocumentName($documentName)
557
    {
558 2
        $dbName = $this->dm->getDocumentDatabase($documentName)->getDatabaseName();
559 2
        $adminDb = $this->dm->getClient()->selectDatabase('admin');
560
561
        try {
562 2
            $adminDb->command(array('enableSharding' => $dbName));
563 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...
564 1
            if ($e->getCode() !== 23 || $e->getMessage() === 'already enabled') {
565
                throw MongoDBException::failedToEnableSharding($dbName, $e->getMessage());
566
            }
567
        }
568 2
    }
569
570
    /**
571
     * @param $documentName
572
     *
573
     * @return array
574
     */
575 2
    private function runShardCollectionCommand($documentName)
576
    {
577 2
        $class = $this->dm->getClassMetadata($documentName);
578 2
        $dbName = $this->dm->getDocumentDatabase($documentName)->getDatabaseName();
579 2
        $shardKey = $class->getShardKey();
580 2
        $adminDb = $this->dm->getClient()->selectDatabase('admin');
581
582 2
        $result = $adminDb->command(
583
            array(
584 2
                'shardCollection' => $dbName . '.' . $class->getCollection(),
585 2
                'key'             => $shardKey['keys']
586
            )
587 2
        )->toArray()[0];
588
589 2
        return $result;
590
    }
591
}
592