TYPO3 CMS  TYPO3_8-7
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 
32 
47 {
61  protected static $excludedTables = [
62  'sys_log' => true,
63  'sys_history' => true,
64  'tx_extensionmanager_domain_model_extension' => true
65  ];
66 
77  protected static $excludedColumns = [
78  'uid' => true,
79  'perms_userid' => true,
80  'perms_groupid' => true,
81  'perms_user' => true,
82  'perms_group' => true,
83  'perms_everybody' => true,
84  'pid' => true
85  ];
86 
93  protected static $cachePrefixTableRelationFields = 'core-refidx-tblRelFields-';
94 
101  public $temp_flexRelations = [];
102 
110  public $WSOL = false;
111 
118  public $relations = [];
119 
126  protected $recordCache = [];
127 
134  public $hashVersion = 1;
135 
141  protected $workspaceId = 0;
142 
148  protected $runtimeCache = null;
149 
154  protected $useRuntimeCache = false;
155 
159  public function __construct()
160  {
161  $this->runtimeCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_runtime');
162  }
163 
170  public function setWorkspaceId($workspaceId)
171  {
172  $this->workspaceId = (int)$workspaceId;
173  }
174 
181  public function getWorkspaceId()
182  {
183  return $this->workspaceId;
184  }
185 
198  public function updateRefIndexTable($tableName, $uid, $testOnly = false)
199  {
200  // First, secure that the index table is not updated with workspace tainted relations:
201  $this->WSOL = false;
202 
203  // Init:
204  $result = [
205  'keptNodes' => 0,
206  'deletedNodes' => 0,
207  'addedNodes' => 0
208  ];
209 
210  $uid = $uid ? (int)$uid : 0;
211  if (!$uid) {
212  return $result;
213  }
214 
215  // If this table cannot contain relations, skip it
216  if ($this->shouldExcludeTableFromReferenceIndex($tableName)) {
217  return $result;
218  }
219 
220  // Fetch tableRelationFields and save them in cache if not there yet
221  $cacheId = static::$cachePrefixTableRelationFields . $tableName;
222  if (!$this->useRuntimeCache || !$this->runtimeCache->has($cacheId)) {
223  $tableRelationFields = $this->fetchTableRelationFields($tableName);
224  $this->runtimeCache->set($cacheId, $tableRelationFields);
225  } else {
226  $tableRelationFields = $this->runtimeCache->get($cacheId);
227  }
228 
229  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
230  $connection = $connectionPool->getConnectionForTable('sys_refindex');
231 
232  // Get current index from Database with hash as index using $uidIndexField
233  // no restrictions are needed, since sys_refindex is not a TCA table
234  $queryBuilder = $connection->createQueryBuilder();
235  $queryBuilder->getRestrictions()->removeAll();
236  $queryResult = $queryBuilder->select('hash')->from('sys_refindex')->where(
237  $queryBuilder->expr()->eq('tablename', $queryBuilder->createNamedParameter($tableName, \PDO::PARAM_STR)),
238  $queryBuilder->expr()->eq('recuid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)),
239  $queryBuilder->expr()->eq(
240  'workspace',
241  $queryBuilder->createNamedParameter($this->getWorkspaceId(), \PDO::PARAM_INT)
242  )
243  )->execute();
244  $currentRelationHashes = [];
245  while ($relation = $queryResult->fetch()) {
246  $currentRelationHashes[$relation['hash']] = true;
247  }
248 
249  // If the table has fields which could contain relations and the record does exist (including deleted-flagged)
250  if ($tableRelationFields !== '') {
251  $existingRecord = $this->getRecordRawCached($tableName, $uid);
252  if ($existingRecord) {
253  // Table has relation fields and record exists - get relations
254  $this->relations = [];
255  $relations = $this->generateDataUsingRecord($tableName, $existingRecord);
256  if (!is_array($relations)) {
257  return $result;
258  }
259  // Traverse the generated index:
260  foreach ($relations as &$relation) {
261  if (!is_array($relation)) {
262  continue;
263  }
264  $relation['hash'] = md5(implode('
265  // First, check if already indexed and if so, unset that row (so in the end we know which rows to remove!)
266  if (isset($currentRelationHashes[$relation['hash']])) {
267  unset($currentRelationHashes[$relation['hash']]);
268  $result['keptNodes']++;
269  $relation['_ACTION'] = 'KEPT';
270  } else {
271  // If new, add it:
272  if (!$testOnly) {
273  $connection->insert('sys_refindex', $relation);
274  }
275  $result['addedNodes']++;
276  $relation['_ACTION'] = 'ADDED';
277  }
278  }
279  $result['relations'] = $relations;
280  }
281  }
282 
283  // If any old are left, remove them:
284  if (!empty($currentRelationHashes)) {
285  $hashList = array_keys($currentRelationHashes);
286  if (!empty($hashList)) {
287  $result['deletedNodes'] = count($hashList);
288  $result['deletedNodes_hashList'] = implode(',', $hashList);
289  if (!$testOnly) {
290  $maxBindParameters = PlatformInformation::getMaxBindParameters($connection->getDatabasePlatform());
291  foreach (array_chunk($hashList, $maxBindParameters - 10, true) as $chunk) {
292  if (empty($chunk)) {
293  continue;
294  }
295  $queryBuilder = $connection->createQueryBuilder();
296  $queryBuilder
297  ->delete('sys_refindex')
298  ->where(
299  $queryBuilder->expr()->in(
300  'hash',
301  $queryBuilder->createNamedParameter($chunk, Connection::PARAM_STR_ARRAY)
302  )
303  )
304  ->execute();
305  }
306  }
307  }
308  }
309 
310  return $result;
311  }
312 
321  public function generateRefIndexData($tableName, $uid)
322  {
323  if (!isset($GLOBALS['TCA'][$tableName])) {
324  return null;
325  }
326 
327  $this->relations = [];
328 
329  $record = null;
330  $uid = $uid ? (int)$uid : 0;
331  if ($uid) {
332  // Get raw record from DB
333  $record = $this->getRecordRawCached($tableName, $uid);
334  }
335 
336  if (!is_array($record)) {
337  return null;
338  }
339 
340  return $this->generateDataUsingRecord($tableName, $record);
341  }
342 
350  protected function generateDataUsingRecord(string $tableName, array $record): array
351  {
352  $this->relations = [];
353  $deleteField = $GLOBALS['TCA'][$tableName]['ctrl']['delete'];
354 
355  // Is the record deleted?
356  $deleted = $deleteField && $record[$deleteField] ? 1 : 0;
357 
358  // Get all relations from record:
359  $recordRelations = $this->getRelations($tableName, $record);
360  // Traverse those relations, compile records to insert in table:
361  foreach ($recordRelations as $fieldName => $fieldRelations) {
362  // Based on type
363  switch ((string)$fieldRelations['type']) {
364  case 'db':
365  $this->createEntryDataForDatabaseRelationsUsingRecord($tableName, $record, $fieldName, '', $deleted, $fieldRelations['itemArray']);
366  break;
367  case 'file_reference':
368  // not used (see getRelations()), but fallback to file
369  case 'file':
370  $this->createEntryDataForFileRelationsUsingRecord($tableName, $record, $fieldName, '', $deleted, $fieldRelations['newValueFiles']);
371  break;
372  case 'flex':
373  // DB references in FlexForms
374  if (is_array($fieldRelations['flexFormRels']['db'])) {
375  foreach ($fieldRelations['flexFormRels']['db'] as $flexPointer => $subList) {
376  $this->createEntryDataForDatabaseRelationsUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, $subList);
377  }
378  }
379  // File references in FlexForms
380  // @todo #65463 Test correct handling of file references in FlexForms
381  if (is_array($fieldRelations['flexFormRels']['file'])) {
382  foreach ($fieldRelations['flexFormRels']['file'] as $flexPointer => $subList) {
383  $this->createEntryDataForFileRelationsUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, $subList);
384  }
385  }
386  // Soft references in FlexForms
387  // @todo #65464 Test correct handling of soft references in FlexForms
388  if (is_array($fieldRelations['flexFormRels']['softrefs'])) {
389  foreach ($fieldRelations['flexFormRels']['softrefs'] as $flexPointer => $subList) {
390  $this->createEntryDataForSoftReferencesUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, $subList['keys']);
391  }
392  }
393  break;
394  }
395  // Soft references in the field
396  if (is_array($fieldRelations['softrefs'])) {
397  $this->createEntryDataForSoftReferencesUsingRecord($tableName, $record, $fieldName, '', $deleted, $fieldRelations['softrefs']['keys']);
398  }
399  }
400 
401  return array_filter($this->relations);
402  }
403 
421  public function createEntryData($table, $uid, $field, $flexPointer, $deleted, $ref_table, $ref_uid, $ref_string = '', $sort = -1, $softref_key = '', $softref_id = '')
422  {
423  $uid = $uid ? (int)$uid : 0;
424  if (!$uid) {
425  return null;
426  }
427  return $this->createEntryDataUsingRecord(
428  (string)$table,
429  $this->getRecordRawCached($table, $uid),
430  (string)$field,
431  (string)$flexPointer,
432  $deleted ? (int)$deleted : 0,
433  (string)$ref_table,
434  $ref_uid ? (int)$ref_uid : 0,
435  (string)$ref_string,
436  $sort ? (int)$sort : 0,
437  (string)$softref_key,
438  (string)$softref_id
439  );
440  }
441 
458  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 = '')
459  {
460  $workspaceId = 0;
461  if (BackendUtility::isTableWorkspaceEnabled($tableName)) {
462  $workspaceId = $this->getWorkspaceId();
463  if (isset($record['t3ver_wsid']) && (int)$record['t3ver_wsid'] !== $workspaceId) {
464  // The given record is workspace-enabled but doesn't live in the selected workspace => don't add index as it's not actually there
465  return false;
466  }
467  }
468  return [
469  'tablename' => $tableName,
470  'recuid' => $record['uid'],
471  'field' => $fieldName,
472  'flexpointer' => $flexPointer,
473  'softref_key' => $softReferenceKey,
474  'softref_id' => $softReferenceId,
475  'sorting' => $sort,
476  'deleted' => (int)$deleted,
477  'workspace' => $workspaceId,
478  'ref_table' => $referencedTable,
479  'ref_uid' => $referencedUid,
480  'ref_string' => mb_substr($referenceString, 0, 1024)
481  ];
482  }
483 
494  public function createEntryData_dbRels($table, $uid, $fieldName, $flexPointer, $deleted, $items)
495  {
496  $uid = $uid ? (int)$uid : 0;
497  if (!$uid) {
498  return;
499  }
501  (string)$table,
502  $this->getRecordRawCached($table, $uid),
503  (string)$fieldName,
504  (string)$flexPointer,
505  $deleted ? (int)$deleted : 0,
506  (array)$items
507  );
508  }
509 
520  protected function createEntryDataForDatabaseRelationsUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, int $deleted, array $items)
521  {
522  foreach ($items as $sort => $i) {
523  $this->relations[] = $this->createEntryDataUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, $i['table'], $i['id'], '', $sort);
524  }
525  }
526 
537  public function createEntryData_fileRels($table, $uid, $fieldName, $flexPointer, $deleted, $items)
538  {
539  $uid = $uid ? (int)$uid : 0;
540  if (!$uid) {
541  return;
542  }
544  (string)$table,
545  $this->getRecordRawCached($table, $uid),
546  (string)$fieldName,
547  (string)$flexPointer,
548  $deleted ? (int)$deleted : 0,
549  (array)$items
550  );
551  }
552 
563  protected function createEntryDataForFileRelationsUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, int $deleted, array $items)
564  {
565  foreach ($items as $sort => $i) {
566  $filePath = $i['ID_absFile'];
567  if (GeneralUtility::isFirstPartOfStr($filePath, PATH_site)) {
568  $filePath = PathUtility::stripPathSitePrefix($filePath);
569  }
570  $this->relations[] = $this->createEntryDataUsingRecord(
571  $tableName,
572  $record,
573  $fieldName,
574  $flexPointer,
575  $deleted,
576  '_FILE',
577  0,
578  $filePath,
579  $sort
580  );
581  }
582  }
583 
594  public function createEntryData_softreferences($table, $uid, $fieldName, $flexPointer, $deleted, $keys)
595  {
596  $uid = $uid ? (int)$uid : 0;
597  if (!$uid || !is_array($keys)) {
598  return;
599  }
601  (string)$table,
602  $this->getRecordRawCached($table, $uid),
603  (string)$fieldName,
604  (string)$flexPointer,
605  $deleted ? (int)$deleted : 0,
606  (array)$keys
607  );
608  }
609 
620  protected function createEntryDataForSoftReferencesUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, int $deleted, array $keys)
621  {
622  foreach ($keys as $spKey => $elements) {
623  if (is_array($elements)) {
624  foreach ($elements as $subKey => $el) {
625  if (is_array($el['subst'])) {
626  switch ((string)$el['subst']['type']) {
627  case 'db':
628  list($referencedTable, $referencedUid) = explode(':', $el['subst']['recordRef']);
629  $this->relations[] = $this->createEntryDataUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, $referencedTable, $referencedUid, '', -1, $spKey, $subKey);
630  break;
631  case 'file_reference':
632  // not used (see getRelations()), but fallback to file
633  case 'file':
634  $this->relations[] = $this->createEntryDataUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, '_FILE', 0, $el['subst']['relFileName'], -1, $spKey, $subKey);
635  break;
636  case 'string':
637  $this->relations[] = $this->createEntryDataUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, '_STRING', 0, $el['subst']['tokenValue'], -1, $spKey, $subKey);
638  break;
639  }
640  }
641  }
642  }
643  }
644  }
645 
646  /*******************************
647  *
648  * Get relations from table row
649  *
650  *******************************/
651 
663  public function getRelations($table, $row, $onlyField = '')
664  {
665  // Initialize:
666  $uid = $row['uid'];
667  $outRow = [];
668  foreach ($row as $field => $value) {
669  if ($this->shouldExcludeTableColumnFromReferenceIndex($table, $field, $onlyField) === false) {
670  $conf = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
671  // Add files
672  $resultsFromFiles = $this->getRelations_procFiles($value, $conf, $uid);
673  if (!empty($resultsFromFiles)) {
674  // We have to fill different arrays here depending on the result.
675  // internal_type file is still a relation of type file and
676  // since http://forge.typo3.org/issues/49538 internal_type file_reference
677  // is a database relation to a sys_file record
678  $fileResultsFromFiles = [];
679  $dbResultsFromFiles = [];
680  foreach ($resultsFromFiles as $resultFromFiles) {
681  if (isset($resultFromFiles['table']) && $resultFromFiles['table'] === 'sys_file') {
682  $dbResultsFromFiles[] = $resultFromFiles;
683  } else {
684  // Creates an entry for the field with all the files:
685  $fileResultsFromFiles[] = $resultFromFiles;
686  }
687  }
688  if (!empty($fileResultsFromFiles)) {
689  $outRow[$field] = [
690  'type' => 'file',
691  'newValueFiles' => $fileResultsFromFiles
692  ];
693  }
694  if (!empty($dbResultsFromFiles)) {
695  $outRow[$field] = [
696  'type' => 'db',
697  'itemArray' => $dbResultsFromFiles
698  ];
699  }
700  }
701  // Add a softref definition for link fields if the TCA does not specify one already
702  if ($conf['type'] === 'input' && $conf['renderType'] === 'inputLink' && empty($conf['softref'])) {
703  $conf['softref'] = 'typolink';
704  }
705  // Add DB:
706  $resultsFromDatabase = $this->getRelations_procDB($value, $conf, $uid, $table);
707  if (!empty($resultsFromDatabase)) {
708  // Create an entry for the field with all DB relations:
709  $outRow[$field] = [
710  'type' => 'db',
711  'itemArray' => $resultsFromDatabase
712  ];
713  }
714  // For "flex" fieldtypes we need to traverse the structure looking for file and db references of course!
715  if ($conf['type'] === 'flex') {
716  // Get current value array:
717  // NOTICE: failure to resolve Data Structures can lead to integrity problems with the reference index. Please look up
718  // the note in the JavaDoc documentation for the function FlexFormTools->getDataStructureIdentifier()
719  $currentValueArray = GeneralUtility::xml2array($value);
720  // Traversing the XML structure, processing files:
721  if (is_array($currentValueArray)) {
722  $this->temp_flexRelations = [
723  'db' => [],
724  'file' => [],
725  'softrefs' => []
726  ];
727  // Create and call iterator object:
728  $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
729  $flexFormTools->traverseFlexFormXMLData($table, $field, $row, $this, 'getRelations_flexFormCallBack');
730  // Create an entry for the field:
731  $outRow[$field] = [
732  'type' => 'flex',
733  'flexFormRels' => $this->temp_flexRelations
734  ];
735  }
736  }
737  // Soft References:
738  if ((string)$value !== '') {
739  $softRefValue = $value;
740  if (!empty($conf['softref'])) {
741  $softRefs = BackendUtility::explodeSoftRefParserList($conf['softref']);
742  foreach ($softRefs as $spKey => $spParams) {
743  $softRefObj = BackendUtility::softRefParserObj($spKey);
744  if (is_object($softRefObj)) {
745  $resultArray = $softRefObj->findRef($table, $field, $uid, $softRefValue, $spKey, $spParams);
746  if (is_array($resultArray)) {
747  $outRow[$field]['softrefs']['keys'][$spKey] = $resultArray['elements'];
748  if ((string)$resultArray['content'] !== '') {
749  $softRefValue = $resultArray['content'];
750  }
751  }
752  }
753  }
754  }
755  if (!empty($outRow[$field]['softrefs']) && (string)$value !== (string)$softRefValue && strpos($softRefValue, '{softref:') !== false) {
756  $outRow[$field]['softrefs']['tokenizedContent'] = $softRefValue;
757  }
758  }
759  }
760  }
761  return $outRow;
762  }
763 
773  public function getRelations_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath)
774  {
775  // Removing "data/" in the beginning of path (which points to location in data array)
776  $structurePath = substr($structurePath, 5) . '/';
777  $dsConf = $dsArr['TCEforms']['config'];
778  // Implode parameter values:
779  list($table, $uid, $field) = [
780  $PA['table'],
781  $PA['uid'],
782  $PA['field']
783  ];
784  // Add files
785  $resultsFromFiles = $this->getRelations_procFiles($dataValue, $dsConf, $uid);
786  if (!empty($resultsFromFiles)) {
787  // We have to fill different arrays here depending on the result.
788  // internal_type file is still a relation of type file and
789  // since http://forge.typo3.org/issues/49538 internal_type file_reference
790  // is a database relation to a sys_file record
791  $fileResultsFromFiles = [];
792  $dbResultsFromFiles = [];
793  foreach ($resultsFromFiles as $resultFromFiles) {
794  if (isset($resultFromFiles['table']) && $resultFromFiles['table'] === 'sys_file') {
795  $dbResultsFromFiles[] = $resultFromFiles;
796  } else {
797  $fileResultsFromFiles[] = $resultFromFiles;
798  }
799  }
800  if (!empty($fileResultsFromFiles)) {
801  $this->temp_flexRelations['file'][$structurePath] = $fileResultsFromFiles;
802  }
803  if (!empty($dbResultsFromFiles)) {
804  $this->temp_flexRelations['db'][$structurePath] = $dbResultsFromFiles;
805  }
806  }
807  // Add a softref definition for link fields if the TCA does not specify one already
808  if ($dsConf['type'] === 'input' && $dsConf['renderType'] === 'inputLink' && empty($dsConf['softref'])) {
809  $dsConf['softref'] = 'typolink';
810  }
811  // Add DB:
812  $resultsFromDatabase = $this->getRelations_procDB($dataValue, $dsConf, $uid, $table);
813  if (!empty($resultsFromDatabase)) {
814  // Create an entry for the field with all DB relations:
815  $this->temp_flexRelations['db'][$structurePath] = $resultsFromDatabase;
816  }
817  // Soft References:
818  if (is_array($dataValue) || (string)$dataValue !== '') {
819  $softRefValue = $dataValue;
820  $softRefs = BackendUtility::explodeSoftRefParserList($dsConf['softref']);
821  if ($softRefs !== false) {
822  foreach ($softRefs as $spKey => $spParams) {
823  $softRefObj = BackendUtility::softRefParserObj($spKey);
824  if (is_object($softRefObj)) {
825  $resultArray = $softRefObj->findRef($table, $field, $uid, $softRefValue, $spKey, $spParams, $structurePath);
826  if (is_array($resultArray) && is_array($resultArray['elements'])) {
827  $this->temp_flexRelations['softrefs'][$structurePath]['keys'][$spKey] = $resultArray['elements'];
828  if ((string)$resultArray['content'] !== '') {
829  $softRefValue = $resultArray['content'];
830  }
831  }
832  }
833  }
834  }
835  if (!empty($this->temp_flexRelations['softrefs']) && (string)$dataValue !== (string)$softRefValue) {
836  $this->temp_flexRelations['softrefs'][$structurePath]['tokenizedContent'] = $softRefValue;
837  }
838  }
839  }
840 
849  public function getRelations_procFiles($value, $conf, $uid)
850  {
851  if ($conf['type'] !== 'group' || ($conf['internal_type'] !== 'file' && $conf['internal_type'] !== 'file_reference')) {
852  return false;
853  }
854 
855  // Collect file values in array:
856  if ($conf['MM']) {
857  $theFileValues = [];
858  $dbAnalysis = GeneralUtility::makeInstance(RelationHandler::class);
859  $dbAnalysis->start('', 'files', $conf['MM'], $uid);
860  foreach ($dbAnalysis->itemArray as $someval) {
861  if ($someval['id']) {
862  $theFileValues[] = $someval['id'];
863  }
864  }
865  } else {
866  $theFileValues = explode(',', $value);
867  }
868  // Traverse the files and add them:
869  $uploadFolder = $conf['internal_type'] === 'file' ? $conf['uploadfolder'] : '';
870  $destinationFolder = $this->destPathFromUploadFolder($uploadFolder);
871  $newValueFiles = [];
872  foreach ($theFileValues as $file) {
873  if (trim($file)) {
874  $realFile = $destinationFolder . '/' . trim($file);
875  $newValueFile = [
876  'filename' => basename($file),
877  'ID' => md5($realFile),
878  'ID_absFile' => $realFile
879  ];
880  // Set sys_file and id for referenced files
881  if ($conf['internal_type'] === 'file_reference') {
882  try {
883  $file = ResourceFactory::getInstance()->retrieveFileOrFolderObject($file);
884  if ($file instanceof File || $file instanceof Folder) {
885  // For setting this as sys_file relation later, the keys filename, ID and ID_absFile
886  // have not to be included, because the are not evaluated for db relations.
887  $newValueFile = [
888  'table' => 'sys_file',
889  'id' => $file->getUid()
890  ];
891  }
892  } catch (\Exception $e) {
893  }
894  }
895  $newValueFiles[] = $newValueFile;
896  }
897  }
898  return $newValueFiles;
899  }
900 
910  public function getRelations_procDB($value, $conf, $uid, $table = '')
911  {
912  // Get IRRE relations
913  if (empty($conf)) {
914  return false;
915  }
916  if ($conf['type'] === 'inline' && !empty($conf['foreign_table']) && empty($conf['MM'])) {
917  $dbAnalysis = GeneralUtility::makeInstance(RelationHandler::class);
918  $dbAnalysis->setUseLiveReferenceIds(false);
919  $dbAnalysis->start($value, $conf['foreign_table'], '', $uid, $table, $conf);
920  return $dbAnalysis->itemArray;
921  // DB record lists:
922  }
923  if ($this->isDbReferenceField($conf)) {
924  $allowedTables = $conf['type'] === 'group' ? $conf['allowed'] : $conf['foreign_table'];
925  if ($conf['MM_opposite_field']) {
926  return [];
927  }
928  $dbAnalysis = GeneralUtility::makeInstance(RelationHandler::class);
929  $dbAnalysis->start($value, $allowedTables, $conf['MM'], $uid, $table, $conf);
930  return $dbAnalysis->itemArray;
931  }
932  return false;
933  }
934 
935  /*******************************
936  *
937  * Setting values
938  *
939  *******************************/
940 
960  public function setReferenceValue($hash, $newValue, $returnDataArray = false, $bypassWorkspaceAdminCheck = false)
961  {
962  $backendUser = $this->getBackendUser();
963  if ($backendUser->workspace === 0 && $backendUser->isAdmin() || $bypassWorkspaceAdminCheck) {
964  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
965  $queryBuilder->getRestrictions()->removeAll();
966 
967  // Get current index from Database
968  $referenceRecord = $queryBuilder
969  ->select('*')
970  ->from('sys_refindex')
971  ->where(
972  $queryBuilder->expr()->eq('hash', $queryBuilder->createNamedParameter($hash, \PDO::PARAM_STR))
973  )
974  ->setMaxResults(1)
975  ->execute()
976  ->fetch();
977 
978  // Check if reference existed.
979  if (!is_array($referenceRecord)) {
980  return 'ERROR: No reference record with hash="' . $hash . '" was found!';
981  }
982 
983  if (empty($GLOBALS['TCA'][$referenceRecord['tablename']])) {
984  return 'ERROR: Table "' . $referenceRecord['tablename'] . '" was not in TCA!';
985  }
986 
987  // Get that record from database
988  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
989  ->getQueryBuilderForTable($referenceRecord['tablename']);
990  $queryBuilder->getRestrictions()->removeAll();
991  $record = $queryBuilder
992  ->select('*')
993  ->from($referenceRecord['tablename'])
994  ->where(
995  $queryBuilder->expr()->eq(
996  'uid',
997  $queryBuilder->createNamedParameter($referenceRecord['recuid'], \PDO::PARAM_INT)
998  )
999  )
1000  ->setMaxResults(1)
1001  ->execute()
1002  ->fetch();
1003 
1004  if (is_array($record)) {
1005  // Get relation for single field from record
1006  $recordRelations = $this->getRelations($referenceRecord['tablename'], $record, $referenceRecord['field']);
1007  if ($fieldRelation = $recordRelations[$referenceRecord['field']]) {
1008  // Initialize data array that is to be sent to DataHandler afterwards:
1009  $dataArray = [];
1010  // Based on type
1011  switch ((string)$fieldRelation['type']) {
1012  case 'db':
1013  $error = $this->setReferenceValue_dbRels($referenceRecord, $fieldRelation['itemArray'], $newValue, $dataArray);
1014  if ($error) {
1015  return $error;
1016  }
1017  break;
1018  case 'file_reference':
1019  // not used (see getRelations()), but fallback to file
1020  case 'file':
1021  $error = $this->setReferenceValue_fileRels($referenceRecord, $fieldRelation['newValueFiles'], $newValue, $dataArray);
1022  if ($error) {
1023  return $error;
1024  }
1025  break;
1026  case 'flex':
1027  // DB references in FlexForms
1028  if (is_array($fieldRelation['flexFormRels']['db'][$referenceRecord['flexpointer']])) {
1029  $error = $this->setReferenceValue_dbRels($referenceRecord, $fieldRelation['flexFormRels']['db'][$referenceRecord['flexpointer']], $newValue, $dataArray, $referenceRecord['flexpointer']);
1030  if ($error) {
1031  return $error;
1032  }
1033  }
1034  // File references in FlexForms
1035  if (is_array($fieldRelation['flexFormRels']['file'][$referenceRecord['flexpointer']])) {
1036  $error = $this->setReferenceValue_fileRels($referenceRecord, $fieldRelation['flexFormRels']['file'][$referenceRecord['flexpointer']], $newValue, $dataArray, $referenceRecord['flexpointer']);
1037  if ($error) {
1038  return $error;
1039  }
1040  }
1041  // Soft references in FlexForms
1042  if ($referenceRecord['softref_key'] && is_array($fieldRelation['flexFormRels']['softrefs'][$referenceRecord['flexpointer']]['keys'][$referenceRecord['softref_key']])) {
1043  $error = $this->setReferenceValue_softreferences($referenceRecord, $fieldRelation['flexFormRels']['softrefs'][$referenceRecord['flexpointer']], $newValue, $dataArray, $referenceRecord['flexpointer']);
1044  if ($error) {
1045  return $error;
1046  }
1047  }
1048  break;
1049  }
1050  // Soft references in the field:
1051  if ($referenceRecord['softref_key'] && is_array($fieldRelation['softrefs']['keys'][$referenceRecord['softref_key']])) {
1052  $error = $this->setReferenceValue_softreferences($referenceRecord, $fieldRelation['softrefs'], $newValue, $dataArray);
1053  if ($error) {
1054  return $error;
1055  }
1056  }
1057  // Data Array, now ready to be sent to DataHandler
1058  if ($returnDataArray) {
1059  return $dataArray;
1060  }
1061  // Execute CMD array:
1062  $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
1063  $dataHandler->dontProcessTransformations = true;
1064  $dataHandler->bypassWorkspaceRestrictions = true;
1065  $dataHandler->bypassFileHandling = true;
1066  // Otherwise this cannot update things in deleted records...
1067  $dataHandler->bypassAccessCheckForRecords = true;
1068  // Check has been done previously that there is a backend user which is Admin and also in live workspace
1069  $dataHandler->start($dataArray, []);
1070  $dataHandler->process_datamap();
1071  // Return errors if any:
1072  if (!empty($dataHandler->errorLog)) {
1073  return LF . 'DataHandler:' . implode((LF . 'DataHandler:'), $dataHandler->errorLog);
1074  }
1075  }
1076  }
1077  } else {
1078  return 'ERROR: BE_USER object is not admin OR not in workspace 0 (Live)';
1079  }
1080 
1081  return false;
1082  }
1083 
1094  public function setReferenceValue_dbRels($refRec, $itemArray, $newValue, &$dataArray, $flexPointer = '')
1095  {
1096  if ((int)$itemArray[$refRec['sorting']]['id'] === (int)$refRec['ref_uid'] && (string)$itemArray[$refRec['sorting']]['table'] === (string)$refRec['ref_table']) {
1097  // Setting or removing value:
1098  // Remove value:
1099  if ($newValue === null) {
1100  unset($itemArray[$refRec['sorting']]);
1101  } else {
1102  list($itemArray[$refRec['sorting']]['table'], $itemArray[$refRec['sorting']]['id']) = explode(':', $newValue);
1103  }
1104  // Traverse and compile new list of records:
1105  $saveValue = [];
1106  foreach ($itemArray as $pair) {
1107  $saveValue[] = $pair['table'] . '_' . $pair['id'];
1108  }
1109  // Set in data array:
1110  if ($flexPointer) {
1111  $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
1112  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'] = [];
1113  $flexFormTools->setArrayValueByPath(substr($flexPointer, 0, -1), $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'], implode(',', $saveValue));
1114  } else {
1115  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']] = implode(',', $saveValue);
1116  }
1117  } else {
1118  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'] . '"';
1119  }
1120 
1121  return false;
1122  }
1123 
1134  public function setReferenceValue_fileRels($refRec, $itemArray, $newValue, &$dataArray, $flexPointer = '')
1135  {
1136  $ID_absFile = PathUtility::stripPathSitePrefix($itemArray[$refRec['sorting']]['ID_absFile']);
1137  if ($ID_absFile === (string)$refRec['ref_string'] && $refRec['ref_table'] === '_FILE') {
1138  // Setting or removing value:
1139  // Remove value:
1140  if ($newValue === null) {
1141  unset($itemArray[$refRec['sorting']]);
1142  } else {
1143  $itemArray[$refRec['sorting']]['filename'] = $newValue;
1144  }
1145  // Traverse and compile new list of records:
1146  $saveValue = [];
1147  foreach ($itemArray as $fileInfo) {
1148  $saveValue[] = $fileInfo['filename'];
1149  }
1150  // Set in data array:
1151  if ($flexPointer) {
1152  $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
1153  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'] = [];
1154  $flexFormTools->setArrayValueByPath(substr($flexPointer, 0, -1), $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'], implode(',', $saveValue));
1155  } else {
1156  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']] = implode(',', $saveValue);
1157  }
1158  } else {
1159  return 'ERROR: either "' . $refRec['ref_table'] . '" was not "_FILE" or file PATH_site+"' . $refRec['ref_string'] . '" did not match that of the record ("' . $itemArray[$refRec['sorting']]['ID_absFile'] . '") in sorting index "' . $refRec['sorting'] . '"';
1160  }
1161 
1162  return false;
1163  }
1164 
1175  public function setReferenceValue_softreferences($refRec, $softref, $newValue, &$dataArray, $flexPointer = '')
1176  {
1177  if (!is_array($softref['keys'][$refRec['softref_key']][$refRec['softref_id']])) {
1178  return 'ERROR: Soft reference parser key "' . $refRec['softref_key'] . '" or the index "' . $refRec['softref_id'] . '" was not found.';
1179  }
1180 
1181  // Set new value:
1182  $softref['keys'][$refRec['softref_key']][$refRec['softref_id']]['subst']['tokenValue'] = '' . $newValue;
1183  // Traverse softreferences and replace in tokenized content to rebuild it with new value inside:
1184  foreach ($softref['keys'] as $sfIndexes) {
1185  foreach ($sfIndexes as $data) {
1186  $softref['tokenizedContent'] = str_replace('{softref:' . $data['subst']['tokenID'] . '}', $data['subst']['tokenValue'], $softref['tokenizedContent']);
1187  }
1188  }
1189  // Set in data array:
1190  if (!strstr($softref['tokenizedContent'], '{softref:')) {
1191  if ($flexPointer) {
1192  $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
1193  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'] = [];
1194  $flexFormTools->setArrayValueByPath(substr($flexPointer, 0, -1), $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'], $softref['tokenizedContent']);
1195  } else {
1196  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']] = $softref['tokenizedContent'];
1197  }
1198  } else {
1199  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.)';
1200  }
1201 
1202  return false;
1203  }
1204 
1205  /*******************************
1206  *
1207  * Helper functions
1208  *
1209  *******************************/
1210 
1217  protected function isDbReferenceField(array $configuration)
1218  {
1219  return
1220  ($configuration['type'] === 'group' && $configuration['internal_type'] === 'db')
1221  || (
1222  ($configuration['type'] === 'select' || $configuration['type'] === 'inline')
1223  && !empty($configuration['foreign_table'])
1224  )
1225  ;
1226  }
1227 
1234  public function isReferenceField(array $configuration)
1235  {
1236  return
1237  $this->isDbReferenceField($configuration)
1238  ||
1239  ($configuration['type'] === 'group' && ($configuration['internal_type'] === 'file' || $configuration['internal_type'] === 'file_reference')) // getRelations_procFiles
1240  ||
1241  ($configuration['type'] === 'input' && $configuration['renderType'] === 'inputLink') // getRelations_procDB
1242  ||
1243  $configuration['type'] === 'flex'
1244  ||
1245  isset($configuration['softref'])
1246  ;
1247  }
1248 
1255  protected function fetchTableRelationFields($tableName)
1256  {
1257  if (!isset($GLOBALS['TCA'][$tableName]['columns'])) {
1258  return '';
1259  }
1260 
1261  $fields = [];
1262 
1263  foreach ($GLOBALS['TCA'][$tableName]['columns'] as $field => $fieldDefinition) {
1264  if (is_array($fieldDefinition['config'])) {
1265  // Check for flex field
1266  if (isset($fieldDefinition['config']['type']) && $fieldDefinition['config']['type'] === 'flex') {
1267  // Fetch all fields if the is a field of type flex in the table definition because the complete row is passed to
1268  // FlexFormTools->getDataStructureIdentifier() in the end and might be needed in ds_pointerField or a hook
1269  return '*';
1270  }
1271  // Only fetch this field if it can contain a reference
1272  if ($this->isReferenceField($fieldDefinition['config'])) {
1273  $fields[] = $field;
1274  }
1275  }
1276  }
1277 
1278  return implode(',', $fields);
1279  }
1280 
1287  public function destPathFromUploadFolder($folder)
1288  {
1289  if (!$folder) {
1290  return substr(PATH_site, 0, -1);
1291  }
1292  return PATH_site . $folder;
1293  }
1294 
1302  public function updateIndex($testOnly, $cli_echo = false)
1303  {
1304  $errors = [];
1305  $tableNames = [];
1306  $recCount = 0;
1307  $tableCount = 0;
1308  $headerContent = $testOnly ? 'Reference Index being TESTED (nothing written, remove the "--check" argument)' : 'Reference Index being Updated';
1309  if ($cli_echo) {
1310  echo '*******************************************' . LF . $headerContent . LF . '*******************************************' . LF;
1311  }
1312  // Traverse all tables:
1313  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
1314  $refIndexConnectionName = empty($GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping']['sys_refindex'])
1316  : $GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping']['sys_refindex'];
1317 
1318  foreach ($GLOBALS['TCA'] as $tableName => $cfg) {
1319  if ($this->shouldExcludeTableFromReferenceIndex($tableName)) {
1320  continue;
1321  }
1322  $tableConnectionName = empty($GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'][$tableName])
1324  : $GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'][$tableName];
1325 
1326  $fields = ['uid'];
1327  if (BackendUtility::isTableWorkspaceEnabled($tableName)) {
1328  $fields[] = 't3ver_wsid';
1329  }
1330  // Traverse all records in tables, including deleted records
1331  $queryBuilder = $connectionPool->getQueryBuilderForTable($tableName);
1332  $queryBuilder->getRestrictions()->removeAll();
1333  try {
1334  $queryResult = $queryBuilder
1335  ->select(...$fields)
1336  ->from($tableName)
1337  ->execute();
1338  } catch (DBALException $e) {
1339  // Table exists in $TCA but does not exist in the database
1340  // @todo: improve / change message and add actual sql error?
1341  GeneralUtility::sysLog(sprintf('Table "%s" exists in $TCA but does not exist in the database. You should run the Database Analyzer in the Install Tool to fix this.', $tableName), 'core', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1342  continue;
1343  }
1344 
1345  $tableNames[] = $tableName;
1346  $tableCount++;
1347  while ($record = $queryResult->fetch()) {
1348  $refIndexObj = GeneralUtility::makeInstance(self::class);
1349  if (isset($record['t3ver_wsid'])) {
1350  $refIndexObj->setWorkspaceId($record['t3ver_wsid']);
1351  }
1352  $result = $refIndexObj->updateRefIndexTable($tableName, $record['uid'], $testOnly);
1353  $recCount++;
1354  if ($result['addedNodes'] || $result['deletedNodes']) {
1355  $error = 'Record ' . $tableName . ':' . $record['uid'] . ' had ' . $result['addedNodes'] . ' added indexes and ' . $result['deletedNodes'] . ' deleted indexes';
1356  $errors[] = $error;
1357  if ($cli_echo) {
1358  echo $error . LF;
1359  }
1360  }
1361  }
1362 
1363  // Subselect based queries only work on the same connection
1364  if ($refIndexConnectionName !== $tableConnectionName) {
1365  GeneralUtility::sysLog(
1366  sprintf(
1367  'Not checking table "%s" for lost indexes, "sys_refindex" table uses a different connection',
1368  $tableName
1369  ),
1370  'core',
1372  );
1373  continue;
1374  }
1375 
1376  // Searching for lost indexes for this table
1377  // Build sub-query to find lost records
1378  $subQueryBuilder = $connectionPool->getQueryBuilderForTable($tableName);
1379  $subQueryBuilder->getRestrictions()->removeAll();
1380  $subQueryBuilder
1381  ->select('uid')
1382  ->from($tableName, 'sub_' . $tableName)
1383  ->where(
1384  $subQueryBuilder->expr()->eq(
1385  'sub_' . $tableName . '.uid',
1386  $queryBuilder->quoteIdentifier('sys_refindex.recuid')
1387  )
1388  );
1389 
1390  // Main query to find lost records
1391  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_refindex');
1392  $queryBuilder->getRestrictions()->removeAll();
1393  $lostIndexes = $queryBuilder
1394  ->count('hash')
1395  ->from('sys_refindex')
1396  ->where(
1397  $queryBuilder->expr()->eq(
1398  'tablename',
1399  $queryBuilder->createNamedParameter($tableName, \PDO::PARAM_STR)
1400  ),
1401  'NOT EXISTS (' . $subQueryBuilder->getSQL() . ')'
1402  )
1403  ->execute()
1404  ->fetchColumn(0);
1405 
1406  if ($lostIndexes > 0) {
1407  $error = 'Table ' . $tableName . ' has ' . $lostIndexes . ' lost indexes which are now deleted';
1408  $errors[] = $error;
1409  if ($cli_echo) {
1410  echo $error . LF;
1411  }
1412  if (!$testOnly) {
1413  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_refindex');
1414  $queryBuilder->delete('sys_refindex')
1415  ->where(
1416  $queryBuilder->expr()->eq(
1417  'tablename',
1418  $queryBuilder->createNamedParameter($tableName, \PDO::PARAM_STR)
1419  ),
1420  'NOT EXISTS (' . $subQueryBuilder->getSQL() . ')'
1421  )
1422  ->execute();
1423  }
1424  }
1425  }
1426 
1427  // Searching lost indexes for non-existing tables
1428  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_refindex');
1429  $queryBuilder->getRestrictions()->removeAll();
1430  $lostTables = $queryBuilder
1431  ->count('hash')
1432  ->from('sys_refindex')
1433  ->where(
1434  $queryBuilder->expr()->notIn(
1435  'tablename',
1436  $queryBuilder->createNamedParameter($tableNames, Connection::PARAM_STR_ARRAY)
1437  )
1438  )->execute()
1439  ->fetchColumn(0);
1440 
1441  if ($lostTables > 0) {
1442  $error = 'Index table hosted ' . $lostTables . ' indexes for non-existing tables, now removed';
1443  $errors[] = $error;
1444  if ($cli_echo) {
1445  echo $error . LF;
1446  }
1447  if (!$testOnly) {
1448  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_refindex');
1449  $queryBuilder->delete('sys_refindex')
1450  ->where(
1451  $queryBuilder->expr()->notIn(
1452  'tablename',
1453  $queryBuilder->createNamedParameter($tableNames, Connection::PARAM_STR_ARRAY)
1454  )
1455  )->execute();
1456  }
1457  }
1458  $errorCount = count($errors);
1459  $recordsCheckedString = $recCount . ' records from ' . $tableCount . ' tables were checked/updated.' . LF;
1460  $flashMessage = GeneralUtility::makeInstance(
1461  FlashMessage::class,
1462  $errorCount ? implode('##LF##', $errors) : 'Index Integrity was perfect!',
1463  $recordsCheckedString,
1464  $errorCount ? FlashMessage::ERROR : FlashMessage::OK
1465  );
1467  $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
1469  $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
1470  $defaultFlashMessageQueue->enqueue($flashMessage);
1471  $bodyContent = $defaultFlashMessageQueue->renderFlashMessages();
1472  if ($cli_echo) {
1473  echo $recordsCheckedString . ($errorCount ? 'Updates: ' . $errorCount : 'Index Integrity was perfect!') . LF;
1474  }
1475  if (!$testOnly) {
1476  $registry = GeneralUtility::makeInstance(Registry::class);
1477  $registry->set('core', 'sys_refindex_lastUpdate', $GLOBALS['EXEC_TIME']);
1478  }
1479  return [$headerContent, $bodyContent, $errorCount];
1480  }
1481 
1502  protected function getRecordRawCached(string $tableName, int $uid)
1503  {
1504  $recordCacheId = $tableName . ':' . $uid;
1505  if (!$this->useRuntimeCache || !isset($this->recordCache[$recordCacheId])) {
1506 
1507  // Fetch fields of the table which might contain relations
1508  $cacheId = static::$cachePrefixTableRelationFields . $tableName;
1509  if (!$this->useRuntimeCache || !$this->runtimeCache->has($cacheId)) {
1510  $tableRelationFields = $this->fetchTableRelationFields($tableName);
1511  if ($this->useRuntimeCache) {
1512  $this->runtimeCache->set($cacheId, $tableRelationFields);
1513  }
1514  } else {
1515  $tableRelationFields = $this->runtimeCache->get($cacheId);
1516  }
1517 
1518  // Return if there are no fields which could contain relations
1519  if ($tableRelationFields === '') {
1520  return $this->relations;
1521  }
1522 
1523  if ($tableRelationFields === '*') {
1524  // If one field of a record is of type flex, all fields have to be fetched to be passed to FlexFormTools->getDataStructureIdentifier()
1525  $selectFields = '*';
1526  } else {
1527  // otherwise only fields that might contain relations are fetched
1528  $selectFields = 'uid,' . $tableRelationFields;
1529  $deleteField = $GLOBALS['TCA'][$tableName]['ctrl']['delete'];
1530  if ($deleteField) {
1531  $selectFields .= ',' . $deleteField;
1532  }
1533  if (BackendUtility::isTableWorkspaceEnabled($tableName)) {
1534  $selectFields .= ',t3ver_wsid';
1535  }
1536  }
1537 
1538  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1539  ->getQueryBuilderForTable($tableName);
1540  $queryBuilder->getRestrictions()->removeAll();
1541  $row = $queryBuilder
1542  ->select(...GeneralUtility::trimExplode(',', $selectFields, true))
1543  ->from($tableName)
1544  ->where(
1545  $queryBuilder->expr()->eq(
1546  'uid',
1547  $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
1548  )
1549  )
1550  ->execute()
1551  ->fetch();
1552 
1553  $this->recordCache[$recordCacheId] = $row;
1554  }
1555  return $this->recordCache[$recordCacheId];
1556  }
1557 
1564  protected function shouldExcludeTableFromReferenceIndex($tableName)
1565  {
1566  if (isset(static::$excludedTables[$tableName])) {
1567  return static::$excludedTables[$tableName];
1568  }
1569 
1570  // Only exclude tables from ReferenceIndex which do not contain any relations and never did since existing references won't be deleted!
1571  // There is no need to add tables without a definition in $GLOBALS['TCA] since ReferenceIndex only handles those.
1572  $excludeTable = false;
1574  $signalSlotDispatcher->dispatch(__CLASS__, 'shouldExcludeTableFromReferenceIndex', [$tableName, &$excludeTable]);
1575 
1576  static::$excludedTables[$tableName] = $excludeTable;
1577 
1578  return static::$excludedTables[$tableName];
1579  }
1580 
1589  protected function shouldExcludeTableColumnFromReferenceIndex($tableName, $column, $onlyColumn)
1590  {
1591  if (isset(static::$excludedColumns[$column])) {
1592  return true;
1593  }
1594 
1595  if (is_array($GLOBALS['TCA'][$tableName]['columns'][$column]) && (!$onlyColumn || $onlyColumn === $column)) {
1596  return false;
1597  }
1598 
1599  return true;
1600  }
1601 
1607  public function enableRuntimeCache()
1608  {
1609  $this->useRuntimeCache = true;
1610  }
1611 
1615  public function disableRuntimeCache()
1616  {
1617  $this->useRuntimeCache = false;
1618  }
1619 
1625  protected function getBackendUser()
1626  {
1627  return $GLOBALS['BE_USER'];
1628  }
1629 }
setReferenceValue_fileRels($refRec, $itemArray, $newValue, &$dataArray, $flexPointer='')
getRelations($table, $row, $onlyField='')
static isFirstPartOfStr($str, $partStr)
setReferenceValue_dbRels($refRec, $itemArray, $newValue, &$dataArray, $flexPointer='')
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static makeInstance($className,... $constructorArguments)
$fields
Definition: pages.php:4
createEntryDataForFileRelationsUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, int $deleted, array $items)
createEntryDataForSoftReferencesUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, int $deleted, array $keys)
getRelations_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath)
getRecordRawCached(string $tableName, int $uid)
generateDataUsingRecord(string $tableName, array $record)
$signalSlotDispatcher
createEntryData_dbRels($table, $uid, $fieldName, $flexPointer, $deleted, $items)
shouldExcludeTableColumnFromReferenceIndex($tableName, $column, $onlyColumn)
setReferenceValue($hash, $newValue, $returnDataArray=false, $bypassWorkspaceAdminCheck=false)
getRelations_procDB($value, $conf, $uid, $table='')
static xml2array($string, $NSprefix='', $reportDocTag=false)
createEntryData_fileRels($table, $uid, $fieldName, $flexPointer, $deleted, $items)
setReferenceValue_softreferences($refRec, $softref, $newValue, &$dataArray, $flexPointer='')
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
createEntryDataUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, int $deleted, string $referencedTable, int $referencedUid, string $referenceString='', int $sort=-1, string $softReferenceKey='', string $softReferenceId='')
createEntryData_softreferences($table, $uid, $fieldName, $flexPointer, $deleted, $keys)
updateRefIndexTable($tableName, $uid, $testOnly=false)
createEntryDataForDatabaseRelationsUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, int $deleted, array $items)