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 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 |
||
25 | class SchemaManager |
||
26 | { |
||
27 | /** |
||
28 | * @var DocumentManager |
||
29 | */ |
||
30 | protected $dm; |
||
31 | |||
32 | /** |
||
33 | * |
||
34 | * @var ClassMetadataFactory |
||
35 | */ |
||
36 | protected $metadataFactory; |
||
37 | |||
38 | /** |
||
39 | * @param DocumentManager $dm |
||
40 | * @param ClassMetadataFactory $cmf |
||
41 | */ |
||
42 | 947 | public function __construct(DocumentManager $dm, ClassMetadataFactory $cmf) |
|
47 | |||
48 | /** |
||
49 | * Ensure indexes are created for all documents that can be loaded with the |
||
50 | * metadata factory. |
||
51 | * |
||
52 | * @param integer $timeout Timeout (ms) for acknowledged index creation |
||
53 | */ |
||
54 | 1 | View Code Duplication | public function ensureIndexes($timeout = null) |
63 | |||
64 | /** |
||
65 | * Ensure indexes exist for all mapped document classes. |
||
66 | * |
||
67 | * Indexes that exist in MongoDB but not the document metadata will be |
||
68 | * deleted. |
||
69 | * |
||
70 | * @param integer $timeout Timeout (ms) for acknowledged index creation |
||
71 | */ |
||
72 | View Code Duplication | public function updateIndexes($timeout = null) |
|
81 | |||
82 | /** |
||
83 | * Ensure indexes exist for the mapped document class. |
||
84 | * |
||
85 | * Indexes that exist in MongoDB but not the document metadata will be |
||
86 | * deleted. |
||
87 | * |
||
88 | * @param string $documentName |
||
89 | * @param integer $timeout Timeout (ms) for acknowledged index creation |
||
90 | * @throws \InvalidArgumentException |
||
91 | */ |
||
92 | 3 | public function updateDocumentIndexes($documentName, $timeout = null) |
|
137 | |||
138 | /** |
||
139 | * @param string $documentName |
||
140 | * @return array |
||
141 | */ |
||
142 | 47 | public function getDocumentIndexes($documentName) |
|
147 | |||
148 | /** |
||
149 | * @param string $documentName |
||
150 | * @param array $visited |
||
151 | * @return array |
||
152 | */ |
||
153 | 47 | private function doGetDocumentIndexes($documentName, array &$visited) |
|
205 | |||
206 | /** |
||
207 | * @param ClassMetadata $class |
||
208 | * @return array |
||
209 | */ |
||
210 | 47 | private function prepareIndexes(ClassMetadata $class) |
|
236 | |||
237 | /** |
||
238 | * Ensure the given document's indexes are created. |
||
239 | * |
||
240 | * @param string $documentName |
||
241 | * @param integer $timeout Timeout (ms) for acknowledged index creation |
||
242 | * @throws \InvalidArgumentException |
||
243 | */ |
||
244 | 43 | public function ensureDocumentIndexes($documentName, $timeout = null) |
|
245 | { |
||
246 | 43 | $class = $this->dm->getClassMetadata($documentName); |
|
247 | 43 | if ($class->isMappedSuperclass || $class->isEmbeddedDocument) { |
|
248 | throw new \InvalidArgumentException('Cannot create document indexes for mapped super classes or embedded documents.'); |
||
249 | } |
||
250 | 43 | if ($indexes = $this->getDocumentIndexes($documentName)) { |
|
251 | 43 | $collection = $this->dm->getDocumentCollection($class->name); |
|
252 | 43 | foreach ($indexes as $index) { |
|
253 | 43 | $keys = $index['keys']; |
|
254 | 43 | $options = $index['options']; |
|
255 | |||
256 | 43 | if ( ! isset($options['safe']) && ! isset($options['w'])) { |
|
257 | 42 | if (version_compare(phpversion('mongo'), '1.3.0', '<')) { |
|
258 | 42 | $options['safe'] = true; |
|
259 | 42 | } else { |
|
260 | $options['w'] = 1; |
||
261 | } |
||
262 | 42 | } |
|
263 | |||
264 | 43 | if (isset($options['safe']) && ! isset($options['w']) && |
|
265 | 43 | version_compare(phpversion('mongo'), '1.3.0', '>=')) { |
|
266 | |||
267 | $options['w'] = is_bool($options['safe']) ? (integer) $options['safe'] : $options['safe']; |
||
268 | unset($options['safe']); |
||
269 | } |
||
270 | |||
271 | 43 | if ( ! isset($options['timeout']) && isset($timeout)) { |
|
272 | 1 | $options['timeout'] = $timeout; |
|
273 | 1 | } |
|
274 | |||
275 | 43 | $collection->ensureIndex($keys, $options); |
|
276 | 43 | } |
|
277 | 43 | } |
|
278 | 43 | } |
|
279 | |||
280 | /** |
||
281 | * Delete indexes for all documents that can be loaded with the |
||
282 | * metadata factory. |
||
283 | */ |
||
284 | 1 | View Code Duplication | public function deleteIndexes() |
293 | |||
294 | /** |
||
295 | * Delete the given document's indexes. |
||
296 | * |
||
297 | * @param string $documentName |
||
298 | * @throws \InvalidArgumentException |
||
299 | */ |
||
300 | 2 | View Code Duplication | public function deleteDocumentIndexes($documentName) |
308 | |||
309 | /** |
||
310 | * Create all the mapped document collections in the metadata factory. |
||
311 | */ |
||
312 | 1 | View Code Duplication | public function createCollections() |
321 | |||
322 | /** |
||
323 | * Create the document collection for a mapped class. |
||
324 | * |
||
325 | * @param string $documentName |
||
326 | * @throws \InvalidArgumentException |
||
327 | */ |
||
328 | 4 | public function createDocumentCollection($documentName) |
|
350 | |||
351 | /** |
||
352 | * Drop all the mapped document collections in the metadata factory. |
||
353 | */ |
||
354 | 1 | View Code Duplication | public function dropCollections() |
363 | |||
364 | /** |
||
365 | * Drop the document collection for a mapped class. |
||
366 | * |
||
367 | * @param string $documentName |
||
368 | * @throws \InvalidArgumentException |
||
369 | */ |
||
370 | 3 | View Code Duplication | public function dropDocumentCollection($documentName) |
380 | |||
381 | /** |
||
382 | * Drop all the mapped document databases in the metadata factory. |
||
383 | */ |
||
384 | 1 | View Code Duplication | public function dropDatabases() |
393 | |||
394 | /** |
||
395 | * Drop the document database for a mapped class. |
||
396 | * |
||
397 | * @param string $documentName |
||
398 | * @throws \InvalidArgumentException |
||
399 | */ |
||
400 | 2 | View Code Duplication | public function dropDocumentDatabase($documentName) |
408 | |||
409 | /** |
||
410 | * Create all the mapped document databases in the metadata factory. |
||
411 | */ |
||
412 | View Code Duplication | public function createDatabases() |
|
421 | |||
422 | /** |
||
423 | * Create the document database for a mapped class. |
||
424 | * |
||
425 | * @param string $documentName |
||
426 | * @throws \InvalidArgumentException |
||
427 | */ |
||
428 | 1 | View Code Duplication | public function createDocumentDatabase($documentName) |
436 | |||
437 | /** |
||
438 | * Determine if an index returned by MongoCollection::getIndexInfo() can be |
||
439 | * considered equivalent to an index in class metadata. |
||
440 | * |
||
441 | * Indexes are considered different if: |
||
442 | * |
||
443 | * (a) Key/direction pairs differ or are not in the same order |
||
444 | * (b) Sparse or unique options differ |
||
445 | * (c) Mongo index is unique without dropDups and mapped index is unique |
||
446 | * with dropDups |
||
447 | * (d) Geospatial options differ (bits, max, min) |
||
448 | * |
||
449 | * Regarding (c), the inverse case is not a reason to delete and |
||
450 | * recreate the index, since dropDups only affects creation of |
||
451 | * the unique index. Additionally, the background option is only |
||
452 | * relevant to index creation and is not considered. |
||
453 | */ |
||
454 | 1 | public function isMongoIndexEquivalentToDocumentIndex($mongoIndex, $documentIndex) |
|
490 | } |
||
491 |
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.