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 HelperWidget 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 HelperWidget, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
147 | abstract class HelperWidget |
||
148 | { |
||
149 | const LIST_HELPER = 1; |
||
150 | const EDIT_HELPER = 2; |
||
151 | |||
152 | /** |
||
153 | * @var string Код поля. |
||
154 | */ |
||
155 | protected $code; |
||
156 | /** |
||
157 | * @var array $settings Настройки виджета для данной модели. |
||
158 | */ |
||
159 | protected $settings = array( |
||
160 | // Поля множественного виджета по умолчанию (array('ОРИГИНАЛЬНОЕ НАЗВАНИЕ', 'ОРИГИНАЛЬНОЕ НАЗВАНИЕ' => 'АЛИАС')) |
||
161 | 'MULTIPLE_FIELDS' => array('ID', 'VALUE', 'ENTITY_ID') |
||
162 | ); |
||
163 | /** |
||
164 | * @var array Настройки "по-умолчанию" для модели. |
||
165 | */ |
||
166 | static protected $defaults; |
||
167 | /** |
||
168 | * @var DataManager Название класса модели. |
||
169 | */ |
||
170 | protected $entityName; |
||
171 | /** |
||
172 | * @var array Данные модели. |
||
173 | */ |
||
174 | protected $data; |
||
175 | /** @var AdminBaseHelper|AdminListHelper|AdminEditHelper $helper Экземпляр хэлпера, вызывающий данный виджет. |
||
176 | */ |
||
177 | protected $helper; |
||
178 | /** |
||
179 | * @var bool Статус отображения JS хелпера. Используется для исключения дублирования JS-кода. |
||
180 | */ |
||
181 | protected $jsHelper = false; |
||
182 | /** |
||
183 | * @var array $validationErrors Ошибки валидации поля. |
||
184 | */ |
||
185 | protected $validationErrors = array(); |
||
186 | /** |
||
187 | * @var string Строка, добавляемая к полю name полей фильтра. |
||
188 | */ |
||
189 | protected $filterFieldPrefix = 'find_'; |
||
190 | |||
191 | /** |
||
192 | * Эксемпляр виджета создаётся всего один раз, при описании настроек интерфейса. При создании есть возможность |
||
193 | * сразу указать для него необходимые настройки. |
||
194 | * |
||
195 | * @param array $settings |
||
196 | */ |
||
197 | public function __construct(array $settings = array()) |
||
203 | |||
204 | /** |
||
205 | * Генерирует HTML для редактирования поля. |
||
206 | * |
||
207 | * @return string |
||
208 | * |
||
209 | * @api |
||
210 | */ |
||
211 | abstract protected function getEditHtml(); |
||
212 | |||
213 | /** |
||
214 | * Генерирует HTML для редактирования поля в мульти-режиме. |
||
215 | * |
||
216 | * @return string |
||
217 | * |
||
218 | * @api |
||
219 | */ |
||
220 | protected function getMultipleEditHtml() |
||
224 | |||
225 | /** |
||
226 | * Оборачивает поле в HTML код, который в большинстве случаев менять не придется. Далее вызывается |
||
227 | * кастомизируемая часть. |
||
228 | * |
||
229 | * @param bool $isPKField Является ли поле первичным ключом модели. |
||
230 | * |
||
231 | * @see HelperWidget::getEditHtml(); |
||
232 | */ |
||
233 | public function showBasicEditField($isPKField) |
||
280 | |||
281 | /** |
||
282 | * Возвращает значение поля в форме "только для чтения" для не множественных свойств. |
||
283 | * |
||
284 | * @return mixed |
||
285 | */ |
||
286 | protected function getValueReadonly() |
||
290 | |||
291 | /** |
||
292 | * Возвращает значения множественного поля. |
||
293 | * |
||
294 | * @return array |
||
295 | */ |
||
296 | protected function getMultipleValue() |
||
323 | |||
324 | /** |
||
325 | * Возвращает значение поля в форме "только для чтения" для множественных свойств. |
||
326 | * |
||
327 | * @return string |
||
328 | */ |
||
329 | protected function getMultipleValueReadonly() |
||
339 | |||
340 | /** |
||
341 | * Обработка строки для безопасного отображения. Если нужно отобразить текст как аттрибут тега, |
||
342 | * используйте static::prepareToTag(). |
||
343 | * |
||
344 | * @param string $string |
||
345 | * @param bool $hideTags Скрыть теги: |
||
346 | * |
||
347 | * - true - вырезать теги оставив содержимое. Результат обработки: <b>text</b> = text |
||
348 | * |
||
349 | * - false - отобразаить теги в виде текста. Результат обработки: <b>text</b> = <b>text</b> |
||
350 | * |
||
351 | * @return string |
||
352 | */ |
||
353 | public static function prepareToOutput($string, $hideTags = true) |
||
361 | |||
362 | /** |
||
363 | * Подготовка строки для использования в аттрибутах тегов. Например: |
||
364 | * ``` |
||
365 | * <input name="test" value="<?= HelperWidget::prepareToTagAttr($value) ?>"/> |
||
366 | * ``` |
||
367 | * |
||
368 | * @param string $string |
||
369 | * |
||
370 | * @return string |
||
371 | */ |
||
372 | public static function prepareToTagAttr($string) |
||
377 | |||
378 | /** |
||
379 | * Подготовка строки для использования в JS. |
||
380 | * |
||
381 | * @param string $string |
||
382 | * |
||
383 | * @return string |
||
384 | */ |
||
385 | public static function prepareToJs($string) |
||
392 | |||
393 | /** |
||
394 | * Генерирует HTML для поля в списке. |
||
395 | * |
||
396 | * @param \CAdminListRow $row |
||
397 | * @param array $data Данные текущей строки. |
||
398 | * |
||
399 | * @return void |
||
400 | * |
||
401 | * @see AdminListHelper::addRowCell() |
||
402 | * |
||
403 | * @api |
||
404 | */ |
||
405 | abstract public function generateRow(&$row, $data); |
||
406 | |||
407 | /** |
||
408 | * Генерирует HTML для поля фильтрации. |
||
409 | * |
||
410 | * @return void |
||
411 | * |
||
412 | * @see AdminListHelper::createFilterForm() |
||
413 | * |
||
414 | * @api |
||
415 | */ |
||
416 | abstract public function showFilterHtml(); |
||
417 | |||
418 | /** |
||
419 | * Возвращает массив настроек данного виджета, либо значение отдельного параметра, если указано его имя. |
||
420 | * |
||
421 | * @param string $name Название конкретного параметра. |
||
422 | * |
||
423 | * @return array|mixed |
||
424 | * |
||
425 | * @api |
||
426 | */ |
||
427 | public function getSettings($name = '') |
||
443 | |||
444 | /** |
||
445 | * Передаёт в виджет ссылку на вызывающий его объект. |
||
446 | * |
||
447 | * @param AdminBaseHelper $helper |
||
448 | */ |
||
449 | public function setHelper(&$helper) |
||
453 | |||
454 | /** |
||
455 | * Возвращает текукщее значение поля фильтрации (спец. символы экранированы). |
||
456 | * |
||
457 | * @return bool|string |
||
458 | */ |
||
459 | protected function getCurrentFilterValue() |
||
467 | |||
468 | /** |
||
469 | * Проверяет корректность введенных в фильтр значений |
||
470 | * |
||
471 | * @param string $operationType тип операции |
||
472 | * @param mixed $value значение фильтра |
||
473 | * |
||
474 | * @see AdminListHelper::checkFilter(); |
||
475 | * @return bool |
||
476 | */ |
||
477 | public function checkFilter($operationType, $value) |
||
481 | |||
482 | /** |
||
483 | * Позволяет модифицировать опции, передаваемые в getList, непосредственно перед выборкой. |
||
484 | * Если в настройках явно указан способ фильтрации, до добавляет соответствующий префикс в $arFilter. |
||
485 | * Если фильтр BETWEEN, то формирует сложную логику фильтрации. |
||
486 | * |
||
487 | * @param array $filter $arFilter целиком |
||
488 | * @param array $select |
||
489 | * @param $sort |
||
490 | * @param array $raw $arSelect, $arFilter, $arSort до примененных к ним преобразований. |
||
491 | * |
||
492 | * @see AdlinListHelper::getData(); |
||
493 | */ |
||
494 | public function changeGetListOptions(&$filter, &$select, &$sort, $raw) |
||
536 | |||
537 | /** |
||
538 | * Проверяет оператор фильтрации. |
||
539 | * |
||
540 | * @return bool |
||
541 | */ |
||
542 | protected function isFilterBetween() |
||
546 | |||
547 | /** |
||
548 | * Действия, выполняемые над полем в процессе редактирования элемента, до его сохранения. |
||
549 | * По-умолчанию выполняется проверка обязательных полей и уникальности. |
||
550 | * |
||
551 | * @see AdminEditHelper::editAction(); |
||
552 | * @see AdminListHelper::editAction(); |
||
553 | */ |
||
554 | public function processEditAction() |
||
563 | |||
564 | /** |
||
565 | * В совсем экзотических случаях может потребоваться моджифицировать значение поля уже после его сохраненния в БД - |
||
566 | * для последующей обработки каким-либо другим классом. |
||
567 | */ |
||
568 | public function processAfterSaveAction() |
||
571 | |||
572 | /** |
||
573 | * Добавляет строку ошибки в массив ошибок. |
||
574 | * |
||
575 | * @param string $messageId Код сообщения об ошибке из лэнг-файла. Плейсхолдер #FIELD# будет заменён на значение |
||
576 | * параметра TITLE. |
||
577 | * @param array $replace Данные для замены. |
||
578 | * |
||
579 | * @see Loc::getMessage() |
||
580 | */ |
||
581 | protected function addError($messageId, $replace = array()) |
||
588 | |||
589 | /** |
||
590 | * Проверка заполненности обязательных полей. |
||
591 | * Не должны быть null или содержать пустую строку. |
||
592 | * |
||
593 | * @return bool |
||
594 | */ |
||
595 | View Code Duplication | public function checkRequired() |
|
605 | |||
606 | /** |
||
607 | * Выставляет код для данного виджета при инициализации. Перегружает настройки. |
||
608 | * |
||
609 | * @param string $code |
||
610 | */ |
||
611 | public function setCode($code) |
||
616 | |||
617 | /** |
||
618 | * @return mixed |
||
619 | */ |
||
620 | public function getCode() |
||
624 | |||
625 | /** |
||
626 | * Устанавливает настройки интерфейса для текущего поля. |
||
627 | * |
||
628 | * @param string $code |
||
629 | * |
||
630 | * @return bool |
||
631 | * |
||
632 | * @see AdminBaseHelper::getInterfaceSettings() |
||
633 | * @see AdminBaseHelper::setFields() |
||
634 | */ |
||
635 | public function loadSettings($code = null) |
||
650 | |||
651 | /** |
||
652 | * Возвращает название сущности данной модели. |
||
653 | * |
||
654 | * @return string|DataManager |
||
655 | */ |
||
656 | public function getEntityName() |
||
660 | |||
661 | /** |
||
662 | * @param string $entityName |
||
663 | */ |
||
664 | public function setEntityName($entityName) |
||
669 | |||
670 | /** |
||
671 | * Устанавливает значение по-умолчанию для данного поля |
||
672 | */ |
||
673 | public function setDefaultValue() |
||
679 | |||
680 | /** |
||
681 | * Передает ссылку на данные сущности в виджет |
||
682 | * |
||
683 | * @param $data |
||
684 | */ |
||
685 | public function setData(&$data) |
||
691 | |||
692 | /** |
||
693 | * Возвращает текущее значение, хранимое в поле виджета |
||
694 | * Если такого поля нет, возвращает null |
||
695 | * |
||
696 | * @return mixed|null |
||
697 | */ |
||
698 | public function getValue() |
||
704 | |||
705 | /** |
||
706 | * Устанавливает значение поля |
||
707 | * |
||
708 | * @param $value |
||
709 | * |
||
710 | * @return bool |
||
711 | */ |
||
712 | protected function setValue($value) |
||
719 | |||
720 | /** |
||
721 | * Получения названия поля таблицы, в которой хранятся множественные данные этого виджета |
||
722 | * |
||
723 | * @param string $fieldName Название поля |
||
724 | * |
||
725 | * @return bool|string |
||
726 | */ |
||
727 | public function getMultipleField($fieldName) |
||
748 | |||
749 | /** |
||
750 | * Выставляет значение отдельной настройки |
||
751 | * |
||
752 | * @param string $name |
||
753 | * @param mixed $value |
||
754 | */ |
||
755 | public function setSetting($name, $value) |
||
759 | |||
760 | /** |
||
761 | * Возвращает собранные ошибки валидации |
||
762 | * @return array |
||
763 | */ |
||
764 | public function getValidationErrors() |
||
768 | |||
769 | /** |
||
770 | * Возвращает имена для атрибута name полей фильтра. |
||
771 | * Если это фильтр BETWEEN, то вернёт массив с вариантами from и to. |
||
772 | * |
||
773 | * @return array|string |
||
774 | */ |
||
775 | protected function getFilterInputName() |
||
787 | |||
788 | /** |
||
789 | * Возвращает текст для атрибута name инпута редактирования. |
||
790 | * |
||
791 | * @param null $suffix опциональное дополнение к названию поля |
||
792 | * |
||
793 | * @return string |
||
794 | */ |
||
795 | protected function getEditInputName($suffix = null) |
||
799 | |||
800 | /** |
||
801 | * Уникальный ID для DOM HTML |
||
802 | * @return string |
||
803 | */ |
||
804 | protected function getEditInputHtmlId() |
||
810 | |||
811 | /** |
||
812 | * Возвращает текст для атрибута name инпута редактирования поля в списке |
||
813 | * @return string |
||
814 | */ |
||
815 | protected function getEditableListInputName() |
||
821 | |||
822 | /** |
||
823 | * Определяет тип вызывающего хэлпера, от чего может зависить поведение виджета. |
||
824 | * |
||
825 | * @return bool|int |
||
826 | * @see HelperWidget::EDIT_HELPER |
||
827 | * @see HelperWidget::LIST_HELPER |
||
828 | */ |
||
829 | protected function getCurrentViewType() |
||
841 | |||
842 | /** |
||
843 | * Проверяет значение поля на уникальность |
||
844 | * @return bool |
||
845 | */ |
||
846 | private function isUnique() |
||
879 | |||
880 | /** |
||
881 | * Проверяет, не является ли текущий запрос попыткой выгрузить данные в Excel |
||
882 | * @return bool |
||
883 | */ |
||
884 | protected function isExcelView() |
||
892 | |||
893 | /** |
||
894 | * @todo Вынести в ресурс (\CJSCore::Init()). |
||
895 | * @todo Описать. |
||
896 | */ |
||
897 | protected function jsHelper() |
||
1051 | } |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.