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 FieldEntry_Relationship 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 FieldEntry_Relationship, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
20 | class FieldEntry_Relationship extends FieldRelationship |
||
21 | { |
||
22 | /** |
||
23 | * |
||
24 | * Name of the field table |
||
25 | * @var string |
||
26 | */ |
||
27 | const FIELD_TBL_NAME = 'tbl_fields_entry_relationship'; |
||
28 | |||
29 | /** |
||
30 | * |
||
31 | * Current recursive level of output |
||
32 | * @var int |
||
33 | */ |
||
34 | protected $recursiveLevel = 1; |
||
35 | public function getRecursiveLevel() |
||
43 | |||
44 | /** |
||
45 | * |
||
46 | * Parent's maximum recursive level of output |
||
47 | * @var int |
||
48 | */ |
||
49 | protected $recursiveDeepness = null; |
||
50 | public function getRecursiveDeepness() |
||
58 | |||
59 | /* Cacheable Managers */ |
||
60 | private $sectionManager; |
||
61 | private $entryManager; |
||
62 | private $sectionInfos; |
||
63 | |||
64 | public $expandIncludableElements = true; |
||
65 | |||
66 | /** |
||
67 | * |
||
68 | * Constructor for the Entry_Relationship Field object |
||
69 | */ |
||
70 | public function __construct() |
||
119 | |||
120 | public function isSortable() |
||
124 | |||
125 | public function canFilter() |
||
129 | |||
130 | public function canPublishFilter() |
||
134 | |||
135 | public function canImport() |
||
139 | |||
140 | public function canPrePopulate() |
||
144 | |||
145 | public function mustBeUnique() |
||
149 | |||
150 | public function allowDatasourceOutputGrouping() |
||
154 | |||
155 | public function requiresSQLGrouping() |
||
159 | |||
160 | public function allowDatasourceParamOutput() |
||
164 | |||
165 | /* ********** INPUT AND FIELD *********** */ |
||
166 | |||
167 | |||
168 | /** |
||
169 | * |
||
170 | * Validates input |
||
171 | * Called before <code>processRawFieldData</code> |
||
172 | * @param $data |
||
173 | * @param $message |
||
174 | * @param $entry_id |
||
175 | */ |
||
176 | public function checkPostFieldData($data, &$message, $entry_id=null) |
||
205 | |||
206 | |||
207 | /** |
||
208 | * |
||
209 | * Process data before saving into database. |
||
210 | * |
||
211 | * @param array $data |
||
212 | * @param int $status |
||
213 | * @param boolean $simulate |
||
214 | * @param int $entry_id |
||
215 | * |
||
216 | * @return Array - data to be inserted into DB |
||
217 | */ |
||
218 | public function processRawFieldData($data, &$status, &$message = null, $simulate = false, $entry_id = null) |
||
241 | |||
242 | /** |
||
243 | * This function permits parsing different field settings values |
||
244 | * |
||
245 | * @param array $settings |
||
246 | * the data array to initialize if necessary. |
||
247 | */ |
||
248 | public function setFromPOST(Array $settings = array()) |
||
280 | |||
281 | |||
282 | /** |
||
283 | * |
||
284 | * Validates the field settings before saving it into the field's table |
||
285 | */ |
||
286 | public function checkFields(Array &$errors, $checkForDuplicates = true) |
||
301 | |||
302 | /** |
||
303 | * |
||
304 | * Save field settings into the field's table |
||
305 | */ |
||
306 | public function commit() |
||
380 | |||
381 | /** |
||
382 | * |
||
383 | * This function allows Fields to cleanup any additional things before it is removed |
||
384 | * from the section. |
||
385 | * @return boolean |
||
386 | */ |
||
387 | public function tearDown() |
||
392 | |||
393 | /** |
||
394 | * Generates the where filter for searching by entry id |
||
395 | * |
||
396 | * @param string $value |
||
397 | * @param @optional string $col |
||
398 | * @param @optional boolean $andOperation |
||
399 | */ |
||
400 | public function generateWhereFilter($value, $col = 'd', $andOperation = true) |
||
411 | |||
412 | /** |
||
413 | * Fetch the number of associated entries for a particular entry id |
||
414 | * |
||
415 | * @param string $value |
||
416 | */ |
||
417 | public function fetchAssociatedEntryCount($value) |
||
429 | |||
430 | public function fetchAssociatedEntrySearchValue($data, $field_id = null, $parent_entry_id = null) |
||
434 | |||
435 | public function findRelatedEntries($entry_id, $parent_field_id) |
||
447 | |||
448 | public function findParentRelatedEntries($parent_field_id, $entry_id) |
||
458 | |||
459 | public function fetchFilterableOperators() |
||
469 | |||
470 | public function fetchSuggestionTypes() |
||
474 | |||
475 | public function fetchIDsfromValue($value) |
||
476 | { |
||
477 | $ids = array(); |
||
478 | $sections = $this->getArray('sections'); |
||
479 | //$value = Lang::createHandle($value); |
||
480 | |||
481 | foreach ($sections as $sectionId) { |
||
482 | $section = $this->sectionManager->fetch($sectionId); |
||
483 | if (!$section) { |
||
484 | continue; |
||
485 | } |
||
486 | $filterableFields = $section->fetchFilterableFields(); |
||
487 | if (empty($filterableFields)) { |
||
488 | continue; |
||
489 | } |
||
490 | foreach ($filterableFields as $fId => $field) { |
||
491 | $joins = ''; |
||
492 | $where = ''; |
||
493 | if ($field instanceof FieldRelationship) { |
||
494 | continue; |
||
495 | } |
||
496 | $field->buildDSRetrievalSQL(array($value), $joins, $where, false); |
||
497 | $fEntries = $this->entryManager->fetch(null, $sectionId, null, null, $where, $joins, true, false, null, false); |
||
498 | if (!empty($fEntries)) { |
||
499 | $ids = array_merge($ids, $fEntries); |
||
500 | break; |
||
501 | } |
||
502 | } |
||
503 | } |
||
504 | |||
505 | return array_map(function ($e) { |
||
506 | return $e['id']; |
||
507 | }, $ids); |
||
508 | } |
||
509 | |||
510 | public function prepareAssociationsDrawerXMLElement(Entry $e, array $parent_association, $prepolutate = '') |
||
511 | { |
||
512 | $currentSection = SectionManager::fetch($parent_association['child_section_id']); |
||
513 | $visibleCols = $currentSection->fetchVisibleColumns(); |
||
514 | $outputFieldId = current(array_keys($visibleCols)); |
||
515 | $outputField = FieldManager::fetch($outputFieldId); |
||
516 | |||
517 | $value = $outputField->prepareReadableValue($e->getData($outputFieldId), $e->get('id'), true, __('None')); |
||
518 | |||
519 | $li = new XMLElement('li'); |
||
520 | $li->setAttribute('class', 'field-' . $this->get('type')); |
||
521 | $a = new XMLElement('a', strip_tags($value)); |
||
522 | $a->setAttribute('href', SYMPHONY_URL . '/publish/' . $parent_association['handle'] . '/edit/' . $e->get('id') . '/'); |
||
523 | $li->appendChild($a); |
||
524 | |||
525 | return $li; |
||
526 | } |
||
527 | |||
528 | /** |
||
529 | * @param string $joins |
||
530 | * @param string $where |
||
531 | */ |
||
532 | public function buildDSRetrievalSQL($data, &$joins, &$where, $andOperation = false) |
||
533 | { |
||
534 | $field_id = $this->get('id'); |
||
535 | |||
536 | // REGEX filtering is a special case, and will only work on the first item |
||
537 | // in the array. You cannot specify multiple filters when REGEX is involved. |
||
538 | if (self::isFilterRegex($data[0])) { |
||
539 | return $this->buildRegexSQL($data[0], array('entries'), $joins, $where); |
||
540 | } |
||
541 | |||
542 | $this->_key++; |
||
543 | |||
544 | $where .= ' AND (1=' . ($andOperation ? '1' : '0') . ' '; |
||
545 | |||
546 | $joins .= " |
||
547 | INNER JOIN |
||
548 | `tbl_entries_data_{$field_id}` AS `t{$field_id}_{$this->_key}` |
||
549 | ON (`e`.`id` = `t{$field_id}_{$this->_key}`.`entry_id`) |
||
550 | "; |
||
551 | |||
552 | $normalizedValues = array(); |
||
553 | foreach ($data as $value) { |
||
554 | if (!is_numeric($value) && !is_null($value)) { |
||
555 | $normalizedValues = array_merge($normalizedValues, $this->fetchIDsfromValue($value)); |
||
556 | } else { |
||
557 | $normalizedValues[] = $value; |
||
558 | } |
||
559 | } |
||
560 | |||
561 | foreach ($normalizedValues as $value) { |
||
562 | $where .= $this->generateWhereFilter($this->cleanValue($value), "t{$field_id}_{$this->_key}", $andOperation); |
||
563 | } |
||
564 | |||
565 | $where .= ')'; |
||
566 | |||
567 | return true; // this tells the DS Manager that filters are OK!! |
||
568 | } |
||
569 | |||
570 | /* ******* EVENTS ******* */ |
||
571 | |||
572 | public function getExampleFormMarkup() |
||
573 | { |
||
574 | $label = Widget::Label($this->get('label')); |
||
575 | $label->appendChild(Widget::Input('fields['.$this->get('element_name').'][entries]', null, 'hidden')); |
||
576 | |||
577 | return $label; |
||
578 | } |
||
579 | |||
580 | |||
581 | /* ******* DATA SOURCE ******* */ |
||
582 | |||
583 | private function fetchEntry($eId, $elements = array()) |
||
584 | { |
||
585 | $entry = EntryManager::fetch($eId, null, 1, 0, null, null, false, true, $elements, false); |
||
586 | if (!is_array($entry) || count($entry) !== 1) { |
||
587 | return null; |
||
588 | } |
||
589 | return $entry[0]; |
||
590 | } |
||
591 | |||
592 | protected function fetchAllIncludableElements() |
||
593 | { |
||
594 | $sections = $this->getArray('sections'); |
||
595 | return $allElements = array_reduce($this->sectionInfos->fetch($sections), function ($memo, $item) { |
||
596 | return array_merge($memo, array_map(function ($field) use ($item) { |
||
597 | return $item['handle'] . '.' . $field['handle']; |
||
598 | }, $item['fields'])); |
||
599 | }, array()); |
||
600 | } |
||
601 | |||
602 | public function fetchIncludableElements() |
||
603 | { |
||
604 | $label = $this->get('element_name'); |
||
605 | $elements = $this->getArray('elements'); |
||
606 | if (empty($elements)) { |
||
607 | $elements = array('*'); |
||
608 | } |
||
609 | $includedElements = array(); |
||
610 | // Include everything |
||
611 | if ($this->expandIncludableElements) { |
||
612 | $includedElements[] = $label . ': *'; |
||
613 | } |
||
614 | // Include individual elements |
||
615 | foreach ($elements as $elem) { |
||
616 | $elem = trim($elem); |
||
617 | if ($elem !== '*') { |
||
618 | $includedElements[] = $label . ': ' . $elem; |
||
619 | } else if ($this->expandIncludableElements) { |
||
620 | $includedElements = array_unique(array_merge($includedElements, array_map(function ($item) use ($label) { |
||
621 | return $label . ': ' . $item; |
||
622 | }, $this->fetchAllIncludableElements()))); |
||
623 | } else { |
||
624 | $includedElements = array('*'); |
||
625 | } |
||
626 | } |
||
627 | // Include system dates |
||
628 | $includedElements[] = $label . ': system:creation-date'; |
||
629 | $includedElements[] = $label . ': system:modification-date'; |
||
630 | return $includedElements; |
||
631 | } |
||
632 | |||
633 | /** |
||
634 | * Appends data into the XML tree of a Data Source |
||
635 | * @param $wrapper |
||
636 | * @param $data |
||
637 | */ |
||
638 | public function appendFormattedElement(XMLElement &$wrapper, $data, $encode = false, $mode = null, $entry_id = null) |
||
639 | { |
||
640 | if (!is_array($data) || empty($data)) { |
||
641 | return; |
||
642 | } |
||
643 | |||
644 | // try to find an existing root |
||
645 | $root = null; |
||
646 | $newRoot = false; |
||
647 | foreach (array_reverse($wrapper->getChildren()) as $xmlField) { |
||
648 | if ($xmlField->getName() === $this->get('element_name')) { |
||
649 | $root = $xmlField; |
||
650 | break; |
||
651 | } |
||
652 | } |
||
653 | |||
654 | // root was not found, create one |
||
655 | if (!$root) { |
||
656 | $root = new XMLElement($this->get('element_name')); |
||
657 | $newRoot = true; |
||
658 | } |
||
659 | |||
660 | // devkit will load |
||
661 | $devkit = isset($_GET['debug']) && (empty($_GET['debug']) || $_GET['debug'] == 'xml'); |
||
662 | |||
663 | // selected items |
||
664 | $entries = static::getEntries($data); |
||
665 | |||
666 | // current linked entries |
||
667 | $root->setAttribute('entries', $data['entries']); |
||
668 | |||
669 | // available sections |
||
670 | $root->setAttribute('sections', $this->get('sections')); |
||
671 | |||
672 | // included elements |
||
673 | $elements = static::parseElements($this); |
||
674 | |||
675 | // DS mode |
||
676 | if (!$mode) { |
||
677 | $mode = '*'; |
||
678 | } |
||
679 | |||
680 | $parentDeepness = General::intval($this->recursiveDeepness); |
||
681 | $deepness = General::intval($this->get('deepness')); |
||
682 | |||
683 | // both deepnesses are defined and parent restricts more |
||
684 | if ($parentDeepness > 0 && $deepness > 0 && $parentDeepness < $deepness) { |
||
685 | $deepness = $parentDeepness; |
||
686 | } |
||
687 | // parent is defined, current is not |
||
688 | else if ($parentDeepness > 0 && $deepness < 1) { |
||
689 | $deepness = $parentDeepness; |
||
690 | } |
||
691 | |||
692 | // cache recursive level because recursion might |
||
693 | // change its value later on. |
||
694 | $recursiveLevel = $this->recursiveLevel; |
||
695 | |||
696 | // build entries |
||
697 | foreach ($entries as $eId) { |
||
698 | // try to find and existing item |
||
699 | // TODO: keep last index found since it should be the next |
||
700 | $item = null; |
||
701 | $newItem = false; |
||
702 | foreach ($root->getChildren() as $xmlItem) { |
||
703 | if (General::intval($xmlItem->getAttribute('id')) === General::intval($eId)) { |
||
704 | $item = $xmlItem; |
||
705 | break; |
||
706 | } |
||
707 | } |
||
708 | |||
709 | // item was not found, create one |
||
710 | if (!$item) { |
||
711 | $item = new XMLElement('item'); |
||
712 | $item->setAllowEmptyAttributes(false); |
||
713 | // output id |
||
714 | $item->setAttribute('id', $eId); |
||
715 | // output recursive level |
||
716 | $item->setAttribute('level', $recursiveLevel); |
||
717 | $item->setAttribute('max-level', $deepness); |
||
718 | $newItem = true; |
||
719 | // item was found, but it is an error, so we can skip it |
||
720 | } else if ($item->getName() === 'error') { |
||
721 | continue; |
||
722 | } |
||
723 | |||
724 | // max recursion check |
||
725 | if ($deepness < 1 || $recursiveLevel < $deepness) { |
||
726 | // current entry, without data |
||
727 | $entry = $this->fetchEntry($eId); |
||
728 | |||
729 | // entry not found... |
||
730 | if (!$entry || empty($entry)) { |
||
731 | $error = new XMLElement('error'); |
||
732 | $error->setAttribute('id', $eId); |
||
733 | $error->setValue(__('Error: entry `%s` not found', array($eId))); |
||
734 | $root->prependChild($error); |
||
735 | continue; |
||
736 | } |
||
737 | |||
738 | // fetch section infos |
||
739 | $sectionId = $entry->get('section_id'); |
||
740 | $section = $this->sectionManager->fetch($sectionId); |
||
741 | $sectionName = $section->get('handle'); |
||
742 | // cache fields info |
||
743 | if (!isset($section->er_field_cache)) { |
||
744 | $section->er_field_cache = $section->fetchFields(); |
||
745 | } |
||
746 | |||
747 | // set section related attributes |
||
748 | $item->setAttribute('section-id', $sectionId); |
||
749 | $item->setAttribute('section', $sectionName); |
||
750 | |||
751 | // Get the valid elements for this section only |
||
752 | $validElements = $elements[$sectionName]; |
||
753 | |||
754 | // adjust the mode for the current section |
||
755 | $curMode = $mode; |
||
756 | |||
757 | // remove section name from current mode so "sectionName.field" becomes simply "field" |
||
758 | if (preg_match('/^(' . $sectionName . '\.)(.*)$/sU', $curMode)) { |
||
759 | $curMode = preg_replace('/^' . $sectionName . '\./sU', '', $curMode); |
||
760 | } |
||
761 | // remove section name from current mode "sectionName" and |
||
762 | // treat it like if it is "sectionName: *" |
||
763 | else if (preg_match('/^(' . $sectionName . ')$/sU', $curMode)) { |
||
764 | $curMode = '*'; |
||
765 | } |
||
766 | // checks if the mode is the current field |
||
767 | // treat it like if is is "*" |
||
768 | elseif ($curMode === $this->get('element_name')) { |
||
769 | $curMode = '*'; |
||
770 | } |
||
771 | // section name was not found in mode, check if the mode is "*" |
||
772 | else if ($curMode !== '*') { |
||
773 | // mode forbids this section |
||
774 | $validElements = null; |
||
775 | } |
||
776 | |||
777 | // special case for system:creation-date |
||
778 | if (preg_match('/^system:\s*creation-date$/sU', $curMode)) { |
||
779 | $item->appendChild( |
||
780 | General::createXMLDateObject( |
||
781 | DateTimeObj::get('U', $entry->get('creation_date')), |
||
782 | 'created' |
||
783 | ) |
||
784 | ); |
||
785 | continue; |
||
786 | } elseif (preg_match('/^system:\s*modification-date$/sU', $curMode)) { |
||
787 | $item->appendChild( |
||
788 | General::createXMLDateObject( |
||
789 | DateTimeObj::get('U', $entry->get('modification_date')), |
||
790 | 'modified' |
||
791 | ) |
||
792 | ); |
||
793 | continue; |
||
794 | } |
||
795 | |||
796 | // this section is not selected, bail out |
||
797 | View Code Duplication | if (!is_array($validElements)) { |
|
798 | if ($newItem) { |
||
799 | if ($devkit) { |
||
800 | $item->setAttribute('x-forbidden-by-ds', $curMode); |
||
801 | } |
||
802 | $root->appendChild($item); |
||
803 | } |
||
804 | continue; |
||
805 | } else { |
||
806 | if ($devkit) { |
||
807 | $item->setAttribute('x-forbidden-by-ds', null); |
||
808 | } |
||
809 | } |
||
810 | |||
811 | // selected fields for fetching |
||
812 | $sectionElements = array(); |
||
813 | |||
814 | // everything is allowed |
||
815 | if (in_array('*', $validElements)) { |
||
816 | if ($curMode !== '*') { |
||
817 | // get only the mode |
||
818 | $sectionElements = array($curMode); |
||
819 | } |
||
820 | else { |
||
821 | // setting null = get all |
||
822 | $sectionElements = null; |
||
823 | } |
||
824 | } |
||
825 | // only use valid elements |
||
826 | else { |
||
827 | if ($curMode !== '*') { |
||
828 | // is this field allowed ? |
||
829 | if (self::isFieldIncluded($curMode, $validElements)) { |
||
830 | // get only the mode |
||
831 | $sectionElements = array($curMode); |
||
832 | } |
||
833 | else { |
||
834 | // $curMode selects something outside of |
||
835 | // the valid elements: select nothing |
||
836 | $sectionElements = array(); |
||
837 | } |
||
838 | } |
||
839 | else { |
||
840 | // use field's valid elements |
||
841 | $sectionElements = $validElements; |
||
842 | } |
||
843 | } |
||
844 | |||
845 | // Filtering is enabled, but nothing is selected |
||
846 | View Code Duplication | if (is_array($sectionElements) && empty($sectionElements)) { |
|
847 | if ($newItem) { |
||
848 | $root->appendChild($item); |
||
849 | if ($devkit) { |
||
850 | $item->setAttribute('x-forbidden-by-selection', $curMode); |
||
851 | } |
||
852 | } |
||
853 | continue; |
||
854 | } else { |
||
855 | if ($devkit) { |
||
856 | $item->setAttribute('x-forbidden-by-selection', null); |
||
857 | } |
||
858 | } |
||
859 | |||
860 | // fetch current entry again, but with data for the allowed schema |
||
861 | $entry = $this->fetchEntry($eId, $sectionElements); |
||
862 | |||
863 | // cache the entry data |
||
864 | $entryData = $entry->getData(); |
||
865 | |||
866 | // for each field returned for this entry... |
||
867 | foreach ($entryData as $fieldId => $data) { |
||
868 | $filteredData = array_filter($data, function ($value) { |
||
869 | return $value != null; |
||
870 | }); |
||
871 | |||
872 | if (empty($filteredData)) { |
||
873 | continue; |
||
874 | } |
||
875 | |||
876 | $field = $section->er_field_cache[$fieldId]; |
||
877 | $fieldName = $field->get('element_name'); |
||
878 | |||
879 | $submodes = null; |
||
880 | if ($curMode === '*') { |
||
881 | $submodes = self::getAllSelectedFieldModes($fieldName, $validElements); |
||
882 | } else { |
||
883 | $submodes = array($curMode); |
||
884 | } |
||
885 | |||
886 | // Special treatments for ERF |
||
887 | if ($field instanceof FieldEntry_relationship) { |
||
888 | // Increment recursive level |
||
889 | $field->recursiveLevel = $recursiveLevel + 1; |
||
890 | $field->recursiveDeepness = $deepness; |
||
891 | } |
||
892 | |||
893 | // current selection does not specify a mode |
||
894 | if ($submodes === null) { |
||
895 | if ($field instanceof FieldEntry_Relationship) { |
||
896 | $field->expandIncludableElements = false; |
||
897 | } |
||
898 | $submodes = array_map(function ($fieldIncludableElement) use ($fieldName) { |
||
899 | return FieldEntry_relationship::extractMode($fieldName, $fieldIncludableElement); |
||
900 | }, $field->fetchIncludableElements()); |
||
901 | if ($field instanceof FieldEntry_Relationship) { |
||
902 | $field->expandIncludableElements = true; |
||
903 | } |
||
904 | } elseif (empty($submodes)) { |
||
905 | if ($devkit) { |
||
906 | $item->setAttribute('x-selection-mode-empty', $fieldName); |
||
907 | } |
||
908 | } |
||
909 | |||
910 | // Append the formatted element for each requested mode |
||
911 | foreach ($submodes as $submode) { |
||
912 | $field->appendFormattedElement($item, $data, $encode, $submode, $eId); |
||
913 | } |
||
914 | } |
||
915 | // output current mode |
||
916 | $item->setAttribute('matched-element', $curMode); |
||
917 | // no field selected |
||
918 | if (is_array($sectionElements) && empty($sectionElements)) { |
||
919 | $item->setAttribute('empty-selection', 'yes'); |
||
920 | } |
||
921 | } // end max recursion check |
||
922 | |||
923 | if ($newItem) { |
||
924 | // append item when done |
||
925 | $root->appendChild($item); |
||
926 | } |
||
927 | } // end each entries |
||
928 | |||
929 | if ($newRoot) { |
||
930 | // output mode for this field |
||
931 | if ($devkit) { |
||
932 | $root->setAttribute('x-data-source-mode', $mode); |
||
933 | $root->setAttribute('x-field-included-elements', $this->get('elements')); |
||
934 | } |
||
935 | // add all our data to the wrapper; |
||
936 | $wrapper->appendChild($root); |
||
937 | } else { |
||
938 | if ($devkit) { |
||
939 | $root->setAttribute('x-data-source-mode', $root->getAttribute('x-data-source-mode') . ', ' . $mode); |
||
940 | } |
||
941 | } |
||
942 | |||
943 | // clean up |
||
944 | $this->recursiveLevel = 1; |
||
945 | $this->recursiveDeepness = null; |
||
946 | } |
||
947 | |||
948 | public function getParameterPoolValue(array $data, $entry_id = null) |
||
949 | { |
||
950 | if(!is_array($data) || empty($data)) return; |
||
951 | return static::getEntries($data); |
||
952 | } |
||
953 | |||
954 | /* ********* Utils *********** */ |
||
955 | |||
956 | /** |
||
957 | * Return true if $fieldName is allowed in $sectionElements |
||
958 | * @param string $fieldName |
||
959 | * @param string $sectionElements |
||
960 | * @return bool |
||
961 | */ |
||
962 | public static function isFieldIncluded($fieldName, $sectionElements) |
||
963 | { |
||
964 | $modes = self::getAllSelectedFieldModes($fieldName, $sectionElements); |
||
965 | return $modes === null || !empty($modes); |
||
966 | } |
||
967 | |||
968 | /** |
||
969 | * Returns all selected modes allowed in $sectionElements or null if they are all allowed |
||
970 | * @param string $fieldName |
||
971 | * @param string $sectionElements |
||
972 | * @return array|null |
||
973 | */ |
||
974 | public static function getAllSelectedFieldModes($fieldName, $sectionElements) |
||
975 | { |
||
976 | $elements = array(); |
||
977 | if (is_array($sectionElements)) { |
||
978 | foreach ($sectionElements as $element) { |
||
979 | // everything is allowed, use "fieldName" directly |
||
980 | if ($element === '*' || $fieldName === $element) { |
||
981 | return null; |
||
982 | } |
||
983 | // make "fieldName: *" the same as "fieldName" |
||
984 | if (preg_match('/\s*:\s*\*/sU', $fieldName)) { |
||
985 | $fieldName = trim(current(explode(':', $fieldName))); |
||
986 | } |
||
987 | // "fieldName" is included as-is |
||
988 | if ($fieldName === $element) { |
||
989 | return null; |
||
990 | } |
||
991 | |||
992 | // element starts with "fieldName:" |
||
993 | if (preg_match('/^' . $fieldName . '\s*:/sU', $element)) { |
||
994 | $elements[] = self::extractMode($fieldName, $element); |
||
995 | } |
||
996 | } |
||
997 | if (!empty($elements)) { |
||
998 | return $elements; |
||
999 | } |
||
1000 | } |
||
1001 | return $elements; |
||
1002 | } |
||
1003 | |||
1004 | public static function parseElements($field) |
||
1005 | { |
||
1006 | $elements = array(); |
||
1007 | $exElements = $field->getArray('elements'); |
||
1008 | |||
1009 | if (in_array('*', $exElements)) { |
||
1010 | $sections = $field->getArray('sections'); |
||
1011 | $sections = SectionManager::fetch($sections); |
||
1012 | return array_reduce($sections, function ($result, $section) { |
||
1013 | $result[$section->get('handle')] = array('*'); |
||
1014 | return $result; |
||
1015 | }, array()); |
||
1016 | } |
||
1017 | |||
1018 | foreach ($exElements as $value) { |
||
1019 | if (!$value) { |
||
1020 | continue; |
||
1021 | } |
||
1022 | // sectionName.fieldName or sectionName.* |
||
1023 | $parts = array_map(trim, explode('.', $value, 2)); |
||
1024 | // first time seeing this section |
||
1025 | if (!isset($elements[$parts[0]])) { |
||
1026 | $elements[$parts[0]] = array(); |
||
1027 | } |
||
1028 | // we have a value after the dot |
||
1029 | if (isset($parts[1]) && !!$parts[1]) { |
||
1030 | $elements[$parts[0]][] = $parts[1]; |
||
1031 | } |
||
1032 | // sectionName only |
||
1033 | else if (!isset($parts[1])) { |
||
1034 | $elements[$parts[0]][] = '*'; |
||
1035 | } |
||
1036 | } |
||
1037 | |||
1038 | return $elements; |
||
1039 | } |
||
1040 | |||
1041 | public static function extractMode($fieldName, $mode) |
||
1053 | |||
1054 | private function buildSectionSelect($name) |
||
1068 | |||
1069 | View Code Duplication | private function appendSelectionSelect(&$wrapper) |
|
1083 | |||
1084 | private function createEntriesHiddenInput($data) |
||
1094 | |||
1095 | private function createActionBarMenu($sections) |
||
1096 | { |
||
1097 | $wrap = new XMLElement('div'); |
||
1098 | $actionBar = ''; |
||
1099 | $modeFooter = $this->get('mode_footer'); |
||
1100 | if ($modeFooter) { |
||
1101 | $section = $this->sectionManager->fetch($this->get('parent_section')); |
||
1102 | $actionBar = ERFXSLTUTilities::processXSLT($this, null, $section->get('handle'), null, 'mode_footer', isset($_REQUEST['debug']), 'field'); |
||
1103 | } |
||
1104 | if (empty($actionBar)) { |
||
1105 | $fieldset = new XMLElement('fieldset'); |
||
1106 | $fieldset->setAttribute('class', 'single'); |
||
1156 | |||
1157 | /* ********* UI *********** */ |
||
1158 | |||
1159 | /** |
||
1160 | * |
||
1161 | * Builds the UI for the field's settings when creating/editing a section |
||
1162 | * @param XMLElement $wrapper |
||
1163 | * @param array $errors |
||
1164 | */ |
||
1165 | public function displaySettingsPanel(XMLElement &$wrapper, $errors=null) |
||
1297 | |||
1298 | /** |
||
1299 | * |
||
1300 | * Builds the UI for the publish page |
||
1301 | * @param XMLElement $wrapper |
||
1302 | * @param mixed $data |
||
1303 | * @param mixed $flagWithError |
||
1304 | * @param string $fieldnamePrefix |
||
1305 | * @param string $fieldnamePostfix |
||
1306 | */ |
||
1307 | public function displayPublishPanel(XMLElement &$wrapper, $data = null, $flagWithError = null, $fieldnamePrefix = null, $fieldnamePostfix = null, $entry_id = null) |
||
1365 | |||
1366 | /** |
||
1367 | * |
||
1368 | * Return a plain text representation of the field's data |
||
1369 | * @param array $data |
||
1370 | * @param int $entry_id |
||
1371 | */ |
||
1372 | public function prepareTextValue($data, $entry_id = null) |
||
1379 | |||
1380 | /** |
||
1381 | * Format this field value for display as readable text value. |
||
1382 | * |
||
1383 | * @param array $data |
||
1384 | * an associative array of data for this string. At minimum this requires a |
||
1385 | * key of 'value'. |
||
1386 | * @param integer $entry_id (optional) |
||
1387 | * An option entry ID for more intelligent processing. Defaults to null. |
||
1388 | * @param string $defaultValue (optional) |
||
1389 | * The value to use when no plain text representation of the field's data |
||
1390 | * can be made. Defaults to null. |
||
1391 | * @return string |
||
1392 | * the readable text summary of the values of this field instance. |
||
1393 | */ |
||
1394 | public function prepareReadableValue($data, $entry_id = null, $truncate = false, $defaultValue = 'None') |
||
1414 | |||
1415 | /** |
||
1416 | * Format this field value for display in the publish index tables. |
||
1417 | * |
||
1418 | * @param array $data |
||
1419 | * an associative array of data for this string. At minimum this requires a |
||
1420 | * key of 'value'. |
||
1421 | * @param XMLElement $link (optional) |
||
1422 | * an XML link structure to append the content of this to provided it is not |
||
1423 | * null. it defaults to null. |
||
1424 | * @param integer $entry_id (optional) |
||
1425 | * An option entry ID for more intelligent processing. defaults to null |
||
1426 | * @return string |
||
1427 | * the formatted string summary of the values of this field instance. |
||
1428 | */ |
||
1429 | public function prepareTableValue($data, XMLElement $link = null, $entry_id = null) |
||
1466 | |||
1467 | /* ********* SQL Data Definition ************* */ |
||
1468 | |||
1469 | /** |
||
1470 | * |
||
1471 | * Creates table needed for entries of individual fields |
||
1472 | */ |
||
1473 | public function createTable() |
||
1487 | |||
1488 | /** |
||
1489 | * Creates the table needed for the settings of the field |
||
1490 | */ |
||
1491 | public static function createFieldTable() |
||
1521 | |||
1522 | public static function update_102() |
||
1548 | |||
1549 | public static function update_103() |
||
1559 | |||
1560 | public static function update_200() |
||
1580 | |||
1581 | public static function update_2008() |
||
1591 | |||
1592 | /** |
||
1593 | * |
||
1594 | * Drops the table needed for the settings of the field |
||
1595 | */ |
||
1596 | public static function deleteFieldTable() |
||
1604 | |||
1605 | private static function removeSectionAssociation($child_field_id) |
||
1609 | } |
||
1610 |
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.