2 declare(strict_types = 1);
18 use Symfony\Component\Console\Command\Command;
19 use Symfony\Component\Console\Input\InputInterface;
20 use Symfony\Component\Console\Input\InputOption;
21 use Symfony\Component\Console\Output\OutputInterface;
22 use Symfony\Component\Console\Style\SymfonyStyle;
43 ->setDescription(
'Permanently deletes all records marked as "deleted" in the database.')
44 ->setHelp(
'Traverse page tree and find and flush deleted records. If you want to get more detailed information, use the --verbose option.')
48 InputOption::VALUE_REQUIRED,
49 'Setting start page in page tree. Default is the page tree root, 0 (zero)'
54 InputOption::VALUE_REQUIRED,
55 'Setting traversal depth. 0 (zero) will only analyze start page (see --pid), 1 will traverse one level of subpages etc.'
60 InputOption::VALUE_NONE,
61 'If this option is set, the records will not actually be deleted, but just the output which records would be deleted are shown'
76 $io =
new SymfonyStyle($input,
$output);
77 $io->title($this->getDescription());
89 if ($io->isVerbose()) {
90 $io->section(
'Searching the database now for deleted records.');
94 $dryRun = $input->hasOption(
'dry-run') && $input->getOption(
'dry-run') !=
false ? true :
false;
99 if (!$io->isQuiet()) {
100 $totalAmountOfTables = count($deletedRecords);
101 $totalAmountOfRecords = 0;
102 foreach ($deletedRecords as $tableName => $itemsInTable) {
103 $totalAmountOfRecords += count($itemsInTable);
105 if ($io->isVeryVerbose()) {
106 $io->writeln(
'Found ' . count($itemsInTable) .
' deleted records in table "' . $tableName .
'".');
109 $io->note(
'Found ' . $totalAmountOfRecords .
' records in ' . $totalAmountOfTables .
' database tables ready to be deleted.');
112 $io->section(
'Deletion process starting now.' . ($dryRun ?
' (Not deleting now, just a dry run)' :
''));
117 $io->success(
'All done!');
134 $queryBuilderForPages = GeneralUtility::makeInstance(ConnectionPool::class)
135 ->getQueryBuilderForTable(
'pages');
136 $queryBuilderForPages->getRestrictions()->removeAll();
138 $pageId = (int)$pageId;
140 $queryBuilderForPages
141 ->select(
'uid',
'deleted')
144 $queryBuilderForPages->expr()->andX(
145 $queryBuilderForPages->expr()->eq(
147 $queryBuilderForPages->createNamedParameter($pageId, \PDO::PARAM_INT)
149 $queryBuilderForPages->expr()->neq(
'deleted', 0)
153 $rowCount = $queryBuilderForPages
159 $deletedRecords[
'pages'][$pageId] = $pageId;
165 foreach ($databaseTables as $tableName => $deletedField) {
167 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
168 ->getQueryBuilderForTable($tableName);
170 $queryBuilder->getRestrictions()->removeAll();
172 $result = $queryBuilder
173 ->select(
'uid', $deletedField)
176 $queryBuilder->expr()->eq(
178 $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT)
183 while ($recordOnPage = $result->fetch()) {
185 if ($recordOnPage[$deletedField]) {
186 $deletedRecords[$tableName][$recordOnPage[
'uid']] = $recordOnPage[
'uid'];
191 $recordOnPage[
'uid'],
192 'uid,t3ver_wsid,t3ver_count,' . $deletedField,
196 if (is_array($versions)) {
197 foreach ($versions as $verRec) {
199 if (!$verRec[
'_CURRENT_VERSION'] && $verRec[$deletedField]) {
200 $deletedRecords[$tableName][$verRec[
'uid']] = $verRec[
'uid'];
210 $result = $queryBuilderForPages
214 $queryBuilderForPages->expr()->eq(
'pid', $pageId)
219 while ($subPage = $result->fetch()) {
229 'uid,t3ver_oid,t3ver_wsid,t3ver_count',
233 if (is_array($versions)) {
234 foreach ($versions as $verRec) {
235 if (!$verRec[
'_CURRENT_VERSION']) {
242 return $deletedRecords;
254 foreach (
$GLOBALS[
'TCA'] as $tableName => $configuration) {
255 if ($tableName !==
'pages' && isset(
$GLOBALS[
'TCA'][$tableName][
'ctrl'][
'delete'])) {
256 $tables[$tableName] =
$GLOBALS[
'TCA'][$tableName][
'ctrl'][
'delete'];
270 protected function deleteRecords(array $deletedRecords,
bool $dryRun, SymfonyStyle $io)
273 if (isset($deletedRecords[
'pages'])) {
274 $_pages = $deletedRecords[
'pages'];
275 unset($deletedRecords[
'pages']);
277 $deletedRecords[
'pages'] = array_reverse($_pages);
281 $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
282 $dataHandler->start([], []);
285 foreach ($deletedRecords as $table => $list) {
286 if ($io->isVerbose()) {
287 $io->writeln(
'Flushing ' . count($list) .
' deleted records from table "' . $table .
'"');
289 foreach ($list as $uid) {
290 if ($io->isVeryVerbose()) {
291 $io->writeln(
'Flushing record "' . $table .
':' . $uid .
'"');
297 $dataHandler->deleteRecord($table, $uid,
true,
true);
299 if (!empty($dataHandler->errorLog)) {
300 $errorMessage = array_merge([
'DataHandler reported an error'], $dataHandler->errorLog);
301 $io->error($errorMessage);
302 } elseif (!$io->isQuiet()) {
303 $io->writeln(
'Permanently deleted record "' . $table .
':' . $uid .
'".');