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 MultilingualBehavior 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 MultilingualBehavior, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
13 | class MultilingualBehavior extends Behavior |
||
14 | { |
||
15 | /** |
||
16 | * Multilingual attributes |
||
17 | * @var array |
||
18 | */ |
||
19 | public $attributes; |
||
20 | |||
21 | /** |
||
22 | * Available languages |
||
23 | * It can be a simple array: array('fr', 'en') or an associative array: array('fr' => 'Français', 'en' => 'English') |
||
24 | * For associative arrays, only the keys will be used. |
||
25 | * @var array |
||
26 | */ |
||
27 | public $languages; |
||
28 | |||
29 | /** |
||
30 | * @var string the default language. |
||
31 | * Example: 'en'. |
||
32 | */ |
||
33 | public $defaultLanguage; |
||
34 | |||
35 | /** |
||
36 | * @var string the name of the translation table |
||
37 | */ |
||
38 | public $tableName; |
||
39 | |||
40 | /** |
||
41 | * @var string the name of translation model class. |
||
42 | */ |
||
43 | public $langClassName; |
||
44 | |||
45 | /** |
||
46 | * @var string the name of the foreign key field of the translation table related to base model table. |
||
47 | */ |
||
48 | public $langForeignKey; |
||
49 | |||
50 | /** |
||
51 | * @var string the prefix of the localized attributes in the lang table. Here to avoid collisions in queries. |
||
52 | * In the translation table, the columns corresponding to the localized attributes have to be name like this: 'l_[name of the attribute]' |
||
53 | * and the id column (primary key) like this : 'l_id' |
||
54 | * Default to ''. |
||
55 | */ |
||
56 | public $localizedPrefix = ''; |
||
57 | |||
58 | /** |
||
59 | * @var string the name of the lang field of the translation table. Default to 'language'. |
||
60 | */ |
||
61 | public $languageField = 'language'; |
||
62 | |||
63 | /** |
||
64 | * @var boolean if this property is set to true required validators will be applied to all translation models. |
||
65 | * Default to false. |
||
66 | */ |
||
67 | public $requireTranslations = false; |
||
68 | |||
69 | /** |
||
70 | * @var boolean whether to force deletion of the associated translations when a base model is deleted. |
||
71 | * Not needed if using foreign key with 'on delete cascade'. |
||
72 | * Default to true. |
||
73 | */ |
||
74 | public $forceDelete = true; |
||
75 | |||
76 | /** |
||
77 | * @var boolean whether to dynamically create translation model class. |
||
78 | * If true, the translation model class will be generated on runtime with the use of the eval() function so no additional php file is needed. |
||
79 | * See {@link createLangClass()} |
||
80 | * Default to true. |
||
81 | */ |
||
82 | public $dynamicLangClass = true; |
||
83 | |||
84 | /** |
||
85 | * @var boolean whether to abridge the language ID. |
||
86 | * Default to true. |
||
87 | */ |
||
88 | public $abridge = true; |
||
89 | |||
90 | /** |
||
91 | * @var string the name of the primary key field of the base model. Defaults to first value of Model::primaryKey. |
||
92 | */ |
||
93 | public $ownerPrimaryKey; |
||
94 | |||
95 | /** |
||
96 | * @var boolean whether to check for existing translations on insert |
||
97 | * Default to false |
||
98 | */ |
||
99 | public $checkOnInsert = false; |
||
100 | |||
101 | private $currentLanguage; |
||
102 | private $ownerClassName; |
||
103 | private $langClassShortName; |
||
104 | private $ownerClassShortName; |
||
105 | private $langAttributes = []; |
||
106 | |||
107 | /** |
||
108 | * @var array excluded validators |
||
109 | */ |
||
110 | private $excludedValidators = ['unique']; |
||
111 | |||
112 | /** |
||
113 | * @inheritdoc |
||
114 | */ |
||
115 | 9 | public function events() |
|
125 | |||
126 | /** |
||
127 | * @inheritdoc |
||
128 | */ |
||
129 | 9 | public function attach($owner) |
|
229 | |||
230 | 9 | public function createLangClass() |
|
246 | |||
247 | /** |
||
248 | * Relation to model translations |
||
249 | * @return ActiveQuery |
||
250 | */ |
||
251 | public function getTranslations() |
||
255 | |||
256 | /** |
||
257 | * Relation to model translation |
||
258 | * @param $language |
||
259 | * @return ActiveQuery |
||
260 | */ |
||
261 | 5 | public function getTranslation($language = null) |
|
267 | |||
268 | /** |
||
269 | * Handle 'beforeValidate' event of the owner. |
||
270 | */ |
||
271 | 10 | public function beforeValidate() |
|
277 | |||
278 | /** |
||
279 | * Handle 'afterFind' event of the owner. |
||
280 | */ |
||
281 | 8 | public function afterFind() |
|
322 | |||
323 | /** |
||
324 | * Handle 'afterInsert' event of the owner. |
||
325 | */ |
||
326 | 4 | View Code Duplication | public function afterInsert() |
335 | |||
336 | /** |
||
337 | * Handle 'afterUpdate' event of the owner. |
||
338 | */ |
||
339 | 5 | View Code Duplication | public function afterUpdate() |
349 | |||
350 | /** |
||
351 | * Handle 'afterDelete' event of the owner. |
||
352 | */ |
||
353 | 2 | public function afterDelete() |
|
361 | |||
362 | /** |
||
363 | * @param array $translations |
||
364 | */ |
||
365 | 7 | private function saveTranslations($translations = []) |
|
399 | |||
400 | /** |
||
401 | * @inheritdoc |
||
402 | */ |
||
403 | 3 | public function canGetProperty($name, $checkVars = true) |
|
408 | |||
409 | /** |
||
410 | * @inheritdoc |
||
411 | */ |
||
412 | public function canSetProperty($name, $checkVars = true) |
||
416 | |||
417 | /** |
||
418 | * @inheritdoc |
||
419 | */ |
||
420 | public function __get($name) |
||
431 | |||
432 | /** |
||
433 | * @inheritdoc |
||
434 | */ |
||
435 | public function __set($name, $value) |
||
446 | |||
447 | /** |
||
448 | * @inheritdoc |
||
449 | * @codeCoverageIgnore |
||
450 | */ |
||
451 | public function __isset($name) |
||
459 | |||
460 | /** |
||
461 | * Whether an attribute exists |
||
462 | * @param string $name the name of the attribute |
||
463 | * @return boolean |
||
464 | */ |
||
465 | public function hasLangAttribute($name) |
||
469 | |||
470 | /** |
||
471 | * @param string $name the name of the attribute |
||
472 | * @return string the attribute value |
||
473 | */ |
||
474 | public function getLangAttribute($name) |
||
478 | |||
479 | /** |
||
480 | * @param string $name the name of the attribute |
||
481 | * @param string $value the value of the attribute |
||
482 | */ |
||
483 | public function setLangAttribute($name, $value) |
||
487 | |||
488 | /** |
||
489 | * @param $records |
||
490 | * @return array |
||
491 | */ |
||
492 | 1 | protected function indexByLanguage($records) |
|
501 | |||
502 | /** |
||
503 | * @param $language |
||
504 | * @return string |
||
505 | */ |
||
506 | protected function getLanguageBaseName($language) |
||
510 | |||
511 | /** |
||
512 | * @param string $className |
||
513 | * @return string |
||
514 | */ |
||
515 | private function getShortClassName($className) |
||
519 | |||
520 | /** |
||
521 | * @return mixed|string |
||
522 | */ |
||
523 | 5 | public function getCurrentLanguage() |
|
527 | |||
528 | /** |
||
529 | * @param $attribute |
||
530 | * @param $language |
||
531 | * @return string |
||
532 | */ |
||
533 | protected function getAttributeName($attribute, $language) |
||
538 | } |
||
539 |
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.