‪TYPO3CMS  11.5
ReferenceIndex.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
18 use Doctrine\DBAL\Exception as DBALException;
19 use Psr\EventDispatcher\EventDispatcherInterface;
20 use Psr\Log\LoggerAwareInterface;
21 use Psr\Log\LoggerAwareTrait;
22 use Psr\Log\LogLevel;
23 use TYPO3\CMS\Backend\Utility\BackendUtility;
35 
41 class ‪ReferenceIndex implements LoggerAwareInterface
42 {
43  use LoggerAwareTrait;
44 
58  protected array ‪$excludedTables = [
59  'sys_log' => true,
60  'tx_extensionmanager_domain_model_extension' => true,
61  ];
62 
73  protected array ‪$excludedColumns = [
74  'uid' => true,
75  'perms_userid' => true,
76  'perms_groupid' => true,
77  'perms_user' => true,
78  'perms_group' => true,
79  'perms_everybody' => true,
80  'pid' => true,
81  ];
82 
91  protected ‪$temp_flexRelations = [];
92 
98  protected ‪$relations = [];
99 
106  protected ‪$hashVersion = 1;
107 
111  protected int ‪$workspaceId = 0;
112 
118  protected array ‪$tableRelationFieldCache = [];
119 
120  protected EventDispatcherInterface ‪$eventDispatcher;
122 
123  public function ‪__construct(EventDispatcherInterface ‪$eventDispatcher = null, ‪SoftReferenceParserFactory ‪$softReferenceParserFactory = null)
124  {
125  $this->eventDispatcher = ‪$eventDispatcher ?? GeneralUtility::makeInstance(EventDispatcherInterface::class);
126  $this->softReferenceParserFactory = ‪$softReferenceParserFactory ?? GeneralUtility::makeInstance(SoftReferenceParserFactory::class);
127  }
128 
135  public function ‪setWorkspaceId(‪$workspaceId)
136  {
137  $this->workspaceId = (int)‪$workspaceId;
138  }
139 
146  protected function ‪getWorkspaceId()
147  {
148  return ‪$this->workspaceId;
149  }
150 
163  public function ‪updateRefIndexTable($tableName, $uid, $testOnly = false)
164  {
165  $result = [
166  'keptNodes' => 0,
167  'deletedNodes' => 0,
168  'addedNodes' => 0,
169  ];
170 
171  $uid = $uid ? (int)$uid : 0;
172  if (!$uid) {
173  return $result;
174  }
175 
176  // If this table cannot contain relations, skip it
177  if ($this->‪shouldExcludeTableFromReferenceIndex($tableName)) {
178  return $result;
179  }
180 
181  $tableRelationFields = $this->‪fetchTableRelationFields($tableName);
182 
183  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
184  $connection = $connectionPool->getConnectionForTable('sys_refindex');
185 
186  // Get current index from Database with hash as index using $uidIndexField
187  // no restrictions are needed, since sys_refindex is not a TCA table
188  $queryBuilder = $connection->createQueryBuilder();
189  $queryBuilder->getRestrictions()->removeAll();
190  $queryResult = $queryBuilder->select('hash')->from('sys_refindex')->where(
191  $queryBuilder->expr()->eq('tablename', $queryBuilder->createNamedParameter($tableName, ‪Connection::PARAM_STR)),
192  $queryBuilder->expr()->eq('recuid', $queryBuilder->createNamedParameter($uid, ‪Connection::PARAM_INT)),
193  $queryBuilder->expr()->eq(
194  'workspace',
195  $queryBuilder->createNamedParameter($this->getWorkspaceId(), ‪Connection::PARAM_INT)
196  )
197  )->executeQuery();
198  $currentRelationHashes = [];
199  while ($relation = $queryResult->fetchAssociative()) {
200  $currentRelationHashes[$relation['hash']] = true;
201  }
202 
203  // If the table has fields which could contain relations and the record does exist
204  if ($tableRelationFields !== []) {
205  $existingRecord = $this->‪getRecord($tableName, $uid);
206  if ($existingRecord) {
207  // Table has relation fields and record exists - get relations
208  $this->relations = [];
209  ‪$relations = $this->‪generateDataUsingRecord($tableName, $existingRecord);
210  if (!is_array(‪$relations)) {
211  return $result;
212  }
213  // Traverse the generated index:
214  foreach (‪$relations as &$relation) {
215  if (!is_array($relation)) {
216  continue;
217  }
218  // Exclude any relations TO a specific table
219  if (($relation['ref_table'] ?? '') && $this->‪shouldExcludeTableFromReferenceIndex($relation['ref_table'])) {
220  continue;
221  }
222  $relation['hash'] = md5(implode('
223  // First, check if already indexed and if so, unset that row (so in the end we know which rows to remove!)
224  if (isset($currentRelationHashes[$relation['hash']])) {
225  unset($currentRelationHashes[$relation['hash']]);
226  $result['keptNodes']++;
227  $relation['_ACTION'] = 'KEPT';
228  } else {
229  // If new, add it:
230  if (!$testOnly) {
231  $connection->insert('sys_refindex', $relation);
232  }
233  $result['addedNodes']++;
234  $relation['_ACTION'] = 'ADDED';
235  }
236  }
237  $result['relations'] = $relations;
238  }
239  }
240 
241  // If any old are left, remove them:
242  if (!empty($currentRelationHashes)) {
243  $hashList = array_keys($currentRelationHashes);
244  if (!empty($hashList)) {
245  $result['deletedNodes'] = count($hashList);
246  $result['deletedNodes_hashList'] = implode(',', $hashList);
247  if (!$testOnly) {
248  $maxBindParameters = PlatformInformation::getMaxBindParameters($connection->getDatabasePlatform());
249  foreach (array_chunk($hashList, $maxBindParameters - 10, true) as $chunk) {
250  if (empty($chunk)) {
251  continue;
252  }
253  $queryBuilder = $connection->createQueryBuilder();
254  $queryBuilder
255  ->delete('sys_refindex')
256  ->where(
257  $queryBuilder->expr()->in(
258  'hash',
259  $queryBuilder->createNamedParameter($chunk, Connection::PARAM_STR_ARRAY)
260  )
261  )
262  ->executeStatement();
263  }
264  }
265  }
266  }
267 
268  return $result;
269  }
270 
278  public function getNumberOfReferencedRecords(string $tableName, int $uid): int
279  {
280  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
281  return (int)$queryBuilder
282  ->count('*')->from('sys_refindex')
283  ->where(
284  $queryBuilder->expr()->eq(
285  'ref_table',
286  $queryBuilder->createNamedParameter($tableName, Connection::PARAM_STR)
287  ),
288  $queryBuilder->expr()->eq(
289  'ref_uid',
290  $queryBuilder->createNamedParameter($uid, Connection::PARAM_INT)
291  )
292  )->executeQuery()->fetchOne();
293  }
294 
302  protected function generateDataUsingRecord(string $tableName, array $record): array
303  {
304  $this->relations = [];
305  // Get all relations from record:
306  $recordRelations = $this->getRelations($tableName, $record);
307  // Traverse those relations, compile records to insert in table:
308  foreach ($recordRelations as $fieldName => $fieldRelations) {
309  // Based on type
310  switch ($fieldRelations['type'] ?? '') {
311  case 'db':
312  $this->createEntryDataForDatabaseRelationsUsingRecord($tableName, $record, $fieldName, '', $fieldRelations['itemArray']);
313  break;
314  case 'flex':
315  // DB references in FlexForms
316  if (is_array($fieldRelations['flexFormRels']['db'])) {
317  foreach ($fieldRelations['flexFormRels']['db'] as $flexPointer => $subList) {
318  $this->createEntryDataForDatabaseRelationsUsingRecord($tableName, $record, $fieldName, $flexPointer, $subList);
319  }
320  }
321  // Soft references in FlexForms
322  // @todo #65464 Test correct handling of soft references in FlexForms
323  if (is_array($fieldRelations['flexFormRels']['softrefs'])) {
324  foreach ($fieldRelations['flexFormRels']['softrefs'] as $flexPointer => $subList) {
325  $this->createEntryDataForSoftReferencesUsingRecord($tableName, $record, $fieldName, $flexPointer, $subList['keys']);
326  }
327  }
328  break;
329  }
330  // Soft references in the field
331  if (is_array($fieldRelations['softrefs']['keys'] ?? false)) {
332  $this->createEntryDataForSoftReferencesUsingRecord($tableName, $record, $fieldName, '', $fieldRelations['softrefs']['keys']);
333  }
334  }
335 
336  return array_filter($this->relations);
337  }
338 
354  protected function createEntryDataUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, string $referencedTable, int $referencedUid, string $referenceString = '', int $sort = -1, string $softReferenceKey = '', string $softReferenceId = '')
355  {
356  $currentWorkspace = $this->getWorkspaceId();
357  if (BackendUtility::isTableWorkspaceEnabled($tableName)) {
358  $fieldConfig = $GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config'];
359  if (isset($record['t3ver_wsid']) && (int)$record['t3ver_wsid'] !== $currentWorkspace && empty($fieldConfig['MM'])) {
360  // The given record is workspace-enabled but doesn't live in the selected workspace. Don't add index, it's not actually there.
361  // We still add those rows if the record is a local side live record of an MM relation and can be a target of a workspace record.
362  // See workspaces ManyToMany Modify addCategoryRelation for details on this case.
363  return false;
364  }
365  }
366  return [
367  'tablename' => $tableName,
368  'recuid' => $record['uid'],
369  'field' => $fieldName,
370  'flexpointer' => $flexPointer,
371  'softref_key' => $softReferenceKey,
372  'softref_id' => $softReferenceId,
373  'sorting' => $sort,
374  'workspace' => $currentWorkspace,
375  'ref_table' => $referencedTable,
376  'ref_uid' => $referencedUid,
377  'ref_string' => mb_substr($referenceString, 0, 1024),
378  ];
379  }
380 
390  protected function ‪createEntryDataForDatabaseRelationsUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, array $items)
391  {
392  foreach ($items as $sort => $i) {
393  $this->relations[] = $this->‪createEntryDataUsingRecord($tableName, $record, $fieldName, $flexPointer, $i['table'], (int)$i['id'], '', $sort);
394  }
395  }
396 
406  protected function ‪createEntryDataForSoftReferencesUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, array $keys)
407  {
408  foreach ($keys as $spKey => $elements) {
409  if (is_array($elements)) {
410  foreach ($elements as $subKey => $el) {
411  if (is_array($el['subst'] ?? false)) {
412  switch ((string)$el['subst']['type']) {
413  case 'db':
414  [$referencedTable, $referencedUid] = explode(':', $el['subst']['recordRef']);
415  $this->relations[] = $this->‪createEntryDataUsingRecord($tableName, $record, $fieldName, $flexPointer, $referencedTable, (int)$referencedUid, '', -1, $spKey, $subKey);
416  break;
417  case 'string':
418  $this->relations[] = $this->‪createEntryDataUsingRecord($tableName, $record, $fieldName, $flexPointer, '_STRING', 0, $el['subst']['tokenValue'], -1, $spKey, $subKey);
419  break;
420  }
421  }
422  }
423  }
424  }
425  }
426 
427  /*******************************
428  *
429  * Get relations from table row
430  *
431  *******************************/
432 
444  public function ‪getRelations($table, $row, $onlyField = '')
445  {
446  // Initialize:
447  $uid = $row['uid'];
448  $outRow = [];
449  foreach ($row as $field => $value) {
450  if ($this->‪shouldExcludeTableColumnFromReferenceIndex($table, $field, $onlyField) === false) {
451  $conf = ‪$GLOBALS['TCA'][$table]['columns'][$field]['config'];
452  // Add a softref definition for link fields if the TCA does not specify one already
453  if ($conf['type'] === 'input' && isset($conf['renderType']) && $conf['renderType'] === 'inputLink' && empty($conf['softref'])) {
454  $conf['softref'] = 'typolink';
455  }
456  // Add DB:
457  $resultsFromDatabase = $this->‪getRelations_procDB($value, $conf, $uid, $table, $row);
458  if (!empty($resultsFromDatabase)) {
459  // Create an entry for the field with all DB relations:
460  $outRow[$field] = [
461  'type' => 'db',
462  'itemArray' => $resultsFromDatabase,
463  ];
464  }
465  // For "flex" fieldtypes we need to traverse the structure looking for db references of course!
466  if ($conf['type'] === 'flex' && is_string($value) && $value !== '') {
467  // Get current value array:
468  // NOTICE: failure to resolve Data Structures can lead to integrity problems with the reference index. Please look up
469  // the note in the JavaDoc documentation for the function FlexFormTools->getDataStructureIdentifier()
470  $currentValueArray = ‪GeneralUtility::xml2array($value);
471  // Traversing the XML structure, processing:
472  if (is_array($currentValueArray)) {
473  $this->temp_flexRelations = [
474  'db' => [],
475  'softrefs' => [],
476  ];
477  // Create and call iterator object:
478  $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
479  $flexFormTools->traverseFlexFormXMLData($table, $field, $row, $this, 'getRelations_flexFormCallBack');
480  // Create an entry for the field:
481  $outRow[$field] = [
482  'type' => 'flex',
483  'flexFormRels' => ‪$this->temp_flexRelations,
484  ];
485  }
486  }
487  // Soft References:
488  if ((string)$value !== '') {
489  $softRefValue = $value;
490  if (!empty($conf['softref'])) {
491  foreach ($this->softReferenceParserFactory->getParsersBySoftRefParserList($conf['softref']) as $softReferenceParser) {
492  $parserResult = $softReferenceParser->parse($table, $field, $uid, $softRefValue);
493  if ($parserResult->hasMatched()) {
494  $outRow[$field]['softrefs']['keys'][$softReferenceParser->getParserKey()] = $parserResult->getMatchedElements();
495  if ($parserResult->hasContent()) {
496  $softRefValue = $parserResult->getContent();
497  }
498  }
499  }
500  }
501  if (!empty($outRow[$field]['softrefs']) && (string)$value !== (string)$softRefValue && str_contains($softRefValue, '{softref:')) {
502  $outRow[$field]['softrefs']['tokenizedContent'] = $softRefValue;
503  }
504  }
505  }
506  }
507  return $outRow;
508  }
509 
520  public function ‪getRelations_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath)
521  {
522  // Removing "data/" in the beginning of path (which points to location in data array)
523  $structurePath = substr($structurePath, 5) . '/';
524  $dsConf = $dsArr['TCEforms']['config'];
525  // Implode parameter values:
526  [$table, $uid, $field] = [
527  $PA['table'],
528  $PA['uid'],
529  $PA['field'],
530  ];
531  // Add a softref definition for link fields if the TCA does not specify one already
532  if (($dsConf['type'] ?? '') === 'input' && ($dsConf['renderType'] ?? '') === 'inputLink' && empty($dsConf['softref'])) {
533  $dsConf['softref'] = 'typolink';
534  }
535  // Add DB:
536  $resultsFromDatabase = $this->‪getRelations_procDB($dataValue, $dsConf, $uid, $table);
537  if (!empty($resultsFromDatabase)) {
538  // Create an entry for the field with all DB relations:
539  $this->temp_flexRelations['db'][$structurePath] = $resultsFromDatabase;
540  }
541  // Soft References:
542  if (is_array($dataValue) || (string)$dataValue !== '') {
543  $softRefValue = $dataValue;
544  foreach ($this->softReferenceParserFactory->getParsersBySoftRefParserList($dsConf['softref'] ?? '') as $softReferenceParser) {
545  $parserResult = $softReferenceParser->parse($table, $field, $uid, $softRefValue, $structurePath);
546  if ($parserResult->hasMatched()) {
547  $this->temp_flexRelations['softrefs'][$structurePath]['keys'][$softReferenceParser->getParserKey()] = $parserResult->getMatchedElements();
548  if ($parserResult->hasContent()) {
549  $softRefValue = $parserResult->getContent();
550  }
551  }
552  }
553  if (!empty($this->temp_flexRelations['softrefs']) && (string)$dataValue !== (string)$softRefValue) {
554  $this->temp_flexRelations['softrefs'][$structurePath]['tokenizedContent'] = $softRefValue;
555  }
556  }
557  }
558 
569  protected function ‪getRelations_procDB($value, $conf, $uid, $table = '', array $row = [])
570  {
571  // Get IRRE relations
572  if (empty($conf)) {
573  return false;
574  }
575  if ($conf['type'] === 'inline' && !empty($conf['foreign_table']) && empty($conf['MM'])) {
576  $dbAnalysis = GeneralUtility::makeInstance(RelationHandler::class);
577  $dbAnalysis->setUseLiveReferenceIds(false);
578  $dbAnalysis->setWorkspaceId($this->‪getWorkspaceId());
579  $dbAnalysis->start($value, $conf['foreign_table'], '', $uid, $table, $conf);
580  return $dbAnalysis->itemArray;
581  // DB record lists:
582  }
583  if ($this->‪isDbReferenceField($conf)) {
584  $allowedTables = $conf['type'] === 'group' ? $conf['allowed'] : $conf['foreign_table'];
585  if ($conf['MM_opposite_field'] ?? false) {
586  // Never handle sys_refindex when looking at MM from foreign side
587  return [];
588  }
589 
590  $dbAnalysis = GeneralUtility::makeInstance(RelationHandler::class);
591  $dbAnalysis->setWorkspaceId($this->‪getWorkspaceId());
592  $dbAnalysis->start($value, $allowedTables, $conf['MM'] ?? '', $uid, $table, $conf);
593  $itemArray = $dbAnalysis->itemArray;
594 
596  && $this->‪getWorkspaceId() > 0
597  && !empty($conf['MM'] ?? '')
598  && !empty($conf['allowed'] ?? '')
599  && empty($conf['MM_opposite_field'] ?? '')
600  && (int)($row['t3ver_wsid'] ?? 0) === 0
601  ) {
602  // When dealing with local side mm relations in workspace 0, there may be workspace records on the foreign
603  // side, for instance when those got an additional category. See ManyToMany Modify addCategoryRelations test.
604  // In those cases, the full set of relations must be written to sys_refindex as workspace rows.
605  // But, if the relations in this workspace and live are identical, no sys_refindex workspace rows
606  // have to be added.
607  $dbAnalysis = GeneralUtility::makeInstance(RelationHandler::class);
608  $dbAnalysis->setWorkspaceId(0);
609  $dbAnalysis->start($value, $allowedTables, $conf['MM'], $uid, $table, $conf);
610  $itemArrayLive = $dbAnalysis->itemArray;
611  if ($itemArrayLive === $itemArray) {
612  $itemArray = false;
613  }
614  }
615  return $itemArray;
616  }
617  return false;
618  }
619 
620  /*******************************
621  *
622  * Setting values
623  *
624  *******************************/
625 
645  public function ‪setReferenceValue($hash, $newValue, $returnDataArray = false, $bypassWorkspaceAdminCheck = false)
646  {
647  $backendUser = $this->‪getBackendUser();
648  if ($backendUser->workspace === 0 && $backendUser->isAdmin() || $bypassWorkspaceAdminCheck) {
649  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
650  $queryBuilder->getRestrictions()->removeAll();
651 
652  // Get current index from Database
653  $referenceRecord = $queryBuilder
654  ->select('*')
655  ->from('sys_refindex')
656  ->where(
657  $queryBuilder->expr()->eq('hash', $queryBuilder->createNamedParameter($hash, ‪Connection::PARAM_STR))
658  )
659  ->setMaxResults(1)
660  ->executeQuery()
661  ->fetchAssociative();
662 
663  // Check if reference existed.
664  if (!is_array($referenceRecord)) {
665  return 'ERROR: No reference record with hash="' . $hash . '" was found!';
666  }
667 
668  if (empty(‪$GLOBALS['TCA'][$referenceRecord['tablename']])) {
669  return 'ERROR: Table "' . $referenceRecord['tablename'] . '" was not in TCA!';
670  }
671 
672  // Get that record from database
673  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
674  ->getQueryBuilderForTable($referenceRecord['tablename']);
675  $queryBuilder->getRestrictions()->removeAll();
676  $record = $queryBuilder
677  ->select('*')
678  ->from($referenceRecord['tablename'])
679  ->where(
680  $queryBuilder->expr()->eq(
681  'uid',
682  $queryBuilder->createNamedParameter($referenceRecord['recuid'], ‪Connection::PARAM_INT)
683  )
684  )
685  ->setMaxResults(1)
686  ->executeQuery()
687  ->fetchAssociative();
688 
689  if (is_array($record)) {
690  // Get relation for single field from record
691  $recordRelations = $this->‪getRelations($referenceRecord['tablename'], $record, $referenceRecord['field']);
692  if ($fieldRelation = $recordRelations[$referenceRecord['field']]) {
693  // Initialize data array that is to be sent to DataHandler afterwards:
694  $dataArray = [];
695  // Based on type
696  switch ((string)$fieldRelation['type']) {
697  case 'db':
698  $error = $this->‪setReferenceValue_dbRels($referenceRecord, $fieldRelation['itemArray'], $newValue, $dataArray);
699  if ($error) {
700  return $error;
701  }
702  break;
703  case 'flex':
704  // DB references in FlexForms
705  if (is_array($fieldRelation['flexFormRels']['db'][$referenceRecord['flexpointer']])) {
706  $error = $this->‪setReferenceValue_dbRels($referenceRecord, $fieldRelation['flexFormRels']['db'][$referenceRecord['flexpointer']], $newValue, $dataArray, $referenceRecord['flexpointer']);
707  if ($error) {
708  return $error;
709  }
710  }
711  // Soft references in FlexForms
712  if ($referenceRecord['softref_key'] && is_array($fieldRelation['flexFormRels']['softrefs'][$referenceRecord['flexpointer']]['keys'][$referenceRecord['softref_key']])) {
713  $error = $this->‪setReferenceValue_softreferences($referenceRecord, $fieldRelation['flexFormRels']['softrefs'][$referenceRecord['flexpointer']], $newValue, $dataArray, $referenceRecord['flexpointer']);
714  if ($error) {
715  return $error;
716  }
717  }
718  break;
719  }
720  // Soft references in the field:
721  if ($referenceRecord['softref_key'] && is_array($fieldRelation['softrefs']['keys'][$referenceRecord['softref_key']])) {
722  $error = $this->‪setReferenceValue_softreferences($referenceRecord, $fieldRelation['softrefs'], $newValue, $dataArray);
723  if ($error) {
724  return $error;
725  }
726  }
727  // Data Array, now ready to be sent to DataHandler
728  if ($returnDataArray) {
729  return $dataArray;
730  }
731  // Execute CMD array:
732  $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
733  $dataHandler->dontProcessTransformations = true;
734  $dataHandler->bypassWorkspaceRestrictions = true;
735  // Otherwise this may lead to permission issues if user is not admin
736  $dataHandler->bypassAccessCheckForRecords = true;
737  // Check has been done previously that there is a backend user which is Admin and also in live workspace
738  $dataHandler->start($dataArray, []);
739  $dataHandler->process_datamap();
740  // Return errors if any:
741  if (!empty($dataHandler->errorLog)) {
742  return LF . 'DataHandler:' . implode(LF . 'DataHandler:', $dataHandler->errorLog);
743  }
744  }
745  }
746  } else {
747  return 'ERROR: BE_USER object is not admin OR not in workspace 0 (Live)';
748  }
749 
750  return false;
751  }
752 
763  protected function ‪setReferenceValue_dbRels($refRec, $itemArray, $newValue, &$dataArray, $flexPointer = '')
764  {
765  if ((int)$itemArray[$refRec['sorting']]['id'] === (int)$refRec['ref_uid'] && (string)$itemArray[$refRec['sorting']]['table'] === (string)$refRec['ref_table']) {
766  // Setting or removing value:
767  // Remove value:
768  if ($newValue === null) {
769  unset($itemArray[$refRec['sorting']]);
770  } else {
771  [$itemArray[$refRec['sorting']]['table'], $itemArray[$refRec['sorting']]['id']] = explode(':', $newValue);
772  }
773  // Traverse and compile new list of records:
774  $saveValue = [];
775  foreach ($itemArray as $pair) {
776  $saveValue[] = $pair['table'] . '_' . $pair['id'];
777  }
778  // Set in data array:
779  if ($flexPointer) {
780  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'] = ‪ArrayUtility::setValueByPath(
781  [],
782  substr($flexPointer, 0, -1),
783  implode(',', $saveValue)
784  );
785  } else {
786  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']] = implode(',', $saveValue);
787  }
788  } else {
789  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'] . '"';
790  }
791 
792  return false;
793  }
794 
805  protected function ‪setReferenceValue_softreferences($refRec, $softref, $newValue, &$dataArray, $flexPointer = '')
806  {
807  if (!is_array($softref['keys'][$refRec['softref_key']][$refRec['softref_id']])) {
808  return 'ERROR: Soft reference parser key "' . $refRec['softref_key'] . '" or the index "' . $refRec['softref_id'] . '" was not found.';
809  }
810 
811  // Set new value:
812  $softref['keys'][$refRec['softref_key']][$refRec['softref_id']]['subst']['tokenValue'] = '' . $newValue;
813  // Traverse softreferences and replace in tokenized content to rebuild it with new value inside:
814  foreach ($softref['keys'] as $sfIndexes) {
815  foreach ($sfIndexes as $data) {
816  $softref['tokenizedContent'] = str_replace('{softref:' . $data['subst']['tokenID'] . '}', $data['subst']['tokenValue'], $softref['tokenizedContent']);
817  }
818  }
819  // Set in data array:
820  if (!str_contains($softref['tokenizedContent'], '{softref:')) {
821  if ($flexPointer) {
822  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'] = ‪ArrayUtility::setValueByPath(
823  [],
824  substr($flexPointer, 0, -1),
825  $softref['tokenizedContent']
826  );
827  } else {
828  $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']] = $softref['tokenizedContent'];
829  }
830  } else {
831  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.)';
832  }
833 
834  return false;
835  }
836 
837  /*******************************
838  *
839  * Helper functions
840  *
841  *******************************/
842 
849  protected function ‪isDbReferenceField(array $configuration): bool
850  {
851  return
852  ($configuration['type'] === 'group' && ($configuration['internal_type'] ?? '') !== 'folder')
853  || (
854  in_array($configuration['type'], ['select', 'category', 'inline'], true)
855  && !empty($configuration['foreign_table'])
856  );
857  }
858 
865  protected function ‪isReferenceField(array $configuration): bool
866  {
867  return
868  $this->‪isDbReferenceField($configuration)
869  || ($configuration['type'] === 'input' && isset($configuration['renderType']) && $configuration['renderType'] === 'inputLink')
870  || $configuration['type'] === 'flex'
871  || isset($configuration['softref'])
872  ;
873  }
874 
881  protected function ‪fetchTableRelationFields(string $tableName): array
882  {
883  if (!empty($this->tableRelationFieldCache[$tableName])) {
884  return $this->tableRelationFieldCache[$tableName];
885  }
886  if (!isset(‪$GLOBALS['TCA'][$tableName]['columns'])) {
887  return [];
888  }
889  ‪$fields = [];
890  foreach (‪$GLOBALS['TCA'][$tableName]['columns'] as $field => $fieldDefinition) {
891  if (is_array($fieldDefinition['config'])) {
892  // Check for flex field
893  if (isset($fieldDefinition['config']['type']) && $fieldDefinition['config']['type'] === 'flex') {
894  // Fetch all fields if the is a field of type flex in the table definition because the complete row is passed to
895  // FlexFormTools->getDataStructureIdentifier() in the end and might be needed in ds_pointerField or a hook
896  $this->tableRelationFieldCache[$tableName] = ['*'];
897  return ['*'];
898  }
899  // Only fetch this field if it can contain a reference
900  if ($this->‪isReferenceField($fieldDefinition['config'])) {
901  ‪$fields[] = $field;
902  }
903  }
904  }
905  $this->tableRelationFieldCache[$tableName] = ‪$fields;
906  return ‪$fields;
907  }
908 
917  public function ‪updateIndex($testOnly, ?ProgressListenerInterface $progressListener = null)
918  {
919  ‪$errors = [];
920  $tableNames = [];
921  $recCount = 0;
922  $isWorkspacesLoaded = ‪ExtensionManagementUtility::isLoaded('workspaces');
923  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
924  $refIndexConnectionName = empty(‪$GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping']['sys_refindex'])
926  : ‪$GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping']['sys_refindex'];
927 
928  // Drop sys_refindex rows from deleted workspaces
929  $listOfActiveWorkspaces = $this->‪getListOfActiveWorkspaces();
930  $unusedWorkspaceRows = $this->‪getAmountOfUnusedWorkspaceRowsInReferenceIndex($listOfActiveWorkspaces);
931  if ($unusedWorkspaceRows > 0) {
932  $error = 'Index table hosted ' . $unusedWorkspaceRows . ' indexes for non-existing or deleted workspaces, now removed.';
933  ‪$errors[] = $error;
934  if ($progressListener) {
935  $progressListener->log($error, LogLevel::WARNING);
936  }
937  if (!$testOnly) {
938  $this->‪removeUnusedWorkspaceRowsFromReferenceIndex($listOfActiveWorkspaces);
939  }
940  }
941 
942  // Main loop traverses all records of all TCA tables
943  foreach (‪$GLOBALS['TCA'] as $tableName => $cfg) {
944  if ($this->‪shouldExcludeTableFromReferenceIndex($tableName)) {
945  continue;
946  }
947  $tableConnectionName = empty(‪$GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'][$tableName])
949  : ‪$GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'][$tableName];
950 
951  // Some additional magic is needed if the table has a field that is the local side of
952  // a mm relation. See the variable usage below for details.
953  $tableHasLocalSideMmRelation = false;
954  foreach (($cfg['columns'] ?? []) as $fieldConfig) {
955  if (!empty($fieldConfig['config']['MM'] ?? '')
956  && !empty($fieldConfig['config']['allowed'] ?? '')
957  && empty($fieldConfig['config']['MM_opposite_field'] ?? '')
958  ) {
959  $tableHasLocalSideMmRelation = true;
960  }
961  }
962 
963  ‪$fields = ['uid'];
964  if (BackendUtility::isTableWorkspaceEnabled($tableName)) {
965  ‪$fields[] = 't3ver_wsid';
966  }
967  // Traverse all records in tables, including deleted records
968  $queryBuilder = $connectionPool->getQueryBuilderForTable($tableName);
969  $queryBuilder->getRestrictions()->removeAll();
970  try {
971  $queryResult = $queryBuilder
972  ->select(...‪$fields)
973  ->from($tableName)
974  ->orderBy('uid')
975  ->executeQuery();
976  } catch (DBALException $e) {
977  // Table exists in TCA but does not exist in the database
978  $this->logger->error('Table {table_name} exists in TCA but does not exist in the database. You should run the Database Analyzer in the Install Tool to fix this.', [
979  'table_name' => $tableName,
980  'exception' => $e,
981  ]);
982  continue;
983  }
984 
985  if ($progressListener) {
986  $progressListener->start($queryResult->rowCount(), $tableName);
987  }
988  $tableNames[] = $tableName;
989  while ($record = $queryResult->fetchAssociative()) {
990  if ($progressListener) {
991  $progressListener->advance();
992  }
993 
994  if ($isWorkspacesLoaded && $tableHasLocalSideMmRelation && (int)($record['t3ver_wsid'] ?? 0) === 0) {
995  // If we have record that can be the local side of a workspace relation, workspace records
996  // may point to it, even though the record has no workspace overlay. See workspace ManyToMany
997  // Modify addCategoryRelation as example. In those cases, we need to iterate all active workspaces
998  // and update refindex for all foreign workspace records that point to it.
999  foreach ($listOfActiveWorkspaces as ‪$workspaceId) {
1000  $refIndexObj = GeneralUtility::makeInstance(self::class);
1001  $refIndexObj->setWorkspaceId(‪$workspaceId);
1002  $result = $refIndexObj->updateRefIndexTable($tableName, $record['uid'], $testOnly);
1003  $recCount++;
1004  if ($result['addedNodes'] || $result['deletedNodes']) {
1005  $error = 'Record ' . $tableName . ':' . $record['uid'] . ' had ' . $result['addedNodes'] . ' added indexes and ' . $result['deletedNodes'] . ' deleted indexes';
1006  ‪$errors[] = $error;
1007  if ($progressListener) {
1008  $progressListener->log($error, LogLevel::WARNING);
1009  }
1010  }
1011  }
1012  } else {
1013  $refIndexObj = GeneralUtility::makeInstance(self::class);
1014  if (isset($record['t3ver_wsid'])) {
1015  $refIndexObj->setWorkspaceId($record['t3ver_wsid']);
1016  }
1017  $result = $refIndexObj->updateRefIndexTable($tableName, $record['uid'], $testOnly);
1018  $recCount++;
1019  if ($result['addedNodes'] || $result['deletedNodes']) {
1020  $error = 'Record ' . $tableName . ':' . $record['uid'] . ' had ' . $result['addedNodes'] . ' added indexes and ' . $result['deletedNodes'] . ' deleted indexes';
1021  ‪$errors[] = $error;
1022  if ($progressListener) {
1023  $progressListener->log($error, LogLevel::WARNING);
1024  }
1025  }
1026  }
1027  }
1028  if ($progressListener) {
1029  $progressListener->finish();
1030  }
1031 
1032  // Subselect based queries only work on the same connection
1033  // @todo: Consider dropping this in v12 and always use sub select: The base set of tables should
1034  // be in exactly one DB and only tables like caches should be "extractable" to a different DB?!
1035  // Even though sys_refindex is a "cache-like" table since it only holds secondary information that
1036  // can always be re-created by analyzing the entire data set, it shouldn't be possible to run it
1037  // on a different database since that prevents quick joins between sys_refindex and target relations.
1038  // We should probably have some report and/or install tool check to make sure all main tables
1039  // are on the same connection in v12.
1040  if ($refIndexConnectionName !== $tableConnectionName) {
1041  $this->logger->error('Not checking table {table_name} for lost indexes, "sys_refindex" table uses a different connection', ['table_name' => $tableName]);
1042  continue;
1043  }
1044 
1045  // Searching for lost indexes for this table
1046  // Build sub-query to find lost records
1047  $subQueryBuilder = $connectionPool->getQueryBuilderForTable($tableName);
1048  $subQueryBuilder->getRestrictions()->removeAll();
1049  $subQueryBuilder
1050  ->select('uid')
1051  ->from($tableName, 'sub_' . $tableName)
1052  ->where(
1053  $subQueryBuilder->expr()->eq(
1054  'sub_' . $tableName . '.uid',
1055  $queryBuilder->quoteIdentifier('sys_refindex.recuid')
1056  )
1057  );
1058 
1059  // Main query to find lost records
1060  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_refindex');
1061  $queryBuilder->getRestrictions()->removeAll();
1062  $lostIndexes = $queryBuilder
1063  ->count('hash')
1064  ->from('sys_refindex')
1065  ->where(
1066  $queryBuilder->expr()->eq(
1067  'tablename',
1068  $queryBuilder->createNamedParameter($tableName, ‪Connection::PARAM_STR)
1069  ),
1070  'NOT EXISTS (' . $subQueryBuilder->getSQL() . ')'
1071  )
1072  ->executeQuery()
1073  ->fetchOne();
1074 
1075  if ($lostIndexes > 0) {
1076  $error = 'Table ' . $tableName . ' has ' . $lostIndexes . ' lost indexes which are now deleted';
1077  ‪$errors[] = $error;
1078  if ($progressListener) {
1079  $progressListener->log($error, LogLevel::WARNING);
1080  }
1081  if (!$testOnly) {
1082  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_refindex');
1083  $queryBuilder->delete('sys_refindex')
1084  ->where(
1085  $queryBuilder->expr()->eq(
1086  'tablename',
1087  $queryBuilder->createNamedParameter($tableName, ‪Connection::PARAM_STR)
1088  ),
1089  'NOT EXISTS (' . $subQueryBuilder->getSQL() . ')'
1090  )
1091  ->executeStatement();
1092  }
1093  }
1094  }
1095 
1096  // Searching lost indexes for non-existing tables
1097  // @todo: Consider moving this *before* the main re-index logic to have a smaller
1098  // dataset when starting with heavy lifting.
1099  $lostTables = $this->‪getAmountOfUnusedTablesInReferenceIndex($tableNames);
1100  if ($lostTables > 0) {
1101  $error = 'Index table hosted ' . $lostTables . ' indexes for non-existing tables, now removed';
1102  ‪$errors[] = $error;
1103  if ($progressListener) {
1104  $progressListener->log($error, LogLevel::WARNING);
1105  }
1106  if (!$testOnly) {
1108  }
1109  }
1110  $errorCount = count(‪$errors);
1111  $recordsCheckedString = $recCount . ' records from ' . count($tableNames) . ' tables were checked/updated.';
1112  if ($progressListener) {
1113  if ($errorCount) {
1114  $progressListener->log($recordsCheckedString . ' Updates: ' . $errorCount, LogLevel::WARNING);
1115  } else {
1116  $progressListener->log($recordsCheckedString . ' Index Integrity was perfect!', LogLevel::INFO);
1117  }
1118  }
1119  if (!$testOnly) {
1120  $registry = GeneralUtility::makeInstance(Registry::class);
1121  $registry->set('core', 'sys_refindex_lastUpdate', ‪$GLOBALS['EXEC_TIME']);
1122  }
1123  return ['resultText' => trim($recordsCheckedString), 'errors' => ‪$errors];
1124  }
1125 
1132  private function ‪getListOfActiveWorkspaces(): array
1133  {
1134  if (!‪ExtensionManagementUtility::isLoaded('workspaces')) {
1135  // If ext:workspaces is not loaded, "0" is the only valid one.
1136  return [0];
1137  }
1138  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_workspace');
1139  // There are no "hidden" workspaces, which wouldn't make much sense anyways.
1140  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1141  $result = $queryBuilder->select('uid')->from('sys_workspace')->orderBy('uid')->executeQuery();
1142  // "0", plus non-deleted workspaces are active
1143  $activeWorkspaces = [0];
1144  while ($row = $result->fetchFirstColumn()) {
1145  $activeWorkspaces[] = (int)$row[0];
1146  }
1147  return $activeWorkspaces;
1148  }
1149 
1156  private function ‪getAmountOfUnusedWorkspaceRowsInReferenceIndex(array $activeWorkspaces): int
1157  {
1158  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
1159  $numberOfInvalidWorkspaceRecords = $queryBuilder->count('hash')
1160  ->from('sys_refindex')
1161  ->where(
1162  $queryBuilder->expr()->notIn(
1163  'workspace',
1164  $queryBuilder->createNamedParameter($activeWorkspaces, Connection::PARAM_INT_ARRAY)
1165  )
1166  )
1167  ->executeQuery()
1168  ->fetchOne();
1169  return (int)$numberOfInvalidWorkspaceRecords;
1170  }
1171 
1176  private function ‪removeUnusedWorkspaceRowsFromReferenceIndex(array $activeWorkspaces): void
1177  {
1178  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
1179  $queryBuilder->delete('sys_refindex')
1180  ->where(
1181  $queryBuilder->expr()->notIn(
1182  'workspace',
1183  $queryBuilder->createNamedParameter($activeWorkspaces, Connection::PARAM_INT_ARRAY)
1184  )
1185  )
1186  ->executeStatement();
1187  }
1188 
1189  protected function ‪getAmountOfUnusedTablesInReferenceIndex(array $tableNames): int
1190  {
1191  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
1192  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_refindex');
1193  $queryBuilder->getRestrictions()->removeAll();
1194  $lostTables = $queryBuilder
1195  ->count('hash')
1196  ->from('sys_refindex')
1197  ->where(
1198  $queryBuilder->expr()->notIn(
1199  'tablename',
1200  $queryBuilder->createNamedParameter($tableNames, Connection::PARAM_STR_ARRAY)
1201  )
1202  )->executeQuery()
1203  ->fetchOne();
1204  return (int)$lostTables;
1205  }
1206 
1207  protected function ‪removeReferenceIndexDataFromUnusedDatabaseTables(array $tableNames): void
1208  {
1209  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
1210  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_refindex');
1211  $queryBuilder->delete('sys_refindex')
1212  ->where(
1213  $queryBuilder->expr()->notIn(
1214  'tablename',
1215  $queryBuilder->createNamedParameter($tableNames, Connection::PARAM_STR_ARRAY)
1216  )
1217  )->executeStatement();
1218  }
1219 
1227  protected function ‪getRecord(string $tableName, int $uid)
1228  {
1229  // Fetch fields of the table which might contain relations
1230  $tableRelationFields = $this->‪fetchTableRelationFields($tableName);
1231 
1232  if ($tableRelationFields === []) {
1233  // Return if there are no fields which could contain relations
1234  return ‪$this->relations;
1235  }
1236  if ($tableRelationFields !== ['*']) {
1237  // Only fields that might contain relations are fetched
1238  $tableRelationFields[] = 'uid';
1239  if (BackendUtility::isTableWorkspaceEnabled($tableName)) {
1240  $tableRelationFields = array_merge($tableRelationFields, ['t3ver_wsid', 't3ver_state']);
1241  }
1242  }
1243 
1244  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1245  ->getQueryBuilderForTable($tableName);
1246  $queryBuilder->getRestrictions()->removeAll();
1247  $queryBuilder
1248  ->select(...array_unique($tableRelationFields))
1249  ->from($tableName)
1250  ->where(
1251  $queryBuilder->expr()->eq(
1252  'uid',
1253  $queryBuilder->createNamedParameter($uid, ‪Connection::PARAM_INT)
1254  )
1255  );
1256  // Do not fetch soft deleted records
1257  $deleteField = (string)(‪$GLOBALS['TCA'][$tableName]['ctrl']['delete'] ?? '');
1258  if ($deleteField !== '') {
1259  $queryBuilder->andWhere(
1260  $queryBuilder->expr()->eq(
1261  $deleteField,
1262  $queryBuilder->createNamedParameter(0, ‪Connection::PARAM_INT)
1263  )
1264  );
1265  }
1266  return $queryBuilder->executeQuery()->fetchAssociative();
1267  }
1268 
1275  protected function ‪shouldExcludeTableFromReferenceIndex(string $tableName): bool
1276  {
1277  if (isset($this->excludedTables[$tableName])) {
1278  return $this->excludedTables[$tableName];
1279  }
1280  // Only exclude tables from ReferenceIndex which do not contain any relations and never
1281  // did since existing references won't be deleted!
1282  $event = new IsTableExcludedFromReferenceIndexEvent($tableName);
1283  $event = $this->eventDispatcher->dispatch($event);
1284  $this->excludedTables[$tableName] = $event->isTableExcluded();
1285  return $this->excludedTables[$tableName];
1286  }
1287 
1297  string $tableName,
1298  string $column,
1299  string $onlyColumn
1300  ): bool {
1301  if (isset($this->excludedColumns[$column])) {
1302  return true;
1303  }
1304  if (is_array(‪$GLOBALS['TCA'][$tableName]['columns'][$column] ?? false)
1305  && (!$onlyColumn || $onlyColumn === $column)
1306  ) {
1307  return false;
1308  }
1309  return true;
1310  }
1311 
1319  public function ‪enableRuntimeCache()
1320  {
1321  trigger_error('Calling ReferenceIndex->enableRuntimeCache() is obsolete and should be dropped.', E_USER_DEPRECATED);
1322  }
1323 
1329  public function ‪disableRuntimeCache()
1330  {
1331  trigger_error('Calling ReferenceIndex->disableRuntimeCache() is obsolete and should be dropped.', E_USER_DEPRECATED);
1332  }
1333 
1339  protected function ‪getBackendUser()
1340  {
1341  return ‪$GLOBALS['BE_USER'];
1342  }
1343 }
‪TYPO3\CMS\Core\DataHandling\DataHandler
Definition: DataHandler.php:86
‪TYPO3\CMS\Core\Database\ReferenceIndex\createEntryDataForDatabaseRelationsUsingRecord
‪createEntryDataForDatabaseRelationsUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, array $items)
Definition: ReferenceIndex.php:387
‪TYPO3\CMS\Core\DataHandling\Event\IsTableExcludedFromReferenceIndexEvent
Definition: IsTableExcludedFromReferenceIndexEvent.php:28
‪TYPO3\CMS\Core\Utility\GeneralUtility\xml2array
‪static mixed xml2array($string, $NSprefix='', $reportDocTag=false)
Definition: GeneralUtility.php:1482
‪TYPO3\CMS\Core\Database\ReferenceIndex\disableRuntimeCache
‪disableRuntimeCache()
Definition: ReferenceIndex.php:1326
‪TYPO3\CMS\Core\Database\ReferenceIndex\updateIndex
‪array updateIndex($testOnly, ?ProgressListenerInterface $progressListener=null)
Definition: ReferenceIndex.php:914
‪TYPO3\CMS\Core\Database\ReferenceIndex\$temp_flexRelations
‪array $temp_flexRelations
Definition: ReferenceIndex.php:90
‪TYPO3\CMS\Core\Database\ReferenceIndex\fetchTableRelationFields
‪array fetchTableRelationFields(string $tableName)
Definition: ReferenceIndex.php:878
‪TYPO3\CMS\Core\Database\ReferenceIndex\getAmountOfUnusedTablesInReferenceIndex
‪getAmountOfUnusedTablesInReferenceIndex(array $tableNames)
Definition: ReferenceIndex.php:1186
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:49
‪TYPO3\CMS\Core\Database\ReferenceIndex\$relations
‪array $relations
Definition: ReferenceIndex.php:96
‪TYPO3\CMS\Core\DataHandling\SoftReference\SoftReferenceParserFactory
Definition: SoftReferenceParserFactory.php:28
‪TYPO3\CMS\Core\Database\ReferenceIndex\enableRuntimeCache
‪enableRuntimeCache()
Definition: ReferenceIndex.php:1316
‪TYPO3\CMS\Core\Database\ReferenceIndex\shouldExcludeTableColumnFromReferenceIndex
‪bool shouldExcludeTableColumnFromReferenceIndex(string $tableName, string $column, string $onlyColumn)
Definition: ReferenceIndex.php:1293
‪TYPO3\CMS\Core\Database\ReferenceIndex\$eventDispatcher
‪EventDispatcherInterface $eventDispatcher
Definition: ReferenceIndex.php:117
‪TYPO3\CMS\Core\Database\ReferenceIndex
Definition: ReferenceIndex.php:42
‪TYPO3\CMS\Core\Database\ReferenceIndex\createEntryDataForSoftReferencesUsingRecord
‪createEntryDataForSoftReferencesUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, array $keys)
Definition: ReferenceIndex.php:403
‪TYPO3\CMS\Core\Database\ReferenceIndex\$excludedColumns
‪array $excludedColumns
Definition: ReferenceIndex.php:73
‪TYPO3\CMS\Core\Registry
Definition: Registry.php:33
‪TYPO3\CMS\Core\Database\ReferenceIndex\$excludedTables
‪array $excludedTables
Definition: ReferenceIndex.php:58
‪TYPO3\CMS\Core\Database\ReferenceIndex\getRecord
‪array false getRecord(string $tableName, int $uid)
Definition: ReferenceIndex.php:1224
‪TYPO3\CMS\Core\Database\ReferenceIndex\getRelations
‪array getRelations($table, $row, $onlyField='')
Definition: ReferenceIndex.php:441
‪TYPO3\CMS\Core\Database\ReferenceIndex\removeReferenceIndexDataFromUnusedDatabaseTables
‪removeReferenceIndexDataFromUnusedDatabaseTables(array $tableNames)
Definition: ReferenceIndex.php:1204
‪TYPO3\CMS\Core\Database\ReferenceIndex\__construct
‪__construct(EventDispatcherInterface $eventDispatcher=null, SoftReferenceParserFactory $softReferenceParserFactory=null)
Definition: ReferenceIndex.php:120
‪$fields
‪$fields
Definition: pages.php:5
‪TYPO3\CMS\Core\Database\Connection\PARAM_STR
‪const PARAM_STR
Definition: Connection.php:54
‪TYPO3\CMS\Core\Database\ReferenceIndex\createEntryDataUsingRecord
‪array bool createEntryDataUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, string $referencedTable, int $referencedUid, string $referenceString='', int $sort=-1, string $softReferenceKey='', string $softReferenceId='')
Definition: ReferenceIndex.php:351
‪TYPO3\CMS\Core\Database\ConnectionPool\DEFAULT_CONNECTION_NAME
‪const DEFAULT_CONNECTION_NAME
Definition: ConnectionPool.php:50
‪TYPO3\CMS\Core\Database\ReferenceIndex\isReferenceField
‪bool isReferenceField(array $configuration)
Definition: ReferenceIndex.php:862
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility
Definition: ExtensionManagementUtility.php:43
‪TYPO3\CMS\Core\Database\ReferenceIndex\setWorkspaceId
‪setWorkspaceId($workspaceId)
Definition: ReferenceIndex.php:132
‪TYPO3\CMS\Core\Database\ReferenceIndex\getAmountOfUnusedWorkspaceRowsInReferenceIndex
‪getAmountOfUnusedWorkspaceRowsInReferenceIndex(array $activeWorkspaces)
Definition: ReferenceIndex.php:1153
‪TYPO3\CMS\Core\Database\ReferenceIndex\$workspaceId
‪int $workspaceId
Definition: ReferenceIndex.php:108
‪TYPO3\CMS\Core\Database\ReferenceIndex\getBackendUser
‪TYPO3 CMS Core Authentication BackendUserAuthentication getBackendUser()
Definition: ReferenceIndex.php:1336
‪TYPO3\CMS\Core\Database\ReferenceIndex\updateRefIndexTable
‪array updateRefIndexTable($tableName, $uid, $testOnly=false)
Definition: ReferenceIndex.php:160
‪TYPO3\CMS\Core\Database\ReferenceIndex\isDbReferenceField
‪bool isDbReferenceField(array $configuration)
Definition: ReferenceIndex.php:846
‪TYPO3\CMS\Core\Database\ReferenceIndex\getListOfActiveWorkspaces
‪int[] getListOfActiveWorkspaces()
Definition: ReferenceIndex.php:1129
‪TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools
Definition: FlexFormTools.php:40
‪TYPO3\CMS\Core\Database\ReferenceIndex\setReferenceValue_dbRels
‪string setReferenceValue_dbRels($refRec, $itemArray, $newValue, &$dataArray, $flexPointer='')
Definition: ReferenceIndex.php:760
‪TYPO3\CMS\Core\Database\ReferenceIndex\$hashVersion
‪int $hashVersion
Definition: ReferenceIndex.php:103
‪$errors
‪$errors
Definition: annotationChecker.php:123
‪TYPO3\CMS\Core\Utility\ArrayUtility\setValueByPath
‪static array setValueByPath(array $array, $path, $value, $delimiter='/')
Definition: ArrayUtility.php:272
‪TYPO3\CMS\Core\Database\ReferenceIndex\generateDataUsingRecord
‪array generateDataUsingRecord(string $tableName, array $record)
Definition: ReferenceIndex.php:299
‪TYPO3\CMS\Backend\View\ProgressListenerInterface
Definition: ProgressListenerInterface.php:31
‪TYPO3\CMS\Core\Database\ReferenceIndex\getRelations_flexFormCallBack
‪getRelations_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath)
Definition: ReferenceIndex.php:517
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:24
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
Definition: DeletedRestriction.php:28
‪TYPO3\CMS\Core\Database\Platform\PlatformInformation
Definition: PlatformInformation.php:33
‪TYPO3\CMS\Core\Database\ReferenceIndex\removeUnusedWorkspaceRowsFromReferenceIndex
‪removeUnusedWorkspaceRowsFromReferenceIndex(array $activeWorkspaces)
Definition: ReferenceIndex.php:1173
‪TYPO3\CMS\Core\Database\ReferenceIndex\setReferenceValue_softreferences
‪string setReferenceValue_softreferences($refRec, $softref, $newValue, &$dataArray, $flexPointer='')
Definition: ReferenceIndex.php:802
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Core\Database\ReferenceIndex\$softReferenceParserFactory
‪SoftReferenceParserFactory $softReferenceParserFactory
Definition: ReferenceIndex.php:118
‪TYPO3\CMS\Core\Database\ReferenceIndex\getWorkspaceId
‪int getWorkspaceId()
Definition: ReferenceIndex.php:143
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\isLoaded
‪static bool isLoaded($key)
Definition: ExtensionManagementUtility.php:114
‪TYPO3\CMS\Core\Database\ReferenceIndex\shouldExcludeTableFromReferenceIndex
‪bool shouldExcludeTableFromReferenceIndex(string $tableName)
Definition: ReferenceIndex.php:1272
‪TYPO3\CMS\Core\Database\ReferenceIndex\getRelations_procDB
‪array bool getRelations_procDB($value, $conf, $uid, $table='', array $row=[])
Definition: ReferenceIndex.php:566
‪TYPO3\CMS\Core\Database\ReferenceIndex\$tableRelationFieldCache
‪array $tableRelationFieldCache
Definition: ReferenceIndex.php:115
‪TYPO3\CMS\Core\Database
Definition: Connection.php:18
‪TYPO3\CMS\Core\Database\ReferenceIndex\setReferenceValue
‪string bool array setReferenceValue($hash, $newValue, $returnDataArray=false, $bypassWorkspaceAdminCheck=false)
Definition: ReferenceIndex.php:642