[BUGFIX] Fix several typos in php comments
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Tree / TableConfiguration / DatabaseTreeDataProvider.php
1 <?php
2 namespace TYPO3\CMS\Core\Tree\TableConfiguration;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Backend\Utility\BackendUtility;
18 use TYPO3\CMS\Core\Database\ConnectionPool;
19 use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
20 use TYPO3\CMS\Core\Imaging\Icon;
21 use TYPO3\CMS\Core\Imaging\IconFactory;
22 use TYPO3\CMS\Core\Localization\LanguageService;
23 use TYPO3\CMS\Core\Utility\GeneralUtility;
24 use TYPO3\CMS\Extbase\Object\ObjectManager;
25 use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
26
27 /**
28 * TCA tree data provider
29 */
30 class DatabaseTreeDataProvider extends AbstractTableConfigurationTreeDataProvider
31 {
32 const SIGNAL_PostProcessTreeData = 'PostProcessTreeData';
33 const MODE_CHILDREN = 1;
34 const MODE_PARENT = 2;
35
36 /**
37 * @var string
38 */
39 protected $tableName = '';
40
41 /**
42 * @var string
43 */
44 protected $treeId = '';
45
46 /**
47 * @var string
48 */
49 protected $labelField = '';
50
51 /**
52 * @var string
53 */
54 protected $tableWhere = '';
55
56 /**
57 * @var int
58 */
59 protected $lookupMode = self::MODE_CHILDREN;
60
61 /**
62 * @var string
63 */
64 protected $lookupField = '';
65
66 /**
67 * @var int
68 */
69 protected $rootUid = 0;
70
71 /**
72 * @var array
73 */
74 protected $idCache = [];
75
76 /**
77 * Stores TCA-Configuration of the LookUpField in tableName
78 *
79 * @var array
80 */
81 protected $columnConfiguration;
82
83 /**
84 * node sort values (the orderings from foreign_Table_where evaluation)
85 *
86 * @var array
87 */
88 protected $nodeSortValues = [];
89
90 /**
91 * @var array TCEforms compiled TSConfig array
92 */
93 protected $generatedTSConfig = [];
94
95 /**
96 * @var Dispatcher
97 */
98 protected $signalSlotDispatcher;
99
100 /**
101 * Sets the label field
102 *
103 * @param string $labelField
104 */
105 public function setLabelField($labelField)
106 {
107 $this->labelField = $labelField;
108 }
109
110 /**
111 * Gets the label field
112 *
113 * @return string
114 */
115 public function getLabelField()
116 {
117 return $this->labelField;
118 }
119
120 /**
121 * Sets the table name
122 *
123 * @param string $tableName
124 */
125 public function setTableName($tableName)
126 {
127 $this->tableName = $tableName;
128 }
129
130 /**
131 * Gets the table name
132 *
133 * @return string
134 */
135 public function getTableName()
136 {
137 return $this->tableName;
138 }
139
140 /**
141 * Sets the lookup field
142 *
143 * @param string $lookupField
144 */
145 public function setLookupField($lookupField)
146 {
147 $this->lookupField = $lookupField;
148 }
149
150 /**
151 * Gets the lookup field
152 *
153 * @return string
154 */
155 public function getLookupField()
156 {
157 return $this->lookupField;
158 }
159
160 /**
161 * Sets the lookup mode
162 *
163 * @param int $lookupMode
164 */
165 public function setLookupMode($lookupMode)
166 {
167 $this->lookupMode = $lookupMode;
168 }
169
170 /**
171 * Gets the lookup mode
172 *
173 * @return int
174 */
175 public function getLookupMode()
176 {
177 return $this->lookupMode;
178 }
179
180 /**
181 * Gets the nodes
182 *
183 * @param \TYPO3\CMS\Backend\Tree\TreeNode $node
184 */
185 public function getNodes(\TYPO3\CMS\Backend\Tree\TreeNode $node)
186 {
187 }
188
189 /**
190 * Gets the root node
191 *
192 * @return \TYPO3\CMS\Core\Tree\TableConfiguration\DatabaseTreeNode
193 */
194 public function getRoot()
195 {
196 return $this->buildRepresentationForNode($this->treeData);
197 }
198
199 /**
200 * Sets the root uid
201 *
202 * @param int $rootUid
203 */
204 public function setRootUid($rootUid)
205 {
206 $this->rootUid = $rootUid;
207 }
208
209 /**
210 * Gets the root uid
211 *
212 * @return int
213 */
214 public function getRootUid()
215 {
216 return $this->rootUid;
217 }
218
219 /**
220 * Sets the tableWhere clause
221 *
222 * @param string $tableWhere
223 */
224 public function setTableWhere($tableWhere)
225 {
226 $this->tableWhere = $tableWhere;
227 }
228
229 /**
230 * Gets the tableWhere clause
231 *
232 * @return string
233 */
234 public function getTableWhere()
235 {
236 return $this->tableWhere;
237 }
238
239 /**
240 * Builds a complete node including childs
241 *
242 * @param \TYPO3\CMS\Backend\Tree\TreeNode $basicNode
243 * @param \TYPO3\CMS\Core\Tree\TableConfiguration\DatabaseTreeNode|null $parent
244 * @param int $level
245 * @return \TYPO3\CMS\Core\Tree\TableConfiguration\DatabaseTreeNode Node object
246 */
247 protected function buildRepresentationForNode(\TYPO3\CMS\Backend\Tree\TreeNode $basicNode, DatabaseTreeNode $parent = null, $level = 0)
248 {
249 /** @var \TYPO3\CMS\Core\Tree\TableConfiguration\DatabaseTreeNode $node */
250 $node = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Tree\TableConfiguration\DatabaseTreeNode::class);
251 $row = [];
252 if ($basicNode->getId() == 0) {
253 $node->setSelected(false);
254 $node->setExpanded(true);
255 $node->setLabel($this->getLanguageService()->sL($GLOBALS['TCA'][$this->tableName]['ctrl']['title']));
256 } else {
257 $row = BackendUtility::getRecordWSOL($this->tableName, $basicNode->getId(), '*', '', false);
258 $node->setLabel(BackendUtility::getRecordTitle($this->tableName, $row) ?: $basicNode->getId());
259 $node->setSelected(GeneralUtility::inList($this->getSelectedList(), $basicNode->getId()));
260 $node->setExpanded($this->isExpanded($basicNode));
261 }
262 $node->setId($basicNode->getId());
263 $node->setSelectable(!GeneralUtility::inList($this->getNonSelectableLevelList(), $level) && !in_array($basicNode->getId(), $this->getItemUnselectableList()));
264 $node->setSortValue($this->nodeSortValues[$basicNode->getId()]);
265 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
266 $node->setIcon($iconFactory->getIconForRecord($this->tableName, $row, Icon::SIZE_SMALL));
267 $node->setParentNode($parent);
268 if ($basicNode->hasChildNodes()) {
269 $node->setHasChildren(true);
270 /** @var \TYPO3\CMS\Backend\Tree\SortedTreeNodeCollection $childNodes */
271 $childNodes = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Tree\SortedTreeNodeCollection::class);
272 $tempNodes = [];
273 foreach ($basicNode->getChildNodes() as $child) {
274 $tempNodes[] = $this->buildRepresentationForNode($child, $node, $level + 1);
275 }
276 $childNodes->exchangeArray($tempNodes);
277 $childNodes->asort();
278 $node->setChildNodes($childNodes);
279 }
280 return $node;
281 }
282
283 /**
284 * Init the tree data
285 */
286 public function initializeTreeData()
287 {
288 parent::initializeTreeData();
289 $this->nodeSortValues = array_flip($this->itemWhiteList);
290 $this->columnConfiguration = $GLOBALS['TCA'][$this->getTableName()]['columns'][$this->getLookupField()]['config'];
291 if (isset($this->columnConfiguration['foreign_table']) && $this->columnConfiguration['foreign_table'] != $this->getTableName()) {
292 throw new \InvalidArgumentException('TCA Tree configuration is invalid: tree for different node-Tables is not implemented yet', 1290944650);
293 }
294 $this->treeData = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Tree\TreeNode::class);
295 $this->loadTreeData();
296 $this->emitPostProcessTreeDataSignal();
297 }
298
299 /**
300 * Loads the tree data (all possible children)
301 */
302 protected function loadTreeData()
303 {
304 $this->treeData->setId($this->getRootUid());
305 $this->treeData->setParentNode(null);
306 if ($this->levelMaximum >= 1) {
307 $childNodes = $this->getChildrenOf($this->treeData, 1);
308 if ($childNodes !== null) {
309 $this->treeData->setChildNodes($childNodes);
310 }
311 }
312 }
313
314 /**
315 * Gets node children
316 *
317 * @param \TYPO3\CMS\Backend\Tree\TreeNode $node
318 * @param int $level
319 * @return \TYPO3\CMS\Backend\Tree\TreeNodeCollection|null
320 */
321 protected function getChildrenOf(\TYPO3\CMS\Backend\Tree\TreeNode $node, $level)
322 {
323 $nodeData = null;
324 if ($node->getId() !== 0) {
325 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
326 ->getQueryBuilderForTable($this->getTableName());
327 $queryBuilder->getRestrictions()->removeAll();
328 $nodeData = $queryBuilder->select('*')
329 ->from($this->getTableName())
330 ->where(
331 $queryBuilder->expr()->eq(
332 'uid',
333 $queryBuilder->createNamedParameter($node->getId(), \PDO::PARAM_INT)
334 )
335 )
336 ->setMaxResults(1)
337 ->execute()
338 ->fetch();
339 }
340 if (empty($nodeData)) {
341 $nodeData = [
342 'uid' => 0,
343 $this->getLookupField() => ''
344 ];
345 }
346 $storage = null;
347 $children = $this->getRelatedRecords($nodeData);
348 if (!empty($children)) {
349 /** @var \TYPO3\CMS\Backend\Tree\TreeNodeCollection $storage */
350 $storage = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Tree\TreeNodeCollection::class);
351 foreach ($children as $child) {
352 $node = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Tree\TreeNode::class);
353 $node->setId($child);
354 if ($level < $this->levelMaximum) {
355 $children = $this->getChildrenOf($node, $level + 1);
356 if ($children !== null) {
357 $node->setChildNodes($children);
358 }
359 }
360 $storage->append($node);
361 }
362 }
363 return $storage;
364 }
365
366 /**
367 * Gets related records depending on TCA configuration
368 *
369 * @param array $row
370 * @return array
371 */
372 protected function getRelatedRecords(array $row)
373 {
374 if ($this->getLookupMode() == self::MODE_PARENT) {
375 $children = $this->getChildrenUidsFromParentRelation($row);
376 } else {
377 $children = $this->getChildrenUidsFromChildrenRelation($row);
378 }
379 $allowedArray = [];
380 foreach ($children as $child) {
381 if (!in_array($child, $this->idCache) && in_array($child, $this->itemWhiteList)) {
382 $allowedArray[] = $child;
383 }
384 }
385 $this->idCache = array_merge($this->idCache, $allowedArray);
386 return $allowedArray;
387 }
388
389 /**
390 * Gets related records depending on TCA configuration
391 *
392 * @param array $row
393 * @return array
394 */
395 protected function getChildrenUidsFromParentRelation(array $row)
396 {
397 $uid = $row['uid'];
398 switch ((string)$this->columnConfiguration['type']) {
399 case 'inline':
400
401 case 'select':
402 if ($this->columnConfiguration['MM']) {
403 /** @var \TYPO3\CMS\Core\Database\RelationHandler $dbGroup */
404 $dbGroup = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\RelationHandler::class);
405 // Dummy field for setting "look from other site"
406 $this->columnConfiguration['MM_oppositeField'] = 'children';
407 $dbGroup->start($row[$this->getLookupField()], $this->getTableName(), $this->columnConfiguration['MM'], $uid, $this->getTableName(), $this->columnConfiguration);
408 $relatedUids = $dbGroup->tableArray[$this->getTableName()];
409 } elseif ($this->columnConfiguration['foreign_field']) {
410 $relatedUids = $this->listFieldQuery($this->columnConfiguration['foreign_field'], $uid);
411 } else {
412 $relatedUids = $this->listFieldQuery($this->getLookupField(), $uid);
413 }
414 break;
415 default:
416 $relatedUids = $this->listFieldQuery($this->getLookupField(), $uid);
417 }
418 return $relatedUids;
419 }
420
421 /**
422 * Gets related children records depending on TCA configuration
423 *
424 * @param array $row
425 * @return array
426 */
427 protected function getChildrenUidsFromChildrenRelation(array $row)
428 {
429 $relatedUids = [];
430 $uid = $row['uid'];
431 $value = $row[$this->getLookupField()];
432 switch ((string)$this->columnConfiguration['type']) {
433 case 'inline':
434 // Intentional fall-through
435 case 'select':
436 if ($this->columnConfiguration['MM']) {
437 $dbGroup = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\RelationHandler::class);
438 $dbGroup->start(
439 $value,
440 $this->getTableName(),
441 $this->columnConfiguration['MM'],
442 $uid,
443 $this->getTableName(),
444 $this->columnConfiguration
445 );
446 $relatedUids = $dbGroup->tableArray[$this->getTableName()];
447 } elseif ($this->columnConfiguration['foreign_field']) {
448 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
449 ->getQueryBuilderForTable($this->getTableName());
450 $queryBuilder->getRestrictions()->removeAll();
451 $records = $queryBuilder->select('uid')
452 ->from($this->getTableName())
453 ->where(
454 $queryBuilder->expr()->eq(
455 $this->columnConfiguration['foreign_field'],
456 $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
457 )
458 )
459 ->execute()
460 ->fetchAll();
461
462 if (!empty($records)) {
463 $relatedUids = array_column($records, 'uid');
464 }
465 } else {
466 $relatedUids = GeneralUtility::intExplode(',', $value, true);
467 }
468 break;
469 default:
470 $relatedUids = GeneralUtility::intExplode(',', $value, true);
471 }
472 return $relatedUids;
473 }
474
475 /**
476 * Queries the table for a field which might contain a list.
477 *
478 * @param string $fieldName the name of the field to be queried
479 * @param int $queryId the uid to search for
480 * @return int[] all uids found
481 */
482 protected function listFieldQuery($fieldName, $queryId)
483 {
484 $queryId = (int)$queryId;
485 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
486 ->getQueryBuilderForTable($this->getTableName());
487 $queryBuilder->getRestrictions()->removeAll();
488
489 $queryBuilder->select('uid')
490 ->from($this->getTableName())
491 ->where($queryBuilder->expr()->inSet($fieldName, $queryBuilder->quote($queryId)));
492
493 if ($queryId === 0) {
494 $queryBuilder->orWhere(
495 $queryBuilder->expr()->comparison(
496 'CAST(' . $queryBuilder->quoteIdentifier($fieldName) . ' AS CHAR)',
497 ExpressionBuilder::EQ,
498 $queryBuilder->quote('')
499 )
500 );
501 }
502
503 $records = $queryBuilder->execute()->fetchAll();
504 $uidArray = is_array($records) ? array_column($records, 'uid') : [];
505
506 return $uidArray;
507 }
508
509 /**
510 * Emits the post processing tree data signal.
511 */
512 protected function emitPostProcessTreeDataSignal()
513 {
514 $this->getSignalSlotDispatcher()->dispatch(
515 self::class,
516 self::SIGNAL_PostProcessTreeData,
517 [$this, $this->treeData]
518 );
519 }
520
521 /**
522 * Get the SignalSlot dispatcher
523 *
524 * @return Dispatcher
525 */
526 protected function getSignalSlotDispatcher()
527 {
528 if (!isset($this->signalSlotDispatcher)) {
529 $this->signalSlotDispatcher = $this->getObjectManager()->get(Dispatcher::class);
530 }
531 return $this->signalSlotDispatcher;
532 }
533
534 /**
535 * Get the ObjectManager
536 *
537 * @return ObjectManager
538 */
539 protected function getObjectManager()
540 {
541 return GeneralUtility::makeInstance(ObjectManager::class);
542 }
543
544 /**
545 * @return LanguageService|null
546 */
547 protected function getLanguageService(): ?LanguageService
548 {
549 return $GLOBALS['LANG'] ?? null;
550 }
551 }