‪TYPO3CMS  9.5
ReferenceIndex.php
Go to the documentation of this file.
1 <?php
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 Doctrine\DBAL\DBALException;
18 use Psr\Log\LoggerAwareInterface;
19 use Psr\Log\LoggerAwareTrait;
35 
49 class ‪ReferenceIndex implements LoggerAwareInterface
50 {
51  use LoggerAwareTrait;
52 
66  protected static ‪$excludedTables = [
67  'sys_log' => true,
68  'sys_history' => true,
69  'tx_extensionmanager_domain_model_extension' => true
70  ];
71 
82  protected static ‪$excludedColumns = [
83  'uid' => true,
84  'perms_userid' => true,
85  'perms_groupid' => true,
86  'perms_user' => true,
87  'perms_group' => true,
88  'perms_everybody' => true,
89  'pid' => true
90  ];
91 
98  protected static ‪$cachePrefixTableRelationFields = 'core-refidx-tblRelFields-';
99 
106  public ‪$temp_flexRelations = [];
107 
115  public ‪$WSOL = false;
116 
123  public ‪$relations = [];
124 
131  protected ‪$recordCache = [];
132 
139  public ‪$hashVersion = 1;
140 
146  protected ‪$workspaceId = 0;
147 
153  protected ‪$runtimeCache;
154 
159  protected ‪$useRuntimeCache = false;
160 
164  public function ‪__construct()
165  {
166  $this->runtimeCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_runtime');
167  }
168 
176  {
177  $this->workspaceId = (int)‪$workspaceId;
178  }
179 
186  public function ‪getWorkspaceId()
187  {
188  return ‪$this->workspaceId;
189  }
190 
203  public function ‪updateRefIndexTable($tableName, $uid, $testOnly = false)
204  {
205  // First, secure that the index table is not updated with workspace tainted relations:
206  $this->WSOL = false;
207 
208  // Init:
209  $result = [
210  'keptNodes' => 0,
211  'deletedNodes' => 0,
212  'addedNodes' => 0
213  ];
214 
215  $uid = $uid ? (int)$uid : 0;
216  if (!$uid) {
217  return $result;
218  }
219 
220  // If this table cannot contain relations, skip it
221  if ($this->‪shouldExcludeTableFromReferenceIndex($tableName)) {
222  return $result;
223  }
224 
225  // Fetch tableRelationFields and save them in cache if not there yet
226  $cacheId = static::$cachePrefixTableRelationFields . $tableName;
227  if (!$this->useRuntimeCache || !$this->runtimeCache->has($cacheId)) {
228  $tableRelationFields = $this->‪fetchTableRelationFields($tableName);
229  $this->runtimeCache->set($cacheId, $tableRelationFields);
230  } else {
231  $tableRelationFields = $this->runtimeCache->get($cacheId);
232  }
233 
234  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
235  $connection = $connectionPool->getConnectionForTable('sys_refindex');
236 
237  // Get current index from Database with hash as index using $uidIndexField
238  // no restrictions are needed, since sys_refindex is not a TCA table
239  $queryBuilder = $connection->createQueryBuilder();
240  $queryBuilder->getRestrictions()->removeAll();
241  $queryResult = $queryBuilder->select('hash')->from('sys_refindex')->where(
242  $queryBuilder->expr()->eq('tablename', $queryBuilder->createNamedParameter($tableName, \PDO::PARAM_STR)),
243  $queryBuilder->expr()->eq('recuid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)),
244  $queryBuilder->expr()->eq(
245  'workspace',
246  $queryBuilder->createNamedParameter($this->getWorkspaceId(), \PDO::PARAM_INT)
247  )
248  )->execute();
249  $currentRelationHashes = [];
250  while ($relation = $queryResult->fetch()) {
251  $currentRelationHashes[$relation['hash']] = true;
252  }
253 
254  // If the table has fields which could contain relations and the record does exist (including deleted-flagged)
255  if ($tableRelationFields !== '') {
256  $existingRecord = $this->‪getRecordRawCached($tableName, $uid);
257  if ($existingRecord) {
258  // Table has relation fields and record exists - get relations
259  $this->relations = [];
260  ‪$relations = $this->‪generateDataUsingRecord($tableName, $existingRecord);
261  if (!is_array(‪$relations)) {
262  return $result;
263  }
264  // Traverse the generated index:
265  foreach (‪$relations as &$relation) {
266  if (!is_array($relation)) {
267  continue;
268  }
269  $relation['hash'] = md5(implode('
270  // First, check if already indexed and if so, unset that row (so in the end we know which rows to remove!)
271  if (isset($currentRelationHashes[$relation['hash']])) {
272  unset($currentRelationHashes[$relation['hash']]);
273  $result['keptNodes']++;
274  $relation['_ACTION'] = 'KEPT';
275  } else {
276  // If new, add it:
277  if (!$testOnly) {
278  $connection->insert('sys_refindex', $relation);
279  }
280  $result['addedNodes']++;
281  $relation['_ACTION'] = 'ADDED';
282  }
283  }
284  $result['relations'] = $relations;
285  }
286  }
287 
288  // If any old are left, remove them:
289  if (!empty($currentRelationHashes)) {
290  $hashList = array_keys($currentRelationHashes);
291  if (!empty($hashList)) {
292  $result['deletedNodes'] = count($hashList);
293  $result['deletedNodes_hashList'] = implode(',', $hashList);
294  if (!$testOnly) {
295  $maxBindParameters = PlatformInformation::getMaxBindParameters($connection->getDatabasePlatform());
296  foreach (array_chunk($hashList, $maxBindParameters - 10, true) as $chunk) {
297  if (empty($chunk)) {
298  continue;
299  }
300  $queryBuilder = $connection->createQueryBuilder();
301  $queryBuilder
302  ->delete('sys_refindex')
303  ->where(
304  $queryBuilder->expr()->in(
305  'hash',
306  $queryBuilder->createNamedParameter($chunk, Connection::PARAM_STR_ARRAY)
307  )
308  )
309  ->execute();
310  }
311  }
312  }
313  }
314 
315  return $result;
316  }
317 
326  public function generateRefIndexData($tableName, $uid)
327  {
328  if (!isset($GLOBALS['TCA'][$tableName])) {
329  return null;
330  }
331 
332  $this->relations = [];
333 
334  $record = null;
335  $uid = $uid ? (int)$uid : 0;
336  if ($uid) {
337  // Get raw record from DB
338  $record = $this->getRecordRawCached($tableName, $uid);
339  }
340 
341  if (!is_array($record)) {
342  return null;
343  }
344 
345  return $this->generateDataUsingRecord($tableName, $record);
346  }
347 
355  public function getNumberOfReferencedRecords(string $tableName, int $uid): int
356  {
357  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
358  return (int)$queryBuilder
359  ->count('*')->from('sys_refindex')
360  ->where(
361  $queryBuilder->expr()->eq(
362  'ref_table',
363  $queryBuilder->createNamedParameter($tableName, \PDO::PARAM_STR)
364  ),
365  $queryBuilder->expr()->eq(
366  'ref_uid',
367  $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
368  ),
369  $queryBuilder->expr()->eq(
370  'deleted',
371  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
372  )
373  )->execute()->fetchColumn(0);
374  }
375 
383  protected function generateDataUsingRecord(string $tableName, array $record): array
384  {
385  $this->relations = [];
386  $deleteField = $GLOBALS['TCA'][$tableName]['ctrl']['delete'];
387 
388  // Is the record deleted?
389  $deleted = $deleteField && $record[$deleteField] ? 1 : 0;
390 
391  // Get all relations from record:
392  $recordRelations = $this->getRelations($tableName, $record);
393  // Traverse those relations, compile records to insert in table:
394  foreach ($recordRelations as $fieldName => $fieldRelations) {
395  // Based on type
396  switch ((string)$fieldRelations['type']) {
397  case 'db':
398  $this->createEntryDataForDatabaseRelationsUsingRecord($tableName, $record, $fieldName, '', $deleted, $fieldRelations['itemArray']);
399  break;
400  case 'file_reference':
401  // not used (see getRelations()), but fallback to file
402  case 'file':
403  $this->createEntryDataForFileRelationsUsingRecord($tableName, $record, $fieldName, '', $deleted, $fieldRelations['newValueFiles']);
404  break;
405  case 'flex':
406  // DB references in FlexForms
407  if (is_array($fieldRelations['flexFormRels']['db'])) {
408  foreach ($fieldRelations['flexFormRels']['db'] as $flexPointer => $subList) {
409  $this->createEntryDataForDatabaseRelationsUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, $subList);
410  }
411  }
412  // File references in FlexForms
413  // @todo #65463 Test correct handling of file references in FlexForms
414  if (is_array($fieldRelations['flexFormRels']['file'])) {
415  foreach ($fieldRelations['flexFormRels']['file'] as $flexPointer => $subList) {
416  $this->createEntryDataForFileRelationsUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, $subList);
417  }
418  }
419  // Soft references in FlexForms
420  // @todo #65464 Test correct handling of soft references in FlexForms
421  if (is_array($fieldRelations['flexFormRels']['softrefs'])) {
422  foreach ($fieldRelations['flexFormRels']['softrefs'] as $flexPointer => $subList) {
423  $this->createEntryDataForSoftReferencesUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, $subList['keys']);
424  }
425  }
426  break;
427  }
428  // Soft references in the field
429  if (is_array($fieldRelations['softrefs'])) {
430  $this->createEntryDataForSoftReferencesUsingRecord($tableName, $record, $fieldName, '', $deleted, $fieldRelations['softrefs']['keys']);
431  }
432  }
433 
434  return array_filter($this->relations);
435  }
436 
454  public function createEntryData($table, $uid, $field, $flexPointer, $deleted, $ref_table, $ref_uid, $ref_string = '', $sort = -1, $softref_key = '', $softref_id = '')
455  {
456  $uid = $uid ? (int)$uid : 0;
457  if (!$uid) {
458  return null;
459  }
460  return $this->createEntryDataUsingRecord(
461  (string)$table,
462  $this->getRecordRawCached($table, $uid),
463  (string)$field,
464  (string)$flexPointer,
465  $deleted ? (int)$deleted : 0,
466  (string)$ref_table,
467  $ref_uid ? (int)$ref_uid : 0,
468  (string)$ref_string,
469  $sort ? (int)$sort : 0,
470  (string)$softref_key,
471  (string)$softref_id
472  );
473  }
474 
491  protected function createEntryDataUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, int $deleted, string $referencedTable, int $referencedUid, string $referenceString = '', int $sort = -1, string $softReferenceKey = '', string $softReferenceId = '')
492  {
493  $workspaceId = 0;
494  if (BackendUtility::isTableWorkspaceEnabled($tableName)) {
495  $workspaceId = $this->getWorkspaceId();
496  if (isset($record['t3ver_wsid']) && (int)$record['t3ver_wsid'] !== $workspaceId) {
497  // The given record is workspace-enabled but doesn't live in the selected workspace => don't add index as it's not actually there
498  return false;
499  }
500  }
501  return [
502  'tablename' => $tableName,
503  'recuid' => $record['uid'],
504  'field' => $fieldName,
505  'flexpointer' => $flexPointer,
506  'softref_key' => $softReferenceKey,
507  'softref_id' => $softReferenceId,
508  'sorting' => $sort,
509  'deleted' => (int)$deleted,
510  'workspace' => ‪$workspaceId,
511  'ref_table' => $referencedTable,
512  'ref_uid' => $referencedUid,
513  'ref_string' => mb_substr($referenceString, 0, 1024)
514  ];
515  }
516 
527  public function ‪createEntryData_dbRels($table, $uid, $fieldName, $flexPointer, $deleted, $items)
528  {
529  $uid = $uid ? (int)$uid : 0;
530  if (!$uid) {
531  return;
532  }
534  (string)$table,
535  $this->‪getRecordRawCached($table, $uid),
536  (string)$fieldName,
537  (string)$flexPointer,
538  $deleted ? (int)$deleted : 0,
539  (array)$items
540  );
541  }
542 
553  protected function ‪createEntryDataForDatabaseRelationsUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, int $deleted, array $items)
554  {
555  foreach ($items as $sort => $i) {
556  $this->relations[] = $this->‪createEntryDataUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, $i['table'], (int)$i['id'], '', $sort);
557  }
558  }
559 
570  public function ‪createEntryData_fileRels($table, $uid, $fieldName, $flexPointer, $deleted, $items)
571  {
572  $uid = $uid ? (int)$uid : 0;
573  if (!$uid) {
574  return;
575  }
577  (string)$table,
578  $this->‪getRecordRawCached($table, $uid),
579  (string)$fieldName,
580  (string)$flexPointer,
581  $deleted ? (int)$deleted : 0,
582  (array)$items
583  );
584  }
585 
596  protected function ‪createEntryDataForFileRelationsUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, int $deleted, array $items)
597  {
598  foreach ($items as $sort => $i) {
599  $filePath = $i['ID_absFile'];
600  if (GeneralUtility::isFirstPartOfStr($filePath, ‪Environment::getPublicPath())) {
601  $filePath = ‪PathUtility::stripPathSitePrefix($filePath);
602  }
603  $this->relations[] = $this->‪createEntryDataUsingRecord(
604  $tableName,
605  $record,
606  $fieldName,
607  $flexPointer,
608  $deleted,
609  '_FILE',
610  0,
611  $filePath,
612  $sort
613  );
614  }
615  }
616 
627  public function ‪createEntryData_softreferences($table, $uid, $fieldName, $flexPointer, $deleted, $keys)
628  {
629  $uid = $uid ? (int)$uid : 0;
630  if (!$uid || !is_array($keys)) {
631  return;
632  }
634  (string)$table,
635  $this->‪getRecordRawCached($table, $uid),
636  (string)$fieldName,
637  (string)$flexPointer,
638  $deleted ? (int)$deleted : 0,
639  (array)$keys
640  );
641  }
642 
653  protected function ‪createEntryDataForSoftReferencesUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, int $deleted, array $keys)
654  {
655  foreach ($keys as $spKey => $elements) {
656  if (is_array($elements)) {
657  foreach ($elements as $subKey => $el) {
658  if (is_array($el['subst'])) {
659  switch ((string)$el['subst']['type']) {
660  case 'db':
661  list($referencedTable, $referencedUid) = explode(':', $el['subst']['recordRef']);
662  $this->relations[] = $this->‪createEntryDataUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, $referencedTable, (int)$referencedUid, '', -1, $spKey, $subKey);
663  break;
664  case 'file_reference':
665  // not used (see getRelations()), but fallback to file
666  case 'file':
667  $this->relations[] = $this->‪createEntryDataUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, '_FILE', 0, $el['subst']['relFileName'], -1, $spKey, $subKey);
668  break;
669  case 'string':
670  $this->relations[] = $this->‪createEntryDataUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, '_STRING', 0, $el['subst']['tokenValue'], -1, $spKey, $subKey);
671  break;
672  }
673  }
674  }
675  }
676  }
677  }
678 
679  /*******************************
680  *
681  * Get relations from table row
682  *
683  *******************************/
684 
696  public function ‪getRelations($table, $row, $onlyField = '')
697  {
698  // Initialize:
699  $uid = $row['uid'];
700  $outRow = [];
701  foreach ($row as $field => $value) {
702  if ($this->‪shouldExcludeTableColumnFromReferenceIndex($table, $field, $onlyField) === false) {
703  $conf = ‪$GLOBALS['TCA'][$table]['columns'][$field]['config'];
704  // Add files
705  $resultsFromFiles = $this->‪getRelations_procFiles($value, $conf, $uid);
706  if (!empty($resultsFromFiles)) {
707  // We have to fill different arrays here depending on the result.
708  // internal_type file is still a relation of type file and
709  // since http://forge.typo3.org/issues/49538 internal_type file_reference
710  // is a database relation to a sys_file record
711  $fileResultsFromFiles = [];
712  $dbResultsFromFiles = [];
713  foreach ($resultsFromFiles as $resultFromFiles) {
714  if (isset($resultFromFiles['table']) && $resultFromFiles['table'] === 'sys_file') {
715  $dbResultsFromFiles[] = $resultFromFiles;
716  } else {
717  // Creates an entry for the field with all the files:
718  $fileResultsFromFiles[] = $resultFromFiles;
719  }
720  }
721  if (!empty($fileResultsFromFiles)) {
722  $outRow[$field] = [
723  'type' => 'file',
724  'newValueFiles' => $fileResultsFromFiles
725  ];
726  }
727  if (!empty($dbResultsFromFiles)) {
728  $outRow[$field] = [
729  'type' => 'db',
730  'itemArray' => $dbResultsFromFiles
731  ];
732  }
733  }
734  // Add a softref definition for link fields if the TCA does not specify one already
735  if ($conf['type'] === 'input' && $conf['renderType'] === 'inputLink' && empty($conf['softref'])) {
736  $conf['softref'] = 'typolink';
737  }
738  // Add DB:
739  $resultsFromDatabase = $this->‪getRelations_procDB($value, $conf, $uid, $table);
740  if (!empty($resultsFromDatabase)) {
741  // Create an entry for the field with all DB relations:
742  $outRow[$field] = [
743  'type' => 'db',
744  'itemArray' => $resultsFromDatabase
745  ];
746  }
747  // For "flex" fieldtypes we need to traverse the structure looking for file and db references of course!
748  if ($conf['type'] === 'flex') {
749  // Get current value array:
750  // NOTICE: failure to resolve Data Structures can lead to integrity problems with the reference index. Please look up
751  // the note in the JavaDoc documentation for the function FlexFormTools->getDataStructureIdentifier()
752  $currentValueArray = GeneralUtility::xml2array($value);
753  // Traversing the XML structure, processing files:
754  if (is_array($currentValueArray)) {
755  $this->temp_flexRelations = [
756  'db' => [],
757  'file' => [],
758  'softrefs' => []
759  ];
760  // Create and call iterator object:
761  $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
762  $flexFormTools->traverseFlexFormXMLData($table, $field, $row, $this, 'getRelations_flexFormCallBack');
763  // Create an entry for the field:
764  $outRow[$field] = [
765  'type' => 'flex',
766  'flexFormRels' => ‪$this->temp_flexRelations
767  ];
768  }
769  }
770  // Soft References:
771  if ((string)$value !== '') {
772  $softRefValue = $value;
773  if (!empty($conf['softref'])) {
774  $softRefs = ‪BackendUtility::explodeSoftRefParserList($conf['softref']);
775  foreach ($softRefs as $spKey => $spParams) {
776  $softRefObj = ‪BackendUtility::softRefParserObj($spKey);
777  if (is_object($softRefObj)) {
778  $resultArray = $softRefObj->findRef($table, $field, $uid, $softRefValue, $spKey, $spParams);
779  if (is_array($resultArray)) {
780  $outRow[$field]['softrefs']['keys'][$spKey] = $resultArray['elements'];
781  if ((string)$resultArray['content'] !== '') {
782  $softRefValue = $resultArray['content'];
783  }
784  }
785  }
786  }
787  }
788  if (!empty($outRow[$field]['softrefs']) && (string)$value !== (string)$softRefValue && strpos($softRefValue, '{softref:') !== false) {
789  $outRow[$field]['softrefs']['tokenizedContent'] = $softRefValue;
790  }
791  }
792  }
793  }
794  return $outRow;
795  }
796 
806  public function ‪getRelations_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath)
807  {
808  // Removing "data/" in the beginning of path (which points to location in data array)
809  $structurePath = substr($structurePath, 5) . '/';
810  $dsConf = $dsArr['TCEforms']['config'];
811  // Implode parameter values:
812  list($table, $uid, $field) = [
813  $PA['table'],
814  $PA['uid'],
815  $PA['field']
816  ];
817  // Add files
818  $resultsFromFiles = $this->‪getRelations_procFiles($dataValue, $dsConf, $uid);
819  if (!empty($resultsFromFiles)) {
820  // We have to fill different arrays here depending on the result.
821  // internal_type file is still a relation of type file and
822  // since http://forge.typo3.org/issues/49538 internal_type file_reference
823  // is a database relation to a sys_file record
824  $fileResultsFromFiles = [];
825  $dbResultsFromFiles = [];
826  foreach ($resultsFromFiles as $resultFromFiles) {
827  if (isset($resultFromFiles['table']) && $resultFromFiles['table'] === 'sys_file') {
828  $dbResultsFromFiles[] = $resultFromFiles;
829  } else {
830  $fileResultsFromFiles[] = $resultFromFiles;
831  }
832  }
833  if (!empty($fileResultsFromFiles)) {
834  $this->temp_flexRelations['file'][$structurePath] = $fileResultsFromFiles;
835  }
836  if (!empty($dbResultsFromFiles)) {
837  $this->temp_flexRelations['db'][$structurePath] = $dbResultsFromFiles;
838  }
839  }
840  // Add a softref definition for link fields if the TCA does not specify one already
841  if ($dsConf['type'] === 'input' && $dsConf['renderType'] === 'inputLink' && empty($dsConf['softref'])) {
842  $dsConf['softref'] = 'typolink';
843  }
844  // Add DB:
845  $resultsFromDatabase = $this->‪getRelations_procDB($dataValue, $dsConf, $uid, $table);
846  if (!empty($resultsFromDatabase)) {
847  // Create an entry for the field with all DB relations:
848  $this->temp_flexRelations['db'][$structurePath] = $resultsFromDatabase;
849  }
850  // Soft References:
851  if (is_array($dataValue) || (string)$dataValue !== '') {
852  $softRefValue = $dataValue;
853  $softRefs = ‪BackendUtility::explodeSoftRefParserList($dsConf['softref']);
854  if ($softRefs !== false) {
855  foreach ($softRefs as $spKey => $spParams) {
856  $softRefObj = ‪BackendUtility::softRefParserObj($spKey);
857  if (is_object($softRefObj)) {
858  $resultArray = $softRefObj->findRef($table, $field, $uid, $softRefValue, $spKey, $spParams, $structurePath);
859  if (is_array($resultArray) && is_array($resultArray['elements'])) {
860  $this->temp_flexRelations['softrefs'][$structurePath]['keys'][$spKey] = $resultArray['elements'];
861  if ((string)$resultArray['content'] !== '') {
862  $softRefValue = $resultArray['content'];
863  }
864  }
865  }
866  }
867  }
868  if (!empty($this->temp_flexRelations['softrefs']) && (string)$dataValue !== (string)$softRefValue) {
869  $this->temp_flexRelations['softrefs'][$structurePath]['tokenizedContent'] = $softRefValue;
870  }
871  }
872  }
873 
883  public function ‪getRelations_procFiles($value, $conf, $uid)
884  {
885  if ($conf['type'] !== 'group' || ($conf['internal_type'] !== 'file' && $conf['internal_type'] !== 'file_reference')) {
886  return false;
887  }
888 
889  // Collect file values in array:
890  if ($conf['MM']) {
891  $theFileValues = [];
892  $dbAnalysis = GeneralUtility::makeInstance(RelationHandler::class);
893  $dbAnalysis->start('', 'files', $conf['MM'], $uid);
894  foreach ($dbAnalysis->itemArray as $someval) {
895  if ($someval['id']) {
896  $theFileValues[] = $someval['id'];
897  }
898  }
899  } else {
900  $theFileValues = explode(',', $value);
901  }
902  // Traverse the files and add them:
903  $uploadFolder = $conf['internal_type'] === 'file' ? $conf['uploadfolder'] : '';
904  $destinationFolder = $this->‪destPathFromUploadFolder($uploadFolder);
905  $newValueFiles = [];
906  foreach ($theFileValues as $file) {
907  if (trim($file)) {
908  $realFile = $destinationFolder . '/' . trim($file);
909  $newValueFile = [
910  'filename' => ‪PathUtility::basename($file),
911  'ID' => md5($realFile),
912  'ID_absFile' => $realFile
913  ];
914  // Set sys_file and id for referenced files
915  if ($conf['internal_type'] === 'file_reference') {
916  try {
918  if ($file instanceof File || $file instanceof Folder) {
919  // For setting this as sys_file relation later, the keys filename, ID and ID_absFile
920  // have not to be included, because the are not evaluated for db relations.
921  $newValueFile = [
922  'table' => 'sys_file',
923  'id' => $file->getUid()
924  ];
925  }
926  } catch (\Exception $e) {
927  }
928  }
929  $newValueFiles[] = $newValueFile;
930  }
931  }
932  return $newValueFiles;
933  }
934 
944  public function ‪getRelations_procDB($value, $conf, $uid, $table = '')
945  {
946  // Get IRRE relations
947  if (empty($conf)) {
948  return false;
949  }
950  if ($conf['type'] === 'inline' && !empty($conf['foreign_table']) && empty($conf['MM'])) {
951  $dbAnalysis = GeneralUtility::makeInstance(RelationHandler::class);
952  $dbAnalysis->setUseLiveReferenceIds(false);
953  $dbAnalysis->start($value, $conf['foreign_table'], '', $uid, $table, $conf);
954  return $dbAnalysis->itemArray;
955  // DB record lists:
956  }
957  if ($this->‪isDbReferenceField($conf)) {
958  $allowedTables = $conf['type'] === 'group' ? $conf['allowed'] : $conf['foreign_table'];
959  if ($conf['MM_opposite_field']) {
960  return [];
961  }
962  $dbAnalysis = GeneralUtility::makeInstance(RelationHandler::class);
963  $dbAnalysis->start($value, $allowedTables, $conf['MM'], $uid, $table, $conf);
964  return $dbAnalysis->itemArray;
965  }
966  return false;
967  }
968 
969  /*******************************
970  *
971  * Setting values
972  *
973  *******************************/
974 
994  public function ‪setReferenceValue($hash, $newValue, $returnDataArray = false, $bypassWorkspaceAdminCheck = false)
995  {
996  $backendUser = $this->‪getBackendUser();
997  if ($backendUser->workspace === 0 && $backendUser->isAdmin() || $bypassWorkspaceAdminCheck) {
998  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
999  $queryBuilder->getRestrictions()->removeAll();
1000 
1001  // Get current index from Database
1002  $referenceRecord = $queryBuilder
1003  ->select('*')
1004  ->from('sys_refindex')
1005  ->where(
1006  $queryBuilder->expr()->eq('hash', $queryBuilder->createNamedParameter($hash, \PDO::PARAM_STR))
1007  )
1008  ->setMaxResults(1)
1009  ->execute()
1010  ->fetch();
1011 
1012  // Check if reference existed.
1013  if (!is_array($referenceRecord)) {
1014  return 'ERROR: No reference record with hash="' . $hash . '" was found!';
1015  }
1016 
1017  if (empty(‪$GLOBALS['TCA'][$referenceRecord['tablename']])) {
1018  return 'ERROR: Table "' . $referenceRecord['tablename'] . '" was not in TCA!';
1019  }
1020 
1021  // Get that record from database
1022  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1023  ->getQueryBuilderForTable($referenceRecord['tablename']);
1024  $queryBuilder->getRestrictions()->removeAll();
1025  $record = $queryBuilder
1026  ->select('*')
1027  ->from($referenceRecord['tablename'])
1028  ->where(
1029  $queryBuilder->expr()->eq(
1030  'uid',
1031  $queryBuilder->createNamedParameter($referenceRecord['recuid'], \PDO::PARAM_INT)
1032  )
1033  )
1034  ->setMaxResults(1)
1035  ->execute()
1036  ->fetch();
1037 
1038  if (is_array($record)) {
1039  // Get relation for single field from record
1040  $recordRelations = $this->‪getRelations($referenceRecord['tablename'], $record, $referenceRecord['field']);
1041  if ($fieldRelation = $recordRelations[$referenceRecord['field']]) {
1042  // Initialize data array that is to be sent to DataHandler afterwards:
1043  $dataArray = [];
1044  // Based on type
1045  switch ((string)$fieldRelation['type']) {
1046  case 'db':
1047  $error = $this->‪setReferenceValue_dbRels($referenceRecord, $fieldRelation['itemArray'], $newValue, $dataArray);
1048  if ($error) {
1049  return $error;
1050  }
1051  break;
1052  case 'file_reference':
1053  // not used (see getRelations()), but fallback to file
1054  case 'file':
1055  $error = $this->‪setReferenceValue_fileRels($referenceRecord, $fieldRelation['newValueFiles'], $newValue, $dataArray);
1056  if ($error) {
1057  return $error;
1058  }
1059  break;
1060  case 'flex':
1061  // DB references in FlexForms
1062  if (is_array($fieldRelation['flexFormRels']['db'][$referenceRecord['flexpointer']])) {
1063  $error = $this->‪setReferenceValue_dbRels($referenceRecord, $fieldRelation['flexFormRels']['db'][$referenceRecord['flexpointer']], $newValue, $dataArray, $referenceRecord['flexpointer']);
1064  if ($error) {
1065  return $error;
1066  }
1067  }
1068  // File references in FlexForms
1069  if (is_array($fieldRelation['flexFormRels']['file'][$referenceRecord['flexpointer']])) {
1070  $error = $this->‪setReferenceValue_fileRels($referenceRecord, $fieldRelation['flexFormRels']['file'][$referenceRecord['flexpointer']], $newValue, $dataArray, $referenceRecord['flexpointer']);
1071  if ($error) {
1072  return $error;
1073  }
1074  }
1075  // Soft references in FlexForms
1076  if ($referenceRecord['softref_key'] && is_array($fieldRelation['flexFormRels']['softrefs'][$referenceRecord['flexpointer']]['keys'][$referenceRecord['softref_key']])) {
1077  $error = $this->‪setReferenceValue_softreferences($referenceRecord, $fieldRelation['flexFormRels']['softrefs'][$referenceRecord['flexpointer']], $newValue, $dataArray, $referenceRecord['flexpointer']);
1078  if ($error) {
1079  return $error;
1080  }
1081  }
1082  break;
1083  }
1084  // Soft references in the field:
1085  if ($referenceRecord['softref_key'] && is_array($fieldRelation['softrefs']['keys'][$referenceRecord['softref_key']])) {
1086  $error = $this->‪setReferenceValue_softreferences($referenceRecord, $fieldRelation['softrefs'], $newValue, $dataArray);
1087  if ($error) {
1088  return $error;
1089  }
1090  }
1091  // Data Array, now ready to be sent to DataHandler
1092  if ($returnDataArray) {
1093  return $dataArray;
1094  }
1095  // Execute CMD array:
1096  $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
1097  $dataHandler->dontProcessTransformations = true;
1098  $dataHandler->bypassWorkspaceRestrictions = true;
1099  $dataHandler->bypassFileHandling = true;
1100  // Otherwise this cannot update things in deleted records...
1101  $dataHandler->bypassAccessCheckForRecords = true;
1102  // Check has been done previously that there is a backend user which is Admin and also in live workspace
1103  $dataHandler->start($dataArray, []);
1104  $dataHandler->process_datamap();
1105  // Return errors if any:
1106  if (!empty($dataHandler->errorLog)) {
1107  return LF . 'DataHandler:' . implode(LF . 'DataHandler:', $dataHandler->errorLog);
1108  }
1109  }
1110  }
1111  } else {
1112  return 'ERROR: BE_USER object is not admin OR not in workspace 0 (Live)';
1113  }
1114 
1115  return false;
1116  }
1128  public function ‪setReferenceValue_dbRels($refRec, $itemArray, $newValue, &$dataArray, $flexPointer = '')
1129  {
1130  if ((int)$itemArray[$refRec['sorting']]['id'] === (int)$refRec['ref_uid'] && (string)$itemArray[$refRec['sorting']]['table'] === (string)$refRec['ref_table']) {
1131  // Setting or removing value:
1132  // Remove value:
1133  if ($newValue === null) {
1134  unset($itemArray[$refRec['sorting']]);
1135  } else {
1136  list($itemArray[$refRec['sorting']]['table'], $itemArray[$refRec['sorting']]['id']) = explode(':', $newValue);
1137  }
1138  // Traverse and compile new list of records:
1139  $saveValue = [];
1140  foreach ($itemArray as $pair) {
1141  $saveValue[] = $pair['table'] . '_' . $pair['id'];
1142  }
1143  // Set in data array:
1144  if ($flexPointer) {
1145  $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
1146  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'] = [];
1147  $flexFormTools->setArrayValueByPath(substr($flexPointer, 0, -1), $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'], implode(',', $saveValue));
1148  } else {
1149  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']] = implode(',', $saveValue);
1150  }
1151  } else {
1152  return 'ERROR: table:id pair "' . $refRec['ref_table'] . ':' . $refRec['ref_uid'] . '" did not match that of the record ("' . $itemArray[$refRec['sorting']]['table'] . ':' . $itemArray[$refRec['sorting']]['id'] . '") in sorting index "' . $refRec['sorting'] . '"';
1153  }
1154 
1155  return false;
1156  }
1168  public function ‪setReferenceValue_fileRels($refRec, $itemArray, $newValue, &$dataArray, $flexPointer = '')
1169  {
1170  $ID_absFile = ‪PathUtility::stripPathSitePrefix($itemArray[$refRec['sorting']]['ID_absFile']);
1171  if ($ID_absFile === (string)$refRec['ref_string'] && $refRec['ref_table'] === '_FILE') {
1172  // Setting or removing value:
1173  // Remove value:
1174  if ($newValue === null) {
1175  unset($itemArray[$refRec['sorting']]);
1176  } else {
1177  $itemArray[$refRec['sorting']]['filename'] = $newValue;
1178  }
1179  // Traverse and compile new list of records:
1180  $saveValue = [];
1181  foreach ($itemArray as $fileInfo) {
1182  $saveValue[] = $fileInfo['filename'];
1183  }
1184  // Set in data array:
1185  if ($flexPointer) {
1186  $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
1187  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'] = [];
1188  $flexFormTools->setArrayValueByPath(substr($flexPointer, 0, -1), $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'], implode(',', $saveValue));
1189  } else {
1190  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']] = implode(',', $saveValue);
1191  }
1192  } else {
1193  return 'ERROR: either "' . $refRec['ref_table'] . '" was not "_FILE" or file Environment::getPublicPath()+"' . $refRec['ref_string'] . '" did not match that of the record ("' . $itemArray[$refRec['sorting']]['ID_absFile'] . '") in sorting index "' . $refRec['sorting'] . '"';
1194  }
1195 
1196  return false;
1197  }
1209  public function ‪setReferenceValue_softreferences($refRec, $softref, $newValue, &$dataArray, $flexPointer = '')
1210  {
1211  if (!is_array($softref['keys'][$refRec['softref_key']][$refRec['softref_id']])) {
1212  return 'ERROR: Soft reference parser key "' . $refRec['softref_key'] . '" or the index "' . $refRec['softref_id'] . '" was not found.';
1213  }
1214 
1215  // Set new value:
1216  $softref['keys'][$refRec['softref_key']][$refRec['softref_id']]['subst']['tokenValue'] = '' . $newValue;
1217  // Traverse softreferences and replace in tokenized content to rebuild it with new value inside:
1218  foreach ($softref['keys'] as $sfIndexes) {
1219  foreach ($sfIndexes as $data) {
1220  $softref['tokenizedContent'] = str_replace('{softref:' . $data['subst']['tokenID'] . '}', $data['subst']['tokenValue'], $softref['tokenizedContent']);
1221  }
1222  }
1223  // Set in data array:
1224  if (!strstr($softref['tokenizedContent'], '{softref:')) {
1225  if ($flexPointer) {
1226  $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
1227  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'] = [];
1228  $flexFormTools->setArrayValueByPath(substr($flexPointer, 0, -1), $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'], $softref['tokenizedContent']);
1229  } else {
1230  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']] = $softref['tokenizedContent'];
1231  }
1232  } else {
1233  return 'ERROR: After substituting all found soft references there were still soft reference tokens in the text. (theoretically this does not have to be an error if the string "{softref:" happens to be in the field for another reason.)';
1234  }
1235 
1236  return false;
1237  }
1238 
1239  /*******************************
1240  *
1241  * Helper functions
1242  *
1243  *******************************/
1244 
1251  protected function ‪isDbReferenceField(array $configuration)
1252  {
1253  return
1254  ($configuration['type'] === 'group' && $configuration['internal_type'] === 'db')
1255  || (
1256  ($configuration['type'] === 'select' || $configuration['type'] === 'inline')
1257  && !empty($configuration['foreign_table'])
1258  )
1259  ;
1260  }
1261 
1268  public function ‪isReferenceField(array $configuration)
1269  {
1270  return
1271  $this->‪isDbReferenceField($configuration)
1272  ||
1273  ($configuration['type'] === 'group' && ($configuration['internal_type'] === 'file' || $configuration['internal_type'] === 'file_reference')) // getRelations_procFiles
1274  ||
1275  ($configuration['type'] === 'input' && $configuration['renderType'] === 'inputLink') // getRelations_procDB
1276  ||
1277  $configuration['type'] === 'flex'
1278  ||
1279  isset($configuration['softref'])
1280  ;
1281  }
1282 
1289  protected function ‪fetchTableRelationFields($tableName)
1290  {
1291  if (!isset(‪$GLOBALS['TCA'][$tableName]['columns'])) {
1292  return '';
1293  }
1294 
1295  ‪$fields = [];
1296 
1297  foreach (‪$GLOBALS['TCA'][$tableName]['columns'] as $field => $fieldDefinition) {
1298  if (is_array($fieldDefinition['config'])) {
1299  // Check for flex field
1300  if (isset($fieldDefinition['config']['type']) && $fieldDefinition['config']['type'] === 'flex') {
1301  // Fetch all fields if the is a field of type flex in the table definition because the complete row is passed to
1302  // FlexFormTools->getDataStructureIdentifier() in the end and might be needed in ds_pointerField or a hook
1303  return '*';
1304  }
1305  // Only fetch this field if it can contain a reference
1306  if ($this->‪isReferenceField($fieldDefinition['config'])) {
1307  ‪$fields[] = $field;
1308  }
1309  }
1310  }
1311 
1312  return implode(',', ‪$fields);
1313  }
1314 
1321  public function ‪destPathFromUploadFolder($folder)
1322  {
1323  return ‪Environment::getPublicPath() . ($folder ? '/' . $folder : '');
1324  }
1325 
1333  public function ‪updateIndex($testOnly, $cli_echo = false)
1334  {
1335  ‪$errors = [];
1336  $tableNames = [];
1337  $recCount = 0;
1338  $tableCount = 0;
1339  $headerContent = $testOnly ? 'Reference Index being TESTED (nothing written, remove the "--check" argument)' : 'Reference Index being Updated';
1340  if ($cli_echo) {
1341  echo '*******************************************' . LF . $headerContent . LF . '*******************************************' . LF;
1342  }
1343  // Traverse all tables:
1344  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
1345  $refIndexConnectionName = empty(‪$GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping']['sys_refindex'])
1347  : ‪$GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping']['sys_refindex'];
1348 
1349  foreach (‪$GLOBALS['TCA'] as $tableName => $cfg) {
1350  if ($this->‪shouldExcludeTableFromReferenceIndex($tableName)) {
1351  continue;
1352  }
1353  $tableConnectionName = empty(‪$GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'][$tableName])
1355  : ‪$GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'][$tableName];
1356 
1357  ‪$fields = ['uid'];
1359  ‪$fields[] = 't3ver_wsid';
1360  }
1361  // Traverse all records in tables, including deleted records
1362  $queryBuilder = $connectionPool->getQueryBuilderForTable($tableName);
1363  $queryBuilder->getRestrictions()->removeAll();
1364  try {
1365  $queryResult = $queryBuilder
1366  ->select(...‪$fields)
1367  ->from($tableName)
1368  ->execute();
1369  } catch (DBALException $e) {
1370  // Table exists in TCA but does not exist in the database
1371  $msg = 'Table "' .
1372  $tableName .
1373  '" exists in TCA but does not exist in the database. You should run the Database Analyzer in the Install Tool to fix this.';
1374  $this->logger->error($msg, ['exception' => $e]);
1375  continue;
1376  }
1377 
1378  $tableNames[] = $tableName;
1379  $tableCount++;
1380  while ($record = $queryResult->fetch()) {
1381  $refIndexObj = GeneralUtility::makeInstance(self::class);
1382  if (isset($record['t3ver_wsid'])) {
1383  $refIndexObj->setWorkspaceId($record['t3ver_wsid']);
1384  }
1385  $result = $refIndexObj->updateRefIndexTable($tableName, $record['uid'], $testOnly);
1386  $recCount++;
1387  if ($result['addedNodes'] || $result['deletedNodes']) {
1388  $error = 'Record ' . $tableName . ':' . $record['uid'] . ' had ' . $result['addedNodes'] . ' added indexes and ' . $result['deletedNodes'] . ' deleted indexes';
1389  ‪$errors[] = $error;
1390  if ($cli_echo) {
1391  echo $error . LF;
1392  }
1393  }
1394  }
1395 
1396  // Subselect based queries only work on the same connection
1397  if ($refIndexConnectionName !== $tableConnectionName) {
1398  $this->logger->error('Not checking table "' . $tableName . '" for lost indexes, "sys_refindex" table uses a different connection');
1399  continue;
1400  }
1401 
1402  // Searching for lost indexes for this table
1403  // Build sub-query to find lost records
1404  $subQueryBuilder = $connectionPool->getQueryBuilderForTable($tableName);
1405  $subQueryBuilder->getRestrictions()->removeAll();
1406  $subQueryBuilder
1407  ->select('uid')
1408  ->from($tableName, 'sub_' . $tableName)
1409  ->where(
1410  $subQueryBuilder->expr()->eq(
1411  'sub_' . $tableName . '.uid',
1412  $queryBuilder->quoteIdentifier('sys_refindex.recuid')
1413  )
1414  );
1415 
1416  // Main query to find lost records
1417  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_refindex');
1418  $queryBuilder->getRestrictions()->removeAll();
1419  $lostIndexes = $queryBuilder
1420  ->count('hash')
1421  ->from('sys_refindex')
1422  ->where(
1423  $queryBuilder->expr()->eq(
1424  'tablename',
1425  $queryBuilder->createNamedParameter($tableName, \PDO::PARAM_STR)
1426  ),
1427  'NOT EXISTS (' . $subQueryBuilder->getSQL() . ')'
1428  )
1429  ->execute()
1430  ->fetchColumn(0);
1431 
1432  if ($lostIndexes > 0) {
1433  $error = 'Table ' . $tableName . ' has ' . $lostIndexes . ' lost indexes which are now deleted';
1434  ‪$errors[] = $error;
1435  if ($cli_echo) {
1436  echo $error . LF;
1437  }
1438  if (!$testOnly) {
1439  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_refindex');
1440  $queryBuilder->delete('sys_refindex')
1441  ->where(
1442  $queryBuilder->expr()->eq(
1443  'tablename',
1444  $queryBuilder->createNamedParameter($tableName, \PDO::PARAM_STR)
1445  ),
1446  'NOT EXISTS (' . $subQueryBuilder->getSQL() . ')'
1447  )
1448  ->execute();
1449  }
1450  }
1451  }
1452 
1453  // Searching lost indexes for non-existing tables
1454  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_refindex');
1455  $queryBuilder->getRestrictions()->removeAll();
1456  $lostTables = $queryBuilder
1457  ->count('hash')
1458  ->from('sys_refindex')
1459  ->where(
1460  $queryBuilder->expr()->notIn(
1461  'tablename',
1462  $queryBuilder->createNamedParameter($tableNames, Connection::PARAM_STR_ARRAY)
1463  )
1464  )->execute()
1465  ->fetchColumn(0);
1466 
1467  if ($lostTables > 0) {
1468  $error = 'Index table hosted ' . $lostTables . ' indexes for non-existing tables, now removed';
1469  ‪$errors[] = $error;
1470  if ($cli_echo) {
1471  echo $error . LF;
1472  }
1473  if (!$testOnly) {
1474  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_refindex');
1475  $queryBuilder->delete('sys_refindex')
1476  ->where(
1477  $queryBuilder->expr()->notIn(
1478  'tablename',
1479  $queryBuilder->createNamedParameter($tableNames, Connection::PARAM_STR_ARRAY)
1480  )
1481  )->execute();
1482  }
1483  }
1484  $errorCount = count(‪$errors);
1485  $recordsCheckedString = $recCount . ' records from ' . $tableCount . ' tables were checked/updated.' . LF;
1486  $flashMessage = GeneralUtility::makeInstance(
1487  FlashMessage::class,
1488  $errorCount ? implode('##LF##', ‪$errors) : 'Index Integrity was perfect!',
1489  $recordsCheckedString,
1491  );
1492 
1493  $flashMessageRenderer = GeneralUtility::makeInstance(FlashMessageRendererResolver::class)->resolve();
1494  $bodyContent = $flashMessageRenderer->render([$flashMessage]);
1495  if ($cli_echo) {
1496  echo $recordsCheckedString . ($errorCount ? 'Updates: ' . $errorCount : 'Index Integrity was perfect!') . LF;
1497  }
1498  if (!$testOnly) {
1499  $registry = GeneralUtility::makeInstance(Registry::class);
1500  $registry->set('core', 'sys_refindex_lastUpdate', ‪$GLOBALS['EXEC_TIME']);
1501  }
1502  return [$headerContent, $bodyContent, $errorCount];
1503  }
1504 
1525  protected function ‪getRecordRawCached(string $tableName, int $uid)
1526  {
1527  $recordCacheId = $tableName . ':' . $uid;
1528  if (!$this->useRuntimeCache || !isset($this->recordCache[$recordCacheId])) {
1529 
1530  // Fetch fields of the table which might contain relations
1531  $cacheId = static::$cachePrefixTableRelationFields . $tableName;
1532  if (!$this->useRuntimeCache || !$this->runtimeCache->has($cacheId)) {
1533  $tableRelationFields = $this->‪fetchTableRelationFields($tableName);
1534  if ($this->useRuntimeCache) {
1535  $this->runtimeCache->set($cacheId, $tableRelationFields);
1536  }
1537  } else {
1538  $tableRelationFields = $this->runtimeCache->get($cacheId);
1539  }
1540 
1541  // Return if there are no fields which could contain relations
1542  if ($tableRelationFields === '') {
1543  return ‪$this->relations;
1544  }
1545 
1546  if ($tableRelationFields === '*') {
1547  // If one field of a record is of type flex, all fields have to be fetched to be passed to FlexFormTools->getDataStructureIdentifier()
1548  $selectFields = '*';
1549  } else {
1550  // otherwise only fields that might contain relations are fetched
1551  $selectFields = 'uid,' . $tableRelationFields;
1552  $deleteField = ‪$GLOBALS['TCA'][$tableName]['ctrl']['delete'];
1553  if ($deleteField) {
1554  $selectFields .= ',' . $deleteField;
1555  }
1557  $selectFields .= ',t3ver_wsid';
1558  }
1559  }
1560 
1561  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1562  ->getQueryBuilderForTable($tableName);
1563  $queryBuilder->getRestrictions()->removeAll();
1564  $row = $queryBuilder
1565  ->select(...GeneralUtility::trimExplode(',', $selectFields, true))
1566  ->from($tableName)
1567  ->where(
1568  $queryBuilder->expr()->eq(
1569  'uid',
1570  $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
1571  )
1572  )
1573  ->execute()
1574  ->fetch();
1575 
1576  $this->recordCache[$recordCacheId] = $row;
1577  }
1578  return $this->recordCache[$recordCacheId];
1579  }
1580 
1587  protected function ‪shouldExcludeTableFromReferenceIndex($tableName)
1588  {
1589  if (isset(static::$excludedTables[$tableName])) {
1590  return static::$excludedTables[$tableName];
1591  }
1592 
1593  // Only exclude tables from ReferenceIndex which do not contain any relations and never did since existing references won't be deleted!
1594  // There is no need to add tables without a definition in $GLOBALS['TCA] since ReferenceIndex only handles those.
1595  $excludeTable = false;
1596  ‪$signalSlotDispatcher = GeneralUtility::makeInstance(Dispatcher::class);
1597  ‪$signalSlotDispatcher->dispatch(__CLASS__, 'shouldExcludeTableFromReferenceIndex', [$tableName, &$excludeTable]);
1598 
1599  static::$excludedTables[$tableName] = $excludeTable;
1600 
1601  return static::$excludedTables[$tableName];
1602  }
1603 
1612  protected function ‪shouldExcludeTableColumnFromReferenceIndex($tableName, $column, $onlyColumn)
1613  {
1614  if (isset(static::$excludedColumns[$column])) {
1615  return true;
1616  }
1617 
1618  if (is_array(‪$GLOBALS['TCA'][$tableName]['columns'][$column]) && (!$onlyColumn || $onlyColumn === $column)) {
1619  return false;
1620  }
1621 
1622  return true;
1623  }
1624 
1630  public function ‪enableRuntimeCache()
1631  {
1632  $this->useRuntimeCache = true;
1633  }
1634 
1638  public function ‪disableRuntimeCache()
1639  {
1640  $this->useRuntimeCache = false;
1641  }
1642 
1648  protected function ‪getBackendUser()
1649  {
1650  return ‪$GLOBALS['BE_USER'];
1651  }
1652 }
‪TYPO3\CMS\Core\DataHandling\DataHandler
Definition: DataHandler.php:81
‪TYPO3\CMS\Core\Database\ReferenceIndex\disableRuntimeCache
‪disableRuntimeCache()
Definition: ReferenceIndex.php:1627
‪TYPO3\CMS\Core\Database\ReferenceIndex\$cachePrefixTableRelationFields
‪static string $cachePrefixTableRelationFields
Definition: ReferenceIndex.php:95
‪TYPO3\CMS\Core\Database\ReferenceIndex\$temp_flexRelations
‪array $temp_flexRelations
Definition: ReferenceIndex.php:102
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:23
‪TYPO3\CMS\Core\Database\ReferenceIndex\$relations
‪array $relations
Definition: ReferenceIndex.php:117
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static string getPublicPath()
Definition: Environment.php:153
‪TYPO3\CMS\Core\Database\ReferenceIndex\getRecordRawCached
‪array false getRecordRawCached(string $tableName, int $uid)
Definition: ReferenceIndex.php:1514
‪TYPO3\CMS\Core\Database\ReferenceIndex\getRelations_procDB
‪array bool getRelations_procDB($value, $conf, $uid, $table='')
Definition: ReferenceIndex.php:933
‪TYPO3\CMS\Core\Database\ReferenceIndex\enableRuntimeCache
‪enableRuntimeCache()
Definition: ReferenceIndex.php:1619
‪TYPO3\CMS\Core\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Core\Database\ReferenceIndex\shouldExcludeTableColumnFromReferenceIndex
‪bool shouldExcludeTableColumnFromReferenceIndex($tableName, $column, $onlyColumn)
Definition: ReferenceIndex.php:1601
‪TYPO3\CMS\Core\Database\ReferenceIndex\shouldExcludeTableFromReferenceIndex
‪bool shouldExcludeTableFromReferenceIndex($tableName)
Definition: ReferenceIndex.php:1576
‪TYPO3\CMS\Backend\Utility\BackendUtility\softRefParserObj
‪static mixed & softRefParserObj($spKey)
Definition: BackendUtility.php:3692
‪TYPO3\CMS\Core\Database\ReferenceIndex\createEntryData_softreferences
‪createEntryData_softreferences($table, $uid, $fieldName, $flexPointer, $deleted, $keys)
Definition: ReferenceIndex.php:616
‪TYPO3\CMS\Core\Utility\PathUtility\stripPathSitePrefix
‪static string stripPathSitePrefix($path)
Definition: PathUtility.php:371
‪TYPO3\CMS\Core\Database\ReferenceIndex
Definition: ReferenceIndex.php:50
‪TYPO3\CMS\Core\Database\ReferenceIndex\createEntryDataForSoftReferencesUsingRecord
‪createEntryDataForSoftReferencesUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, int $deleted, array $keys)
Definition: ReferenceIndex.php:642
‪TYPO3\CMS\Core\Database\ReferenceIndex\$excludedColumns
‪static array $excludedColumns
Definition: ReferenceIndex.php:80
‪TYPO3\CMS\Core\Registry
Definition: Registry.php:32
‪TYPO3\CMS\Core\Database\ReferenceIndex\fetchTableRelationFields
‪string fetchTableRelationFields($tableName)
Definition: ReferenceIndex.php:1278
‪TYPO3\CMS\Core\Database\ReferenceIndex\$excludedTables
‪static array $excludedTables
Definition: ReferenceIndex.php:65
‪TYPO3\CMS\Core\Database\ReferenceIndex\$recordCache
‪array $recordCache
Definition: ReferenceIndex.php:124
‪TYPO3\CMS\Core\Resource\ResourceFactory\getInstance
‪static ResourceFactory getInstance()
Definition: ResourceFactory.php:39
‪TYPO3\CMS\Core\Database\ReferenceIndex\getRelations
‪array getRelations($table, $row, $onlyField='')
Definition: ReferenceIndex.php:685
‪TYPO3\CMS\Core\Database\ReferenceIndex\$WSOL
‪bool $WSOL
Definition: ReferenceIndex.php:110
‪TYPO3\CMS\Core\Database\ReferenceIndex\$useRuntimeCache
‪bool $useRuntimeCache
Definition: ReferenceIndex.php:148
‪TYPO3\CMS\Core\Messaging\FlashMessageRendererResolver
Definition: FlashMessageRendererResolver.php:26
‪TYPO3\CMS\Core\Database\ReferenceIndex\updateIndex
‪array updateIndex($testOnly, $cli_echo=false)
Definition: ReferenceIndex.php:1322
‪$fields
‪$fields
Definition: pages.php:4
‪TYPO3\CMS\Core\Database\ReferenceIndex\createEntryData_dbRels
‪createEntryData_dbRels($table, $uid, $fieldName, $flexPointer, $deleted, $items)
Definition: ReferenceIndex.php:516
‪TYPO3\CMS\Core\Utility\PathUtility\basename
‪static string basename($path)
Definition: PathUtility.php:164
‪TYPO3\CMS\Core\Database\ConnectionPool\DEFAULT_CONNECTION_NAME
‪const DEFAULT_CONNECTION_NAME
Definition: ConnectionPool.php:48
‪TYPO3\CMS\Core\Database\ReferenceIndex\isReferenceField
‪bool isReferenceField(array $configuration)
Definition: ReferenceIndex.php:1257
‪TYPO3\CMS\Backend\Utility\BackendUtility\isTableWorkspaceEnabled
‪static bool isTableWorkspaceEnabled($table)
Definition: BackendUtility.php:4493
‪TYPO3\CMS\Core\Database\ReferenceIndex\createEntryDataUsingRecord
‪array bool createEntryDataUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, int $deleted, string $referencedTable, int $referencedUid, string $referenceString='', int $sort=-1, string $softReferenceKey='', string $softReferenceId='')
Definition: ReferenceIndex.php:480
‪TYPO3\CMS\Core\Database\ReferenceIndex\setWorkspaceId
‪setWorkspaceId($workspaceId)
Definition: ReferenceIndex.php:164
‪TYPO3\CMS\Core\Database\ReferenceIndex\$workspaceId
‪int $workspaceId
Definition: ReferenceIndex.php:137
‪TYPO3\CMS\Core\Database\ReferenceIndex\getBackendUser
‪TYPO3 CMS Core Authentication BackendUserAuthentication getBackendUser()
Definition: ReferenceIndex.php:1637
‪TYPO3\CMS\Core\Database\ReferenceIndex\updateRefIndexTable
‪array updateRefIndexTable($tableName, $uid, $testOnly=false)
Definition: ReferenceIndex.php:192
‪TYPO3\CMS\Core\Database\ReferenceIndex\isDbReferenceField
‪bool isDbReferenceField(array $configuration)
Definition: ReferenceIndex.php:1240
‪TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools
Definition: FlexFormTools.php:36
‪TYPO3\CMS\Core\Resource\Folder
Definition: Folder.php:34
‪TYPO3\CMS\Core\Resource\ResourceFactory
Definition: ResourceFactory.php:33
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:23
‪TYPO3\CMS\Core\Database\ReferenceIndex\setReferenceValue_dbRels
‪string setReferenceValue_dbRels($refRec, $itemArray, $newValue, &$dataArray, $flexPointer='')
Definition: ReferenceIndex.php:1117
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:34
‪TYPO3\CMS\Core\Database\ReferenceIndex\getRelations_procFiles
‪bool array getRelations_procFiles($value, $conf, $uid)
Definition: ReferenceIndex.php:872
‪TYPO3\CMS\Core\Database\ReferenceIndex\$hashVersion
‪int $hashVersion
Definition: ReferenceIndex.php:131
‪TYPO3\CMS\Core\Database\ReferenceIndex\createEntryDataForFileRelationsUsingRecord
‪createEntryDataForFileRelationsUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, int $deleted, array $items)
Definition: ReferenceIndex.php:585
‪TYPO3\CMS\Backend\Utility\BackendUtility
Definition: BackendUtility.php:72
‪TYPO3\CMS\Backend\Utility\BackendUtility\explodeSoftRefParserList
‪static array bool explodeSoftRefParserList($parserList)
Definition: BackendUtility.php:3728
‪TYPO3\CMS\Core\Database\ReferenceIndex\createEntryDataForDatabaseRelationsUsingRecord
‪createEntryDataForDatabaseRelationsUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, int $deleted, array $items)
Definition: ReferenceIndex.php:542
‪TYPO3\CMS\Core\Database\ReferenceIndex\createEntryData_fileRels
‪createEntryData_fileRels($table, $uid, $fieldName, $flexPointer, $deleted, $items)
Definition: ReferenceIndex.php:559
‪$errors
‪$errors
Definition: annotationChecker.php:115
‪TYPO3\CMS\Core\Messaging\AbstractMessage\OK
‪const OK
Definition: AbstractMessage.php:27
‪TYPO3\CMS\Core\Database\ReferenceIndex\generateDataUsingRecord
‪array generateDataUsingRecord(string $tableName, array $record)
Definition: ReferenceIndex.php:372
‪TYPO3\CMS\Core\Database\ReferenceIndex\$runtimeCache
‪TYPO3 CMS Core Cache Frontend FrontendInterface $runtimeCache
Definition: ReferenceIndex.php:143
‪TYPO3\CMS\Core\Messaging\FlashMessage
Definition: FlashMessage.php:22
‪TYPO3\CMS\Core\Database\ReferenceIndex\getRelations_flexFormCallBack
‪getRelations_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath)
Definition: ReferenceIndex.php:795
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:39
‪TYPO3\CMS\Core\Database\Platform\PlatformInformation
Definition: PlatformInformation.php:31
‪TYPO3\CMS\Core\Database\ReferenceIndex\setReferenceValue_softreferences
‪string setReferenceValue_softreferences($refRec, $softref, $newValue, &$dataArray, $flexPointer='')
Definition: ReferenceIndex.php:1198
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Core\Database\ReferenceIndex\getWorkspaceId
‪int getWorkspaceId()
Definition: ReferenceIndex.php:175
‪$signalSlotDispatcher
‪$signalSlotDispatcher
Definition: ext_localconf.php:6
‪TYPO3\CMS\Core\Resource\ResourceFactory\retrieveFileOrFolderObject
‪File Folder null retrieveFileOrFolderObject($input)
Definition: ResourceFactory.php:491
‪TYPO3\CMS\Core\Messaging\AbstractMessage\ERROR
‪const ERROR
Definition: AbstractMessage.php:29
‪TYPO3\CMS\Core\Database\ReferenceIndex\__construct
‪__construct()
Definition: ReferenceIndex.php:153
‪TYPO3\CMS\Core\Database\ReferenceIndex\destPathFromUploadFolder
‪string destPathFromUploadFolder($folder)
Definition: ReferenceIndex.php:1310
‪TYPO3\CMS\Core\Database
Definition: Connection.php:3
‪TYPO3\CMS\Core\Database\ReferenceIndex\setReferenceValue_fileRels
‪string setReferenceValue_fileRels($refRec, $itemArray, $newValue, &$dataArray, $flexPointer='')
Definition: ReferenceIndex.php:1157
‪TYPO3\CMS\Core\Database\ReferenceIndex\setReferenceValue
‪string bool array setReferenceValue($hash, $newValue, $returnDataArray=false, $bypassWorkspaceAdminCheck=false)
Definition: ReferenceIndex.php:983
‪TYPO3\CMS\Extbase\SignalSlot\Dispatcher
Definition: Dispatcher.php:28