‪TYPO3CMS  11.5
SelectSingleElement.php
Go to the documentation of this file.
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 
17 
18 use TYPO3\CMS\Backend\Form\Behavior\OnFieldChangeTrait;
24 
32 {
33  use OnFieldChangeTrait;
34 
40  protected ‪$defaultFieldInformation = [
41  'tcaDescription' => [
42  'renderType' => 'tcaDescription',
43  ],
44  ];
45 
51  protected ‪$defaultFieldWizard = [
52  'selectIcons' => [
53  'renderType' => 'selectIcons',
54  'disabled' => true,
55  ],
56  'localizationStateSelector' => [
57  'renderType' => 'localizationStateSelector',
58  'after' => [
59  'selectIcons',
60  ],
61  ],
62  'otherLanguageContent' => [
63  'renderType' => 'otherLanguageContent',
64  'after' => [ 'localizationStateSelector' ],
65  ],
66  'defaultLanguageDifferences' => [
67  'renderType' => 'defaultLanguageDifferences',
68  'after' => [ 'otherLanguageContent' ],
69  ],
70  ];
71 
77  public function ‪render()
78  {
79  $resultArray = $this->‪initializeResultArray();
80 
81  $table = $this->data['tableName'];
82  $field = $this->data['fieldName'];
83  $parameterArray = $this->data['parameterArray'];
84  $config = $parameterArray['fieldConf']['config'];
85 
86  $selectItems = $parameterArray['fieldConf']['config']['items'];
87  $classList = ['form-select', 'form-control-adapt'];
88 
89  // Check against inline uniqueness
90  $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
91  $inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']);
92  $uniqueIds = [];
93  if (($this->data['isInlineChild'] ?? false) && ($this->data['inlineParentUid'] ?? false)) {
94  // If config[foreign_unique] is set for the parent inline field, all
95  // already used unique ids must be excluded from the select items.
96  $inlineObjectName = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']);
97  if (($this->data['inlineParentConfig']['foreign_table'] ?? false) === $table
98  && ($this->data['inlineParentConfig']['foreign_unique'] ?? false) === $field
99  ) {
100  $classList[] = 't3js-inline-unique';
101  $uniqueIds = $this->data['inlineData']['unique'][$inlineObjectName . '-' . $table]['used'] ?? [];
102  }
103  // hide uid of parent record for symmetric relations
104  if (($this->data['inlineParentConfig']['foreign_table'] ?? false) === $table
105  && (
106  ($this->data['inlineParentConfig']['foreign_field'] ?? false) === $field
107  || ($this->data['inlineParentConfig']['symmetric_field'] ?? false) === $field
108  )
109  ) {
110  $uniqueIds[] = $this->data['inlineParentUid'];
111  }
112  $uniqueIds = array_map(static fn($item) => (int)$item, $uniqueIds);
113  }
114 
115  // Initialization:
116  $selectId = ‪StringUtility::getUniqueId('tceforms-select-');
117  $selectedIcon = '';
118  $size = (int)($config['size'] ?? 0);
119 
120  // Style set on <select/>
121  $options = '';
122  $disabled = false;
123  if (!empty($config['readOnly'])) {
124  $disabled = true;
125  }
126 
127  // Prepare groups
128  $selectItemCounter = 0;
129  $selectItemGroupCount = 0;
130  $selectItemGroups = [];
131  $selectedValue = '';
132  $hasIcons = false;
133 
134  // In case e.g. "l10n_display" is set to "defaultAsReadonly" only one value (as string) could be handed in
135  if (!empty($parameterArray['itemFormElValue'])) {
136  if (is_array($parameterArray['itemFormElValue'])) {
137  $selectedValue = (string)$parameterArray['itemFormElValue'][0];
138  } else {
139  $selectedValue = (string)$parameterArray['itemFormElValue'];
140  }
141  }
142 
143  foreach ($selectItems as $item) {
144  $selected = $selectedValue === (string)$item[1];
145 
146  if ($item[1] === '--div--') {
147  // IS OPTGROUP
148  if ($selectItemCounter !== 0) {
149  $selectItemGroupCount++;
150  }
151  $selectItemGroups[$selectItemGroupCount]['header'] = [
152  'title' => $item[0],
153  ];
154  } elseif ($selected || !in_array((int)$item[1], $uniqueIds, true)) {
155  // IS ITEM
156  $icon = !empty($item[2]) ? ‪FormEngineUtility::getIconHtml($item[2], $item[0], $item[0]) : '';
157 
158  if ($selected) {
159  $selectedIcon = $icon;
160  }
161 
162  $selectItemGroups[$selectItemGroupCount]['items'][] = [
163  'title' => $this->‪appendValueToLabelInDebugMode($item[0], $item[1]),
164  'value' => $item[1],
165  'icon' => $icon,
166  'selected' => $selected,
167  ];
168  $selectItemCounter++;
169  }
170  }
171 
172  // Fallback icon
173  // @todo: assign a special icon for non matching values?
174  if (!$selectedIcon && !empty($selectItemGroups[0]['items'][0]['icon'])) {
175  $selectedIcon = $selectItemGroups[0]['items'][0]['icon'];
176  }
177 
178  // Process groups
179  foreach ($selectItemGroups as $selectItemGroup) {
180  // suppress groups without items
181  if (empty($selectItemGroup['items'])) {
182  continue;
183  }
184 
185  $optionGroup = is_array($selectItemGroup['header'] ?? null);
186  $options .= ($optionGroup ? '<optgroup label="' . htmlspecialchars($selectItemGroup['header']['title'], ENT_COMPAT, 'UTF-8', false) . '">' : '');
187 
188  if (is_array($selectItemGroup['items'])) {
189  foreach ($selectItemGroup['items'] as $item) {
190  $options .= '<option value="' . htmlspecialchars($item['value']) . '" data-icon="' .
191  htmlspecialchars($item['icon']) . '"'
192  . ($item['selected'] ? ' selected="selected"' : '') . '>' . htmlspecialchars((string)($item['title'] ?? ''), ENT_COMPAT, 'UTF-8', false) . '</option>';
193  }
194  $hasIcons = !empty($item['icon']);
195  }
196 
197  $options .= ($optionGroup ? '</optgroup>' : '');
198  }
199 
200  $selectAttributes = [
201  'id' => $selectId,
202  'name' => (string)($parameterArray['itemFormElName'] ?? ''),
203  'data-formengine-validation-rules' => $this->‪getValidationDataAsJsonString($config),
204  'class' => implode(' ', $classList),
205  ];
206  if ($size) {
207  $selectAttributes['size'] = (string)$size;
208  }
209  if ($disabled) {
210  $selectAttributes['disabled'] = 'disabled';
211  }
212 
213  $fieldInformationResult = $this->‪renderFieldInformation();
214  $fieldInformationHtml = $fieldInformationResult['html'];
215  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
216 
217  $fieldControlResult = $this->‪renderFieldControl();
218  $fieldControlHtml = $fieldControlResult['html'];
219  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldControlResult, false);
220 
221  $fieldWizardResult = $this->‪renderFieldWizard();
222  $fieldWizardHtml = $fieldWizardResult['html'];
223  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false);
224 
225  $html = [];
226  $html[] = '<div class="formengine-field-item t3js-formengine-field-item">';
227  $html[] = $fieldInformationHtml;
228  $html[] = '<div class="form-control-wrap">';
229  $html[] = '<div class="form-wizards-wrap">';
230  $html[] = '<div class="form-wizards-element">';
231  if ($hasIcons) {
232  $html[] = '<div class="input-group">';
233  $html[] = '<span class="input-group-addon input-group-icon">';
234  $html[] = $selectedIcon;
235  $html[] = '</span>';
236  }
237  $html[] = '<select ' . GeneralUtility::implodeAttributes($selectAttributes, true) . '>';
238  $html[] = $options;
239  $html[] = '</select>';
240  if ($hasIcons) {
241  $html[] = '</div>';
242  }
243  $html[] = '</div>';
244  if (!$disabled && !empty($fieldControlHtml)) {
245  $html[] = '<div class="form-wizards-items-aside form-wizards-items-aside--field-control">';
246  $html[] = '<div class="btn-group">';
247  $html[] = $fieldControlHtml;
248  $html[] = '</div>';
249  $html[] = '</div>';
250  }
251  if (!$disabled && !empty($fieldWizardHtml)) {
252  $html[] = '<div class="form-wizards-items-bottom">';
253  $html[] = $fieldWizardHtml;
254  $html[] = '</div>';
255  }
256  $html[] = '</div>';
257  $html[] = '</div>';
258  $html[] = '</div>';
259 
260  if ($this->validateOnFieldChange($parameterArray['fieldChangeFunc'] ?? [])) {
261  $onFieldChangeItems = $this->getOnFieldChangeItems($parameterArray['fieldChangeFunc'] ?? []);
262  $resultArray['requireJsModules']['selectSingleElement'] = ‪JavaScriptModuleInstruction::forRequireJS(
263  'TYPO3/CMS/Backend/FormEngine/Element/SelectSingleElement'
264  )->invoke('initializeOnReady', '#' . $selectId, ['onChange' => $onFieldChangeItems]);
265  } else {
266  // @deprecated
267  $resultArray['requireJsModules'][] = ['TYPO3/CMS/Backend/FormEngine/Element/SelectSingleElement' => implode(LF, [
268  'function(SelectSingleElement) {',
269  'SelectSingleElement.initializeOnReady(',
270  GeneralUtility::quoteJSvalue('#' . $selectId) . ',',
271  '{',
272  'onChange: function() {',
273  implode('', $parameterArray['fieldChangeFunc']),
274  '}',
275  '}',
276  ');',
277  '}',
278  ])];
279  }
280 
281  $resultArray['html'] = implode(LF, $html);
282  return $resultArray;
283  }
284 }
‪TYPO3\CMS\Backend\Form\Element\SelectSingleElement\$defaultFieldWizard
‪array $defaultFieldWizard
Definition: SelectSingleElement.php:48
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldInformation
‪array renderFieldInformation()
Definition: AbstractFormElement.php:76
‪TYPO3\CMS\Backend\Form\AbstractNode\mergeChildReturnIntoExistingResult
‪array mergeChildReturnIntoExistingResult(array $existing, array $childReturn, bool $mergeHtml=true)
Definition: AbstractNode.php:120
‪TYPO3\CMS\Backend\Form\AbstractNode\initializeResultArray
‪array initializeResultArray()
Definition: AbstractNode.php:91
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement
Definition: AbstractFormElement.php:35
‪TYPO3\CMS\Core\Page\JavaScriptModuleInstruction
Definition: JavaScriptModuleInstruction.php:23
‪TYPO3\CMS\Backend\Form\Element
Definition: AbstractFormElement.php:16
‪TYPO3\CMS\Core\Page\JavaScriptModuleInstruction\forRequireJS
‪static self forRequireJS(string $name, string $exportName=null)
Definition: JavaScriptModuleInstruction.php:49
‪TYPO3\CMS\Backend\Form\Utility\FormEngineUtility
Definition: FormEngineUtility.php:39
‪TYPO3\CMS\Backend\Form\AbstractNode\getValidationDataAsJsonString
‪string getValidationDataAsJsonString(array $config)
Definition: AbstractNode.php:156
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldControl
‪array renderFieldControl()
Definition: AbstractFormElement.php:92
‪TYPO3\CMS\Backend\Form\Element\SelectSingleElement\$defaultFieldInformation
‪array $defaultFieldInformation
Definition: SelectSingleElement.php:38
‪TYPO3\CMS\Backend\Form\Utility\FormEngineUtility\getIconHtml
‪static string getIconHtml($icon, $alt='', $title='')
Definition: FormEngineUtility.php:122
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\appendValueToLabelInDebugMode
‪string appendValueToLabelInDebugMode($label, $value)
Definition: AbstractFormElement.php:436
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static string getUniqueId($prefix='')
Definition: StringUtility.php:128
‪TYPO3\CMS\Backend\Form\InlineStackProcessor
Definition: InlineStackProcessor.php:30
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Backend\Form\Element\SelectSingleElement
Definition: SelectSingleElement.php:32
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:22
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldWizard
‪array renderFieldWizard()
Definition: AbstractFormElement.php:108
‪TYPO3\CMS\Backend\Form\Element\SelectSingleElement\render
‪array render()
Definition: SelectSingleElement.php:74