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 ElementModel 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 ElementModel, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
45 | class ElementModel extends BitrixModel |
||
46 | { |
||
47 | /** |
||
48 | * Corresponding IBLOCK_ID |
||
49 | * |
||
50 | * @var int |
||
51 | */ |
||
52 | const IBLOCK_ID = null; |
||
53 | |||
54 | /** |
||
55 | * IBLOCK version (1 or 2) |
||
56 | * |
||
57 | * @var int |
||
58 | */ |
||
59 | const IBLOCK_VERSION = 2; |
||
60 | |||
61 | /** |
||
62 | * Bitrix entity object. |
||
63 | * |
||
64 | * @var object |
||
65 | */ |
||
66 | public static $bxObject; |
||
67 | |||
68 | /** |
||
69 | * Corresponding object class name. |
||
70 | * |
||
71 | * @var string |
||
72 | */ |
||
73 | protected static $objectClass = 'CIBlockElement'; |
||
74 | |||
75 | /** |
||
76 | * Iblock PropertiesData from Bitrix DB |
||
77 | * |
||
78 | * @var null|array |
||
79 | */ |
||
80 | protected static $iblockPropertiesData = []; |
||
81 | |||
82 | /** |
||
83 | * Have sections been already fetched from DB? |
||
84 | * |
||
85 | * @var bool |
||
86 | */ |
||
87 | protected $sectionsAreFetched = false; |
||
88 | |||
89 | /** |
||
90 | * Log in Bitrix workflow ($bWorkFlow for CIBlockElement::Add/Update). |
||
91 | * |
||
92 | * @var bool |
||
93 | */ |
||
94 | protected static $workFlow = false; |
||
95 | |||
96 | /** |
||
97 | * Update search after each create or update ($bUpdateSearch for CIBlockElement::Add/Update). |
||
98 | * |
||
99 | * @var bool |
||
100 | */ |
||
101 | protected static $updateSearch = true; |
||
102 | |||
103 | /** |
||
104 | * Resize pictures during add/update ($bResizePictures for CIBlockElement::Add/Update). |
||
105 | * |
||
106 | * @var bool |
||
107 | */ |
||
108 | protected static $resizePictures = false; |
||
109 | |||
110 | /** |
||
111 | * Getter for corresponding iblock id. |
||
112 | * |
||
113 | * @throws LogicException |
||
114 | * |
||
115 | * @return int |
||
116 | */ |
||
117 | public static function iblockId() |
||
126 | |||
127 | /** |
||
128 | * Create new item in database. |
||
129 | * |
||
130 | * @param $fields |
||
131 | * |
||
132 | * @throws LogicException |
||
133 | * |
||
134 | * @return static|bool |
||
135 | * @throws ExceptionFromBitrix |
||
136 | */ |
||
137 | View Code Duplication | public static function create($fields) |
|
|
|||
138 | { |
||
139 | if (!isset($fields['IBLOCK_ID'])) { |
||
140 | $fields['IBLOCK_ID'] = static::iblockId(); |
||
141 | } |
||
142 | |||
143 | return static::internalCreate($fields); |
||
144 | } |
||
145 | |||
146 | public static function internalDirectCreate($bxObject, $fields) |
||
150 | |||
151 | /** |
||
152 | * Fetches static::$iblockPropertiesData if it's not fetched and returns it. |
||
153 | * |
||
154 | * @return array |
||
155 | */ |
||
156 | protected static function getCachedIblockPropertiesData() |
||
171 | |||
172 | /** |
||
173 | * Setter for self::$iblockPropertiesData[static::iblockId()] mainly for testing. |
||
174 | * |
||
175 | * @param $data |
||
176 | * @return void |
||
177 | */ |
||
178 | public static function setCachedIblockPropertiesData($data) |
||
182 | |||
183 | /** |
||
184 | * Corresponding section model full qualified class name. |
||
185 | * MUST be overridden if you are going to use section model for this iblock. |
||
186 | * |
||
187 | * @throws LogicException |
||
188 | * |
||
189 | * @return string |
||
190 | */ |
||
191 | public static function sectionModel() |
||
195 | |||
196 | /** |
||
197 | * Instantiate a query object for the model. |
||
198 | * |
||
199 | * @return ElementQuery |
||
200 | */ |
||
201 | public static function query() |
||
205 | |||
206 | /** |
||
207 | * Scope to sort by date. |
||
208 | * |
||
209 | * @param ElementQuery $query |
||
210 | * @param string $sort |
||
211 | * |
||
212 | * @return ElementQuery |
||
213 | */ |
||
214 | public function scopeSortByDate($query, $sort = 'DESC') |
||
218 | |||
219 | /** |
||
220 | * Scope to get only items from a given section. |
||
221 | * |
||
222 | * @param ElementQuery $query |
||
223 | * @param mixed $id |
||
224 | * |
||
225 | * @return ElementQuery |
||
226 | */ |
||
227 | public function scopeFromSectionWithId($query, $id) |
||
233 | |||
234 | /** |
||
235 | * Scope to get only items from a given section. |
||
236 | * |
||
237 | * @param ElementQuery $query |
||
238 | * @param string $code |
||
239 | * |
||
240 | * @return ElementQuery |
||
241 | */ |
||
242 | public function scopeFromSectionWithCode($query, $code) |
||
248 | |||
249 | /** |
||
250 | * Fill extra fields when $this->field is called. |
||
251 | * |
||
252 | * @return null |
||
253 | */ |
||
254 | protected function afterFill() |
||
258 | |||
259 | /** |
||
260 | * Load all model attributes from cache or database. |
||
261 | * |
||
262 | * @return $this |
||
263 | */ |
||
264 | public function load() |
||
270 | |||
271 | /** |
||
272 | * Get element's sections from cache or database. |
||
273 | * |
||
274 | * @return array |
||
275 | */ |
||
276 | public function getSections() |
||
284 | |||
285 | /** |
||
286 | * Refresh model from database and place data to $this->fields. |
||
287 | * |
||
288 | * @return array |
||
289 | */ |
||
290 | public function refresh() |
||
294 | |||
295 | /** |
||
296 | * Refresh element's fields and save them to a class field. |
||
297 | * |
||
298 | * @return array |
||
299 | */ |
||
300 | View Code Duplication | public function refreshFields() |
|
321 | |||
322 | /** |
||
323 | * Refresh element's sections and save them to a class field. |
||
324 | * |
||
325 | * @return array |
||
326 | */ |
||
327 | public function refreshSections() |
||
343 | |||
344 | /** |
||
345 | * @deprecated in favour of `->section()` |
||
346 | * Get element direct section as ID or array of fields. |
||
347 | * |
||
348 | * @param bool $load |
||
349 | * |
||
350 | * @return false|int|array |
||
351 | */ |
||
352 | public function getSection($load = false) |
||
367 | |||
368 | /** |
||
369 | * Get element direct section as model object. |
||
370 | * |
||
371 | * @param bool $load |
||
372 | * |
||
373 | * @return false|SectionModel |
||
374 | */ |
||
375 | public function section($load = false) |
||
386 | |||
387 | /** |
||
388 | * Proxy for GetPanelButtons |
||
389 | * |
||
390 | * @param array $options |
||
391 | * @return array |
||
392 | */ |
||
393 | public function getPanelButtons($options = []) |
||
402 | |||
403 | /** |
||
404 | * Save props to database. |
||
405 | * If selected is not empty then only props from it are saved. |
||
406 | * |
||
407 | * @param array $selected |
||
408 | * |
||
409 | * @return bool |
||
410 | */ |
||
411 | public function saveProps($selected = []) |
||
427 | |||
428 | /** |
||
429 | * Normalize properties's format converting it to 'PROPERTY_"CODE"_VALUE'. |
||
430 | * |
||
431 | * @return null |
||
432 | */ |
||
433 | protected function normalizePropertyFormat() |
||
447 | |||
448 | /** |
||
449 | * Construct 'PROPERTY_VALUES' => [...] from flat fields array. |
||
450 | * This is used in save. |
||
451 | * If $selectedFields are specified only those are saved. |
||
452 | * |
||
453 | * @param $selectedFields |
||
454 | * |
||
455 | * @return array |
||
456 | */ |
||
457 | protected function constructPropertyValuesForSave($selectedFields = []) |
||
535 | |||
536 | /** |
||
537 | * Determine whether the field should be stopped from passing to "update". |
||
538 | * |
||
539 | * @param string $field |
||
540 | * @param mixed $value |
||
541 | * @param array $selectedFields |
||
542 | * |
||
543 | * @return bool |
||
544 | */ |
||
545 | protected function fieldShouldNotBeSaved($field, $value, $selectedFields) |
||
559 | |||
560 | /** |
||
561 | * @param $fields |
||
562 | * @param $fieldsSelectedForSave |
||
563 | * @return bool |
||
564 | */ |
||
565 | protected function internalUpdate($fields, $fieldsSelectedForSave) |
||
580 | |||
581 | /** |
||
582 | * Get value from language field according to current language. |
||
583 | * |
||
584 | * @param $field |
||
585 | * @return mixed |
||
586 | */ |
||
587 | protected function getValueFromLanguageField($field) |
||
593 | |||
594 | /** |
||
595 | * @param $value |
||
596 | */ |
||
597 | public static function setWorkflow($value) |
||
601 | |||
602 | /** |
||
603 | * @param $value |
||
604 | */ |
||
605 | public static function setUpdateSearch($value) |
||
609 | |||
610 | /** |
||
611 | * @param $value |
||
612 | */ |
||
613 | public static function setResizePictures($value) |
||
617 | } |
||
618 |
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.