SelectSingleElement::render()   F
last analyzed

Complexity

Conditions 33
Paths > 20000

Size

Total Lines 202
Code Lines 134

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 33
eloc 134
nc 268800
nop 0
dl 0
loc 202
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Backend\Form\Element;
17
18
use TYPO3\CMS\Backend\Form\InlineStackProcessor;
19
use TYPO3\CMS\Backend\Form\Utility\FormEngineUtility;
20
use TYPO3\CMS\Core\Utility\GeneralUtility;
21
use TYPO3\CMS\Core\Utility\StringUtility;
22
23
/**
24
 * Creates a widget where only one item can be selected.
25
 * This is either a select drop-down if no size config is given or set to 1, or a select box.
26
 *
27
 * This is rendered for type=select, renderType=selectSingle
28
 */
29
class SelectSingleElement extends AbstractFormElement
30
{
31
    /**
32
     * Default field information enabled for this element.
33
     *
34
     * @var array
35
     */
36
    protected $defaultFieldInformation = [
37
        'tcaDescription' => [
38
            'renderType' => 'tcaDescription',
39
        ],
40
    ];
41
42
    /**
43
     * Default field wizards enabled for this element.
44
     *
45
     * @var array
46
     */
47
    protected $defaultFieldWizard = [
48
        'selectIcons' => [
49
            'renderType' => 'selectIcons',
50
            'disabled' => true,
51
        ],
52
        'localizationStateSelector' => [
53
            'renderType' => 'localizationStateSelector',
54
            'after' => [
55
                'selectIcons',
56
            ],
57
        ],
58
        'otherLanguageContent' => [
59
            'renderType' => 'otherLanguageContent',
60
            'after' => [ 'localizationStateSelector' ],
61
        ],
62
        'defaultLanguageDifferences' => [
63
            'renderType' => 'defaultLanguageDifferences',
64
            'after' => [ 'otherLanguageContent' ],
65
        ],
66
    ];
67
68
    /**
69
     * Render single element
70
     *
71
     * @return array As defined in initializeResultArray() of AbstractNode
72
     */
73
    public function render()
74
    {
75
        $resultArray = $this->initializeResultArray();
76
77
        $table = $this->data['tableName'];
78
        $field = $this->data['fieldName'];
79
        $parameterArray = $this->data['parameterArray'];
80
        $config = $parameterArray['fieldConf']['config'];
81
82
        $selectItems = $parameterArray['fieldConf']['config']['items'];
83
        $classList = ['form-select', 'form-control-adapt'];
84
85
        // Check against inline uniqueness
86
        /** @var InlineStackProcessor $inlineStackProcessor */
87
        $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
88
        $inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']);
89
        $uniqueIds = null;
90
        if (($this->data['isInlineChild'] ?? false) && ($this->data['inlineParentUid'] ?? false)) {
91
            // @todo: At least parts of this if is dead and/or broken: $uniqueIds is filled but never used.
92
            // See InlineControlContainer where 'inlineData' 'unique' 'used' is set. What exactly is
93
            // this if supposed to do and when should it kick in and what for?
94
            $inlineObjectName = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']);
95
            if (($this->data['inlineParentConfig']['foreign_table'] ?? false) === $table
96
                && ($this->data['inlineParentConfig']['foreign_unique'] ?? false) === $field
97
            ) {
98
                $classList[] = 't3js-inline-unique';
99
                $uniqueIds = $this->data['inlineData']['unique'][$inlineObjectName . '-' . $table]['used'];
100
            }
101
            // hide uid of parent record for symmetric relations
102
            if (($this->data['inlineParentConfig']['foreign_table'] ?? false) === $table
103
                && (
104
                    ($this->data['inlineParentConfig']['foreign_field'] ?? false) === $field
105
                    || ($this->data['inlineParentConfig']['symmetric_field'] ?? false) === $field
106
                )
107
            ) {
108
                $uniqueIds[] = $this->data['inlineParentUid'];
109
            }
110
        }
111
112
        // Initialization:
113
        $selectId = StringUtility::getUniqueId('tceforms-select-');
114
        $selectedIcon = '';
115
        $size = (int)($config['size'] ?? 0);
116
117
        // Style set on <select/>
118
        $options = '';
119
        $disabled = false;
120
        if (!empty($config['readOnly'])) {
121
            $disabled = true;
122
        }
123
124
        // Prepare groups
125
        $selectItemCounter = 0;
126
        $selectItemGroupCount = 0;
127
        $selectItemGroups = [];
128
        $selectedValue = '';
129
        $hasIcons = false;
130
131
        // In case e.g. "l10n_display" is set to "defaultAsReadonly" only one value (as string) could be handed in
132
        if (!empty($parameterArray['itemFormElValue'])) {
133
            if (is_array($parameterArray['itemFormElValue'])) {
134
                $selectedValue = (string)$parameterArray['itemFormElValue'][0];
135
            } else {
136
                $selectedValue = (string)$parameterArray['itemFormElValue'];
137
            }
138
        }
139
140
        foreach ($selectItems as $item) {
141
            if ($item[1] === '--div--') {
142
                // IS OPTGROUP
143
                if ($selectItemCounter !== 0) {
144
                    $selectItemGroupCount++;
145
                }
146
                $selectItemGroups[$selectItemGroupCount]['header'] = [
147
                    'title' => $item[0],
148
                ];
149
            } else {
150
                // IS ITEM
151
                $icon = !empty($item[2]) ? FormEngineUtility::getIconHtml($item[2], $item[0], $item[0]) : '';
152
                $selected = $selectedValue === (string)$item[1];
153
154
                if ($selected) {
155
                    $selectedIcon = $icon;
156
                }
157
158
                $selectItemGroups[$selectItemGroupCount]['items'][] = [
159
                    'title' => $this->appendValueToLabelInDebugMode($item[0], $item[1]),
160
                    'value' => $item[1],
161
                    'icon' => $icon,
162
                    'selected' => $selected,
163
                ];
164
                $selectItemCounter++;
165
            }
166
        }
167
168
        // Fallback icon
169
        // @todo: assign a special icon for non matching values?
170
        if (!$selectedIcon && $selectItemGroups[0]['items'][0]['icon']) {
171
            $selectedIcon = $selectItemGroups[0]['items'][0]['icon'];
172
        }
173
174
        // Process groups
175
        foreach ($selectItemGroups as $selectItemGroup) {
176
            // suppress groups without items
177
            if (empty($selectItemGroup['items'])) {
178
                continue;
179
            }
180
181
            $optionGroup = is_array($selectItemGroup['header'] ?? null);
182
            $options .= ($optionGroup ? '<optgroup label="' . htmlspecialchars($selectItemGroup['header']['title'], ENT_COMPAT, 'UTF-8', false) . '">' : '');
183
184
            if (is_array($selectItemGroup['items'])) {
185
                foreach ($selectItemGroup['items'] as $item) {
186
                    $options .= '<option value="' . htmlspecialchars($item['value']) . '" data-icon="' .
187
                        htmlspecialchars($item['icon']) . '"'
188
                        . ($item['selected'] ? ' selected="selected"' : '') . '>' . htmlspecialchars((string)($item['title'] ?? ''), ENT_COMPAT, 'UTF-8', false) . '</option>';
189
                }
190
                $hasIcons = !empty($item['icon']);
191
            }
192
193
            $options .= ($optionGroup ? '</optgroup>' : '');
194
        }
195
196
        $selectAttributes = [
197
            'id' => $selectId,
198
            'name' => (string)($parameterArray['itemFormElName'] ?? ''),
199
            'data-formengine-validation-rules' => $this->getValidationDataAsJsonString($config),
200
            'class' => implode(' ', $classList),
201
        ];
202
        if ($size) {
203
            $selectAttributes['size'] = (string)$size;
204
        }
205
        if ($disabled) {
206
            $selectAttributes['disabled'] = 'disabled';
207
        }
208
209
        $fieldInformationResult = $this->renderFieldInformation();
210
        $fieldInformationHtml = $fieldInformationResult['html'];
211
        $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
212
213
        $fieldControlResult = $this->renderFieldControl();
214
        $fieldControlHtml = $fieldControlResult['html'];
215
        $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldControlResult, false);
216
217
        $fieldWizardResult = $this->renderFieldWizard();
218
        $fieldWizardHtml = $fieldWizardResult['html'];
219
        $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false);
220
221
        $html = [];
222
        $html[] = '<div class="formengine-field-item t3js-formengine-field-item">';
223
        $html[] = $fieldInformationHtml;
224
        $html[] =   '<div class="form-control-wrap">';
225
        $html[] =       '<div class="form-wizards-wrap">';
226
        $html[] =           '<div class="form-wizards-element">';
227
        if ($hasIcons) {
228
            $html[] =           '<div class="input-group">';
229
            $html[] =               '<span class="input-group-addon input-group-icon">';
230
            $html[] =                   $selectedIcon;
231
            $html[] =               '</span>';
232
        }
233
        $html[] =                   '<select ' . GeneralUtility::implodeAttributes($selectAttributes, true) . '>';
234
        $html[] =                       $options;
235
        $html[] =                   '</select>';
236
        if ($hasIcons) {
237
            $html[] =           '</div>';
238
        }
239
        $html[] =           '</div>';
240
        if (!$disabled && !empty($fieldControlHtml)) {
241
            $html[] =      '<div class="form-wizards-items-aside">';
242
            $html[] =          '<div class="btn-group">';
243
            $html[] =              $fieldControlHtml;
244
            $html[] =          '</div>';
245
            $html[] =      '</div>';
246
        }
247
        if (!$disabled && !empty($fieldWizardHtml)) {
248
            $html[] =       '<div class="form-wizards-items-bottom">';
249
            $html[] =           $fieldWizardHtml;
250
            $html[] =       '</div>';
251
        }
252
        $html[] =       '</div>';
253
        $html[] =   '</div>';
254
        $html[] = '</div>';
255
256
        $resultArray['requireJsModules'][] = ['TYPO3/CMS/Backend/FormEngine/Element/SelectSingleElement' => implode(LF, [
257
            'function(SelectSingleElement) {',
258
                'require([\'TYPO3/CMS/Core/DocumentService\'], function(DocumentService) {',
259
                    'DocumentService.ready().then(function() {',
260
                        'SelectSingleElement.initialize(',
261
                            GeneralUtility::quoteJSvalue('#' . $selectId) . ',',
262
                            '{',
263
                                'onChange: function() {',
264
                                    implode('', $parameterArray['fieldChangeFunc']),
265
                                '}',
266
                            '}',
267
                        ');',
268
                    '});',
269
                '});',
270
            '}',
271
        ])];
272
273
        $resultArray['html'] = implode(LF, $html);
274
        return $resultArray;
275
    }
276
}
277