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 WhereFilter 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 WhereFilter, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
30 | class WhereFilter implements FilterInterface |
||
31 | { |
||
32 | const PARAMETER_OPERATOR_OR = 'or'; |
||
33 | const PARAMETER_OPERATOR_GT = 'gt'; |
||
34 | const PARAMETER_OPERATOR_GTE = 'gte'; |
||
35 | const PARAMETER_OPERATOR_LT = 'lt'; |
||
36 | const PARAMETER_OPERATOR_LTE = 'lte'; |
||
37 | const PARAMETER_OPERATOR_BETWEEN = 'between'; |
||
38 | const PARAMETER_OPERATOR_NEQ = 'neq'; |
||
39 | const PARAMETER_OPERATOR_LIKE = 'like'; |
||
40 | const PARAMETER_OPERATOR_NLIKE = 'nlike'; |
||
41 | |||
42 | const PARAMETER_ID_KEY = 'id'; |
||
43 | const PARAMETER_NULL_VALUE = 'null'; |
||
44 | |||
45 | /** |
||
46 | * @var IriConverterInterface |
||
47 | */ |
||
48 | private $iriConverter; |
||
49 | |||
50 | /** |
||
51 | * @var FilterQueryExtractorInterface |
||
52 | */ |
||
53 | private $queryExtractor; |
||
54 | |||
55 | /** |
||
56 | * @var ManagerRegistry |
||
57 | */ |
||
58 | private $managerRegistry; |
||
59 | |||
60 | /** |
||
61 | * @var array|null |
||
62 | */ |
||
63 | private $properties; |
||
64 | |||
65 | /** |
||
66 | * @var PropertyAccessorInterface |
||
67 | */ |
||
68 | private $propertyAccessor; |
||
69 | |||
70 | /** |
||
71 | * @var RequestStack |
||
72 | */ |
||
73 | private $requestStack; |
||
74 | |||
75 | /** |
||
76 | * @param ManagerRegistry $managerRegistry |
||
77 | * @param RequestStack $requestStack |
||
78 | * @param IriConverterInterface $iriConverter |
||
79 | * @param PropertyAccessorInterface $propertyAccessor |
||
80 | * @param FilterQueryExtractorInterface $queryExtractor |
||
81 | * @param null|array $properties Null to allow filtering on all properties with the exact strategy or a map of property name with strategy. |
||
82 | */ |
||
83 | public function __construct( |
||
98 | |||
99 | /** |
||
100 | * {@inheritdoc} |
||
101 | */ |
||
102 | public function apply(ResourceInterface $resource, QueryBuilder $queryBuilder) |
||
182 | |||
183 | /** |
||
184 | * Handles the given filter to call the proper operator. At this point, it's unclear if the value passed is the real |
||
185 | * value operator. |
||
186 | * |
||
187 | * @param QueryBuilder $queryBuilder |
||
188 | * @param ClassMetadata $resourceMetadata |
||
189 | * @param string[] $aliases |
||
190 | * @param ClassMetadata[] $associationsMetadata |
||
191 | * @param string $property |
||
192 | * @param array|string $value |
||
193 | * @param string|null $parameter If is string is used to construct the parameter to avoid parameter conflicts. |
||
194 | * |
||
195 | * @return array |
||
196 | */ |
||
197 | private function handleFilter( |
||
267 | |||
268 | /** |
||
269 | * Gets the proper query expression for the set of data given. |
||
270 | * |
||
271 | * @param QueryBuilder $queryBuilder |
||
272 | * @param string $alias alias of the entity to which belongs the property |
||
273 | * @param ClassMetadata $aliasMetadata |
||
274 | * @param string $property |
||
275 | * @param string $operator |
||
276 | * @param string|array $value |
||
277 | * @param string|null $parameter If is string is used to construct the parameter to avoid parameter conflicts. |
||
278 | * |
||
279 | * @return Expr|null |
||
280 | */ |
||
281 | private function handleOperator( |
||
374 | |||
375 | /** |
||
376 | * Normalizes the value. If the key is an ID, get the real ID value. If is null, set the value to null. Otherwise |
||
377 | * return unchanged value. |
||
378 | * |
||
379 | * @param ClassMetadata $metadata |
||
380 | * @param string $property |
||
381 | * @param string $value |
||
382 | * |
||
383 | * @return null|string |
||
384 | */ |
||
385 | private function normalizeValue(ClassMetadata $metadata, $property, $value) |
||
413 | |||
414 | /** |
||
415 | * {@inheritdoc} |
||
416 | * |
||
417 | * TODO |
||
418 | */ |
||
419 | public function getDescription(ResourceInterface $resource) |
||
423 | |||
424 | /** |
||
425 | * Gets the ID from an URI or a raw ID. |
||
426 | * |
||
427 | * @param string $value |
||
428 | * |
||
429 | * @return string |
||
430 | */ |
||
431 | protected function getFilterValueFromUrl($value) |
||
443 | |||
444 | /** |
||
445 | * Gets the alias used for the entity to which the property belongs. |
||
446 | * |
||
447 | * @example |
||
448 | * $property was `name` |
||
449 | * $explodedProperty then is [] |
||
450 | * => 'o' |
||
451 | * |
||
452 | * $property was `relatedDummy_name` |
||
453 | * $explodedProperty then is ['relatedDummy'] |
||
454 | * => WhereFilter_relatedDummyAlias |
||
455 | * |
||
456 | * $property was `relatedDummy_anotherDummy_name` |
||
457 | * $explodedProperty then is ['relatedDummy', 'anotherDummy'] |
||
458 | * => WhereFilter_relatedDummy_anotherDummyAlias |
||
459 | * |
||
460 | * @param string[] $aliases Array containing all the properties for each an alias is used. The key is the |
||
461 | * property and the value the actual alias. |
||
462 | * @param string[] $explodedProperty |
||
463 | * |
||
464 | * @return string alias |
||
465 | */ |
||
466 | private function getResourceAliasForProperty(array &$aliases, array $explodedProperty) |
||
480 | |||
481 | /** |
||
482 | * Gets the metadata to which belongs the property. |
||
483 | * |
||
484 | * @example |
||
485 | * $property was `name` |
||
486 | * $explodedProperty then is [] |
||
487 | * => $resourceMetadata |
||
488 | * |
||
489 | * $property was `relatedDummy_name` |
||
490 | * $explodedProperty then is ['relatedDummy'] |
||
491 | * => metadata of relatedDummy |
||
492 | * |
||
493 | * $property was `relatedDummy_anotherDummy_name` |
||
494 | * $explodedProperty then is ['relatedDummy', 'anotherDummy'] |
||
495 | * => metadata of anotherDummy |
||
496 | * |
||
497 | * @param ClassMetadata $resourceMetadata |
||
498 | * @param ClassMetadata[] $associationsMetadata |
||
499 | * @param array $explodedProperty |
||
500 | * |
||
501 | * @return ClassMetadata |
||
502 | */ |
||
503 | private function getAssociationMetadataForProperty( |
||
537 | |||
538 | /** |
||
539 | * Gets class metadata for the given class. |
||
540 | * |
||
541 | * @param string $class |
||
542 | * |
||
543 | * @return ClassMetadata |
||
544 | */ |
||
545 | private function getMetadata($class) |
||
553 | |||
554 | /** |
||
555 | * Gets class metadata for the given resource. |
||
556 | * |
||
557 | * @param ResourceInterface $resource |
||
558 | * |
||
559 | * @return ClassMetadata |
||
560 | */ |
||
561 | private function getClassMetadata(ResourceInterface $resource) |
||
571 | } |
||
572 |
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.