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