‪TYPO3CMS  ‪main
TcaCategory.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
27 
39 {
43  public function ‪addData(array $result): array
44  {
45  $table = $result['tableName'];
46 
47  foreach ($result['processedTca']['columns'] as $fieldName => $fieldConfig) {
48  // This data provider only works for type=category
49  if (($fieldConfig['config']['type'] ?? '') !== 'category') {
50  continue;
51  }
52 
53  // Make sure we are only processing supported renderTypes
54  if (!$this->‪isTargetRenderType($fieldConfig)) {
55  continue;
56  }
57 
58  $fieldConfig = $this->‪initializeDefaultFieldConfig($fieldConfig);
59  $fieldConfig = $this->‪parseStartingPointsFromSiteConfiguration($result, $fieldConfig);
60  $fieldConfig = $this->‪overrideConfigFromPageTSconfig($result, $table, $fieldName, $fieldConfig);
61 
62  // Prepare the list of currently selected nodes using RelationHandler
63  // This is needed to ensure a correct value initialization before the actual tree is loaded
64  $result['databaseRow'][$fieldName] = $this->‪processDatabaseFieldValue($result['databaseRow'], $fieldName);
65  $result['databaseRow'][$fieldName] = $this->‪processCategoryFieldValue($result, $fieldName);
66 
67  // Since AbstractItemProvider does sometimes access $result[...][config] instead of
68  // our updated $fieldConfig, we have to assign it here and from now on, only work
69  // with the $result[...][config] array.
70  $result['processedTca']['columns'][$fieldName] = $fieldConfig;
71 
72  // This is usually only executed in an ajax request
73  if ($result['selectTreeCompileItems'] ?? false) {
74  // Fetch static items from TCA and TSconfig. Since this is
75  // not supported, throw an exception if something was found.
76  $staticItems = $this->‪sanitizeItemArray($result['processedTca']['columns'][$fieldName]['config']['items'] ?? [], $table, $fieldName);
77  $tsConfigItems = $this->‪addItemsFromPageTsConfig($result, $fieldName, []);
78  if ($staticItems !== [] || $tsConfigItems !== []) {
79  throw new \RuntimeException(
80  'Static items are not supported for field ' . $fieldName . ' from table ' . $table . ' with type category',
81  1627336557
82  );
83  }
84 
85  // Fetch the list of all possible "related" items and apply processing
86  // @todo: This uses 4th param 'true' as hack to add the full item rows speeding up
87  // processing of items in the tree class construct below. Simplify the construct:
88  // The entire $treeDataProvider / $treeRenderer / $tree construct should probably
89  // vanish and the tree processing could happen here in the data provider? Watch
90  // out for the permission event in the tree construct when doing this.
91  $dynamicItems = $this->‪addItemsFromForeignTable($result, $fieldName, [], true);
92  // Remove items as configured via TsConfig
93  $dynamicItems = $this->‪removeItemsByKeepItemsPageTsConfig($result, $fieldName, $dynamicItems);
94  $dynamicItems = $this->‪removeItemsByRemoveItemsPageTsConfig($result, $fieldName, $dynamicItems);
95  // Finally, the only data needed for the tree code are the valid uids of the possible records
96  $uidListOfAllDynamicItems = array_map(intval(...), array_filter(
97  array_values(array_column($dynamicItems, 'value')),
98  static fn(‪$uid) => (int)‪$uid > 0
99  ));
100  $fullRowsOfDynamicItems = [];
101  foreach ($dynamicItems as $item) {
102  // @todo: Prepare performance hack for tree calculation below.
103  if (isset($item['_row'])) {
104  $fullRowsOfDynamicItems[(int)$item['_row']['uid']] = $item['_row'];
105  }
106  }
107  // Initialize the tree data provider
109  $result['processedTca']['columns'][$fieldName]['config'],
110  $table,
111  $fieldName,
112  $result['databaseRow']
113  );
114  $treeDataProvider->setSelectedList(implode(',', $result['databaseRow'][$fieldName]));
115  // Basically the tree data provider fetches all tree nodes again and
116  // then verifies if a given rows' uid is within the item whitelist.
117  // @todo: Simplify construct, probably remove entirely. See @todo above as well.
118  $treeDataProvider->setAvailableItems($fullRowsOfDynamicItems);
119  $treeDataProvider->setItemWhiteList($uidListOfAllDynamicItems);
120  $treeDataProvider->initializeTreeData();
121  $treeRenderer = GeneralUtility::makeInstance(ArrayTreeRenderer::class);
122  $tree = GeneralUtility::makeInstance(TableConfigurationTree::class);
123  $tree->setDataProvider($treeDataProvider);
124  $tree->setNodeRenderer($treeRenderer);
125 
126  // Add the calculated tree nodes
127  $result['processedTca']['columns'][$fieldName]['config']['items'] = $tree->render();
128  }
129  }
130 
131  return $result;
132  }
133 
140  array $result,
141  string $table,
142  string $fieldName,
143  array $fieldConfig
144  ): array {
145  $pageTsConfig = $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['config.']['treeConfig.'] ?? [];
146 
147  if (!is_array($pageTsConfig) || $pageTsConfig === []) {
148  return $fieldConfig;
149  }
150 
151  if (isset($pageTsConfig['startingPoints'])) {
152  $fieldConfig['config']['treeConfig']['startingPoints'] = implode(',', array_unique(‪GeneralUtility::intExplode(',', (string)$pageTsConfig['startingPoints'])));
153  }
154  if (isset($pageTsConfig['appearance.']['expandAll'])) {
155  $fieldConfig['config']['treeConfig']['appearance']['expandAll'] = (bool)$pageTsConfig['appearance.']['expandAll'];
156  }
157  if (isset($pageTsConfig['appearance.']['maxLevels'])) {
158  $fieldConfig['config']['treeConfig']['appearance']['maxLevels'] = (int)$pageTsConfig['appearance.']['maxLevels'];
159  }
160  if (isset($pageTsConfig['appearance.']['nonSelectableLevels'])) {
161  $fieldConfig['config']['treeConfig']['appearance']['nonSelectableLevels'] = $pageTsConfig['appearance.']['nonSelectableLevels'];
162  }
163 
164  return $fieldConfig;
165  }
166 
170  protected function ‪processCategoryFieldValue(array $result, string $fieldName): array
171  {
172  $fieldConfig = $result['processedTca']['columns'][$fieldName];
173  $relationHandler = GeneralUtility::makeInstance(RelationHandler::class);
174 
175  $newDatabaseValueArray = [];
176  $currentDatabaseValueArray = array_key_exists($fieldName, $result['databaseRow']) ? $result['databaseRow'][$fieldName] : [];
177 
178  if (!empty($fieldConfig['config']['MM']) && $result['command'] !== 'new') {
179  $relationHandler->start(
180  implode(',', $currentDatabaseValueArray),
181  $fieldConfig['config']['foreign_table'],
182  $fieldConfig['config']['MM'],
183  $result['databaseRow']['uid'],
184  $result['tableName'],
185  $fieldConfig['config']
186  );
187  $newDatabaseValueArray = array_merge($newDatabaseValueArray, $relationHandler->getValueArray());
188  } else {
189  // If not dealing with MM relations, use default live uid, not versioned uid for record relations
190  $relationHandler->start(
191  implode(',', $currentDatabaseValueArray),
192  $fieldConfig['config']['foreign_table'],
193  '',
194  $this->‪getLiveUid($result),
195  $result['tableName'],
196  $fieldConfig['config']
197  );
198  $databaseIds = array_merge($newDatabaseValueArray, $relationHandler->getValueArray());
199  // remove all items from the current DB values if not available as relation
200  $newDatabaseValueArray = array_values(array_intersect($currentDatabaseValueArray, $databaseIds));
201  }
202 
203  // Since only uids are allowed, the array must be unique
204  return array_unique($newDatabaseValueArray);
205  }
206 
207  protected function ‪isTargetRenderType($fieldConfig): bool
208  {
209  // Type category does not support any renderType
210  return !isset($fieldConfig['config']['renderType']);
211  }
212 
213  protected function ‪initializeDefaultFieldConfig(array $fieldConfig): array
214  {
215  $fieldConfig = array_replace_recursive([
216  'config' => [
217  'treeConfig' => [
218  'parentField' => 'parent',
219  'appearance' => [
220  'expandAll' => true,
221  'showHeader' => true,
222  'maxLevels' => 99,
223  ],
224  ],
225  ],
226  ], $fieldConfig);
227 
228  // Calculate maxitems value, while 0 will fall back to 99999
229  $fieldConfig['config']['maxitems'] = ‪MathUtility::forceIntegerInRange(
230  $fieldConfig['config']['maxitems'] ?? 0,
231  0,
232  99999
233  ) ?: 99999;
234 
235  return $fieldConfig;
236  }
237 }
‪TYPO3\CMS\Backend\Form\FormDataProvider\AbstractItemProvider\sanitizeItemArray
‪array sanitizeItemArray($itemArray, $tableName, $fieldName)
Definition: AbstractItemProvider.php:1104
‪TYPO3\CMS\Backend\Form\FormDataProvider\AbstractItemProvider\addItemsFromPageTsConfig
‪array addItemsFromPageTsConfig(array $result, $fieldName, array $items)
Definition: AbstractItemProvider.php:147
‪TYPO3\CMS\Backend\Form\FormDataProvider\TcaCategory\processCategoryFieldValue
‪processCategoryFieldValue(array $result, string $fieldName)
Definition: TcaCategory.php:170
‪TYPO3\CMS\Core\Database\RelationHandler
Definition: RelationHandler.php:36
‪TYPO3\CMS\Core\Tree\TableConfiguration\TableConfigurationTree
Definition: TableConfigurationTree.php:24
‪TYPO3\CMS\Backend\Form\FormDataProvider\AbstractItemProvider\addItemsFromForeignTable
‪array addItemsFromForeignTable(array $result, $fieldName, array $items, bool $includeFullRows=false)
Definition: AbstractItemProvider.php:266
‪TYPO3\CMS\Core\Tree\TableConfiguration\TreeDataProviderFactory\getDataProvider
‪static DatabaseTreeDataProvider getDataProvider(array $tcaConfiguration, $table, $field, $currentValue)
Definition: TreeDataProviderFactory.php:36
‪TYPO3\CMS\Backend\Form\FormDataProvider\TcaCategory\initializeDefaultFieldConfig
‪initializeDefaultFieldConfig(array $fieldConfig)
Definition: TcaCategory.php:213
‪TYPO3\CMS\Backend\Form\FormDataProvider\AbstractItemProvider\parseStartingPointsFromSiteConfiguration
‪parseStartingPointsFromSiteConfiguration(array $result, array $fieldConfig)
Definition: AbstractItemProvider.php:919
‪TYPO3\CMS\Backend\Form\FormDataProvider\AbstractItemProvider\getLiveUid
‪int string getLiveUid(array $result)
Definition: AbstractItemProvider.php:1129
‪TYPO3\CMS\Backend\Form\FormDataProvider\TcaCategory\overrideConfigFromPageTSconfig
‪overrideConfigFromPageTSconfig(array $result, string $table, string $fieldName, array $fieldConfig)
Definition: TcaCategory.php:139
‪TYPO3\CMS\Backend\Form\FormDataProvider
Definition: AbstractDatabaseRecordProvider.php:16
‪TYPO3\CMS\Backend\Form\FormDataProviderInterface
Definition: FormDataProviderInterface.php:23
‪TYPO3\CMS\Backend\Form\FormDataProvider\AbstractItemProvider\removeItemsByKeepItemsPageTsConfig
‪array removeItemsByKeepItemsPageTsConfig(array $result, $fieldName, array $items)
Definition: AbstractItemProvider.php:381
‪TYPO3\CMS\Backend\Form\FormDataProvider\AbstractItemProvider\processDatabaseFieldValue
‪array processDatabaseFieldValue(array $row, $fieldName)
Definition: AbstractItemProvider.php:953
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪TYPO3\CMS\Backend\Form\FormDataProvider\TcaCategory
Definition: TcaCategory.php:39
‪TYPO3\CMS\Backend\Form\FormDataProvider\TcaCategory\addData
‪addData(array $result)
Definition: TcaCategory.php:43
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Core\Tree\TableConfiguration\ArrayTreeRenderer
Definition: ArrayTreeRenderer.php:27
‪TYPO3\CMS\Core\Tree\TableConfiguration\TreeDataProviderFactory
Definition: TreeDataProviderFactory.php:26
‪TYPO3\CMS\Core\Utility\MathUtility\forceIntegerInRange
‪static int forceIntegerInRange(mixed $theInt, int $min, int $max=2000000000, int $defaultValue=0)
Definition: MathUtility.php:34
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Backend\Form\FormDataProvider\TcaCategory\isTargetRenderType
‪isTargetRenderType($fieldConfig)
Definition: TcaCategory.php:207
‪TYPO3\CMS\Core\Utility\GeneralUtility\intExplode
‪static list< int > intExplode(string $delimiter, string $string, bool $removeEmptyValues=false)
Definition: GeneralUtility.php:756
‪TYPO3\CMS\Backend\Form\FormDataProvider\AbstractItemProvider
Definition: AbstractItemProvider.php:50
‪TYPO3\CMS\Backend\Form\FormDataProvider\AbstractItemProvider\removeItemsByRemoveItemsPageTsConfig
‪array removeItemsByRemoveItemsPageTsConfig(array $result, $fieldName, array $items)
Definition: AbstractItemProvider.php:414