‪TYPO3CMS  ‪main
CleanFlexFormsCommand.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Symfony\Component\Console\Attribute\AsCommand;
21 use Symfony\Component\Console\Command\Command;
22 use Symfony\Component\Console\Input\InputInterface;
23 use Symfony\Component\Console\Input\InputOption;
24 use Symfony\Component\Console\Output\OutputInterface;
25 use Symfony\Component\Console\Style\SymfonyStyle;
31 
35 #[AsCommand('cleanup:flexforms', 'Clean up database FlexForm fields that do not match the chosen data structure.')]
36 class ‪CleanFlexFormsCommand extends Command
37 {
38  public function ‪__construct(
39  private readonly ‪ConnectionPool $connectionPool,
40  private readonly ‪FlexFormTools $flexFormTools,
41  ) {
42  parent::__construct();
43  }
44 
48  public function ‪configure()
49  {
50  $this
51  ->setHelp('Clean up records with dirty FlexForm values not reflected in current data structure.')
52  ->addOption(
53  'dry-run',
54  null,
55  InputOption::VALUE_NONE,
56  'If this option is set, the records will not be updated, but only show the output which records would have been updated.'
57  );
58  }
59 
63  protected function ‪execute(InputInterface $input, OutputInterface ‪$output): int
64  {
65  $io = new SymfonyStyle($input, ‪$output);
66  $io->title($this->getDescription());
67 
68  $isDryRun = $input->hasOption('dry-run') && $input->getOption('dry-run');
69 
70  $numberOfAffectedRecords = 0;
71  $numberOfAffectedTables = 0;
72  foreach (‪$GLOBALS['TCA'] as $tableName => $tableConfiguration) {
73  if (!is_array($tableConfiguration['columns'] ?? false)) {
74  continue;
75  }
76  $flexFieldsInTable = [];
77  foreach ($tableConfiguration['columns'] as $columnName => $columnDefinition) {
78  if (($columnDefinition['config']['type'] ?? '') !== 'flex') {
79  continue;
80  }
81  $flexFieldsInTable[] = $columnName;
82  }
83  if (empty($flexFieldsInTable)) {
84  continue;
85  }
86  $tableHadUpdate = false;
87  $queryBuilder = $this->connectionPool->getQueryBuilderForTable($tableName);
88  // We do *not* consider soft-deleted records, they are stalled and "don't exist anymore"
89  // from Backend point of view, this command follows this view.
90  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
91  $orWheres = [];
92  foreach ($flexFieldsInTable as $flexFieldInTable) {
93  $orWheres[] = $queryBuilder->expr()->neq($flexFieldInTable, $queryBuilder->createNamedParameter(''));
94  }
95  $result = $queryBuilder
96  ->select('*')
97  ->from($tableName)
98  ->where($queryBuilder->expr()->or(...$orWheres))
99  ->orderBy('uid')
100  ->executeQuery();
101  while (‪$record = $result->fetchAssociative()) {
102  $recordHadUpdate = false;
103  foreach ($flexFieldsInTable as $flexFieldInTable) {
104  if ((string)‪$record[$flexFieldInTable] === '') {
105  // Don't handle empty or null value
106  continue;
107  }
108  // Clean XML and check against current record value.
109  $cleanXml = $this->flexFormTools->cleanFlexFormXML($tableName, $flexFieldInTable, ‪$record);
110  if ($cleanXml !== ‪$record[$flexFieldInTable]) {
111  $recordHadUpdate = true;
112  $tableHadUpdate = true;
113  if (!$isDryRun) {
114  $this->connectionPool->getConnectionForTable($tableName)->update(
115  $tableName,
116  [$flexFieldInTable => $cleanXml],
117  ['uid' => ‪$record['uid']],
118  [$flexFieldInTable => ‪Connection::PARAM_STR]
119  );
120  }
121  $io->writeln(
122  $isDryRun
123  ? 'Found dirty FlexForm XML in record "' . $tableName . ':' . ‪$record['uid'] . '", field "' . $flexFieldInTable . '".'
124  : 'Updated FlexForm XML in record "' . $tableName . ':' . ‪$record['uid'] . '", field "' . $flexFieldInTable . '".'
125  );
126  }
127  }
128  if ($recordHadUpdate) {
129  $numberOfAffectedRecords++;
130  }
131  }
132  if ($tableHadUpdate) {
133  $numberOfAffectedTables++;
134  }
135  }
136 
137  if ($numberOfAffectedRecords) {
138  $io->success(
139  $isDryRun
140  ? 'Found ' . $numberOfAffectedRecords . ' dirty records in ' . $numberOfAffectedTables . ' tables.'
141  : 'Updated ' . $numberOfAffectedRecords . ' dirty records in ' . $numberOfAffectedTables . ' tables.'
142  );
143  } else {
144  $io->success('No dirty FlexForm fields found.');
145  }
146 
147  return Command::SUCCESS;
148  }
149 }
‪TYPO3\CMS\Core\Database\Connection\PARAM_STR
‪const PARAM_STR
Definition: Connection.php:57
‪TYPO3\CMS\Lowlevel\Command\CleanFlexFormsCommand\configure
‪configure()
Definition: CleanFlexFormsCommand.php:48
‪TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools
Definition: FlexFormTools.php:40
‪TYPO3\CMS\Webhooks\Message\$record
‪identifier readonly int readonly array $record
Definition: PageModificationMessage.php:36
‪TYPO3\CMS\Lowlevel\Command\CleanFlexFormsCommand
Definition: CleanFlexFormsCommand.php:37
‪$output
‪$output
Definition: annotationChecker.php:114
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:41
‪$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\Lowlevel\Command\CleanFlexFormsCommand\__construct
‪__construct(private readonly ConnectionPool $connectionPool, private readonly FlexFormTools $flexFormTools,)
Definition: CleanFlexFormsCommand.php:38
‪TYPO3\CMS\Lowlevel\Command
Definition: CleanFlexFormsCommand.php:18
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Lowlevel\Command\CleanFlexFormsCommand\execute
‪execute(InputInterface $input, OutputInterface $output)
Definition: CleanFlexFormsCommand.php:63