diff --git a/Classes/Command/AsyncReferenceIndexCommandController.php b/Classes/Command/AsyncReferenceIndexCommand.php similarity index 65% rename from Classes/Command/AsyncReferenceIndexCommandController.php rename to Classes/Command/AsyncReferenceIndexCommand.php index 0815e2c..fde374a 100644 --- a/Classes/Command/AsyncReferenceIndexCommandController.php +++ b/Classes/Command/AsyncReferenceIndexCommand.php @@ -6,7 +6,11 @@ use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Database\ReferenceIndex; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Extbase\Mvc\Controller\CommandController; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; /** * Async Reference Index Commands @@ -15,33 +19,48 @@ * based on the queue maintained by the DataHandler * override shipped with this extension. */ -class AsyncReferenceIndexCommandController extends CommandController +class AsyncReferenceIndexCommand extends Command { use ReferenceIndexQueueAware; const LOCKFILE = 'typo3temp/var/reference-indexing-running.lock'; + /** + * Configure the asynchronous reference indexing command + */ + protected function configure() + { + $this->setDescription('Update the reference index'); + $this->addOption('force', 'f', InputOption::VALUE_NONE, 'Index directly to sys_refindex without asynchronous indexing'); + $this->addOption('check', 'c', InputOption::VALUE_NONE, 'Check reference index without modification if indexing directly to sys_refindex'); + $this->addOption('silent', 's', InputOption::VALUE_NONE, 'Suppress output if indexing directly to sys_refindex'); + } + /** * Update Reference Index * - * Updates the reference index - if providing the -f parameter the - * indexing will index directly to sys_refindex - else the + * Updates the reference index - if providing the --force option the + * indexing will index directly to sys_refindex, additional --check + * option will only check sys_refindex without modification, --silent + * option will suppress output * - * @param boolean $force - * @param boolean $check - * @param boolean $silent + * @param InputInterface $input + * @param OutputInterface $output * * @return void */ - public function updateCommand($force = false, $check = false, $silent = false) { - if ($force) { + protected function execute(InputInterface $input, OutputInterface $output) { + if ($input->getOption('force')) { AsyncReferenceIndex::captureReferenceIndex(false); $refIndexObj = GeneralUtility::makeInstance(ReferenceIndex::class); - $refIndexObj->updateIndex($check, !$silent); + $refIndexObj->updateIndex($input->getOption('check'), !$input->getOption('silent')); } else { - $this->updateReferenceIndex(); + $io = new SymfonyStyle($input, $output); + $io->title($this->getDescription()); + $this->updateReferenceIndex($io); } + return 0; } /** @@ -51,27 +70,28 @@ public function updateCommand($force = false, $check = false, $silent = false) { * processing the queue maintained by * the overridden DataHandler class. * + * @param SymfonyStyle $io * @return void */ - protected function updateReferenceIndex() + protected function updateReferenceIndex(SymfonyStyle $io) { $lockFile = GeneralUtility::getFileAbsFileName(static::LOCKFILE); if (file_exists($lockFile)) { - $this->response->setContent('Another process is updating the reference index - skipping' . PHP_EOL); + $io->writeln('Another process is updating the reference index - skipping'); return; } $count = $this->performCount('tx_asyncreferenceindexing_queue'); if (!$count) { - $this->response->setContent('No reference indexing tasks queued - nothing to do.' . PHP_EOL); + $io->writeln('No reference indexing tasks queued - nothing to do.'); return; } $this->lock(); - $this->response->setContent( - 'Processing reference index for ' . $count . ' record(s)' . PHP_EOL + $io->writeln( + 'Processing reference index for ' . $count . ' record(s)' ); // Note about loop: a fresh instance of ReferenceIndex is *intentional*. The class mutates @@ -102,12 +122,12 @@ protected function updateReferenceIndex() ); } - $this->response->appendContent('Reference indexing complete!' . PHP_EOL); + $io->writeln('Reference indexing complete!'); $this->unlock(); } catch (\Exception $error) { - $this->response->appendContent('ERROR! ' . $error->getMessage() . ' (' . $error->getCode() . ')' . PHP_EOL); + $io->writeln('ERROR! ' . $error->getMessage() . ' (' . $error->getCode() . ')'); $this->unlock(); } diff --git a/Classes/DataHandling/DataHandler.php b/Classes/DataHandling/DataHandler.php index 6385d1b..deb2b5c 100644 --- a/Classes/DataHandling/DataHandler.php +++ b/Classes/DataHandling/DataHandler.php @@ -8,7 +8,7 @@ * * Override of core's DataHandler to remove capability to do on-the-fly * reference indexing, instead delegating that task to the provided - * command controller. The command controller can be used directly from + * Symfony Console Command. The command can be used directly from * CLI, put into crontab or via the Scheduler system extension. * * The runs can be scheduled as frequently as desired. Indexing will only diff --git a/Classes/Database/ReferenceIndex.php b/Classes/Database/ReferenceIndex.php index f9f1f5d..6de108e 100644 --- a/Classes/Database/ReferenceIndex.php +++ b/Classes/Database/ReferenceIndex.php @@ -8,7 +8,7 @@ * * Override of core's DataHandler to remove capability to do on-the-fly * reference indexing, instead delegating that task to the provided - * command controller. The command controller can be used directly from + * Symfony Console Command. The command can be used directly from * CLI, put into crontab or via the Scheduler system extension. * * The runs can be scheduled as frequently as desired. Indexing will only diff --git a/Classes/EventListener/IsTableExcludedFromReferenceIndexEventListener.php b/Classes/EventListener/IsTableExcludedFromReferenceIndexEventListener.php new file mode 100644 index 0000000..6ed0f19 --- /dev/null +++ b/Classes/EventListener/IsTableExcludedFromReferenceIndexEventListener.php @@ -0,0 +1,28 @@ +isTableExcluded()) { + return; + } + $excludeTablesFromReferenceIndexing = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('asynchronous_reference_indexing', 'excludeTablesFromReferenceIndexing'); + if (empty($excludeTablesFromReferenceIndexing)) { + return; + } + $excludeTableArray = GeneralUtility::trimExplode(',', $excludeTablesFromReferenceIndexing); + if (in_array($event->getTable(), $excludeTableArray)) { + $event->markAsExcluded(); + } + } +} diff --git a/Classes/Slot/ReferenceIndexSlot.php b/Classes/Slot/ReferenceIndexSlot.php deleted file mode 100644 index 25edf75..0000000 --- a/Classes/Slot/ReferenceIndexSlot.php +++ /dev/null @@ -1,37 +0,0 @@ - [ + 'class' => \NamelessCoder\AsyncReferenceIndexing\Command\AsyncReferenceIndexCommand::class, + ], +]; diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml new file mode 100644 index 0000000..e9e1c09 --- /dev/null +++ b/Configuration/Services.yaml @@ -0,0 +1,14 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + + NamelessCoder\AsyncReferenceIndexing\: + resource: '../Classes/*' + + NamelessCoder\AsyncReferenceIndexing\EventListener\IsTableExcludedFromReferenceIndexEventListener: + tags: + - name: event.listener + identifier: 'asynchronous_reference_indexing/IsTableExcludedFromReferenceIndexEventListener' + event: TYPO3\CMS\Core\DataHandling\Event\IsTableExcludedFromReferenceIndexEvent diff --git a/README.md b/README.md index 97ee259..2c800d1 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,10 @@ Provides a couple of things: * A similar override for the ReferenceIndex class which replaces methods called also outside of DataHandler, to catch those cases. * An SQL table storing queued reference index updates. -* A CommandController which can be executed via CLI to process queued reference indexing +* A Symfony Command which can be executed via CLI or scheduler to process queued reference indexing without running into timeout or long wait issues. -* Provides option to exclude tables from reference indexing (only on TYPO3 8.6+). See extension configuration. - +* Provides option to exclude tables from reference indexing. See extension configuration. + Depending on how often your editors perform record imports, copies, deletions etc. this can over time save many, many hours of waiting for the TYPO3 backend to respond. @@ -42,14 +42,14 @@ Word of warning Failing to update the reference index can have negative effects on your site in some cases, both in frontend and backend. You are advised to add a scheduler task or cronjob for the included -command controller *and set the frequency to a very low value such as once every minute*. The -controller maintains a lock file and prevents parallel executions, so frequent runs are safe. +Symfony Console Command *and set the frequency to a very low value such as once every minute*. The +command maintains a lock file and prevents parallel executions, so frequent runs are safe. Note that this extension consistently captures all of the current reference indexing, including that which you can trigger using the existing (non-Extbase) CLI command or via the "DB check" backend module which is added when you install the `lowlevel` system extension. Using either of these methods to force reference index updating will instead fill the queue for the command -controller included with *this* extension so that all existing records which have relations +included with *this* extension so that all existing records which have relations will be processed on the next run. Possible side effects @@ -59,7 +59,7 @@ Delaying update of the reference index has one main side effect: if the editor t record whose relations have not been indexed, an appropriate warning may not be shown. Secondary side effect is in listing of relationships between records. Such information will be -updated only when the command controller runs. +updated only when the command runs. Frontend rendering should not be affected negatively. @@ -67,16 +67,16 @@ Usage ----- To re-index a site from scratch you would normally execute the following command, if you have -a lot of garbage in the sys_refindex table you might wan't to truncate it before: +a lot of garbage in the sys_refindex table you might want to truncate it before: ``` -TYPO3_PATH_ROOT=$PWD/web vendor/bin/typo3cms asyncreferenceindex:update --force 1 +TYPO3_PATH_ROOT=$PWD/web vendor/bin/typo3cms asynchronous_reference_indexing:index --force ``` Afterwards you can update the sys_refindex by executing the command: ``` -TYPO3_PATH_ROOT=$PWD/web vendor/bin/typo3cms asyncreferenceindex:update +TYPO3_PATH_ROOT=$PWD/web vendor/bin/typo3cms asynchronous_reference_indexing:index ``` Alternatively you can setup a Scheduler Task to execute the command at a certain interval. diff --git a/ext_emconf.php b/ext_emconf.php index eb55e48..3905dbe 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -1,32 +1,19 @@ 'Asynchronous Reference Indexing', - 'description' => 'Delegates reference indexing to a command controller (scheduler compatible) to avoid major performance issues on very large setups or large database operations.', + 'description' => 'Delegates reference indexing to a Symfony Console Command (scheduler compatible) to avoid major performance issues on very large setups or large database operations.', 'category' => 'misc', 'author' => 'Claus Due', 'author_email' => 'claus@namelesscoder.net', 'author_company' => '', - 'shy' => '', - 'dependencies' => '', - 'conflicts' => '', - 'priority' => '', - 'module' => '', 'state' => 'beta', - 'internal' => '', - 'uploadfolder' => 0, - 'createDirs' => '', - 'modify_tables' => '', - 'clearCacheOnLoad' => 0, - 'lockType' => '', 'version' => '2.1.0', 'constraints' => [ 'depends' => [ - 'php' => '7.0.0-7.3.99', - 'typo3' => '8.7.0-9.99.99', + 'php' => '7.2.0-7.4.99', + 'typo3' => '10.4.0-10.4.99', ], 'conflicts' => [], 'suggests' => [], ], - 'suggests' => [], - '_md5_values_when_last_written' => '', ]; diff --git a/ext_localconf.php b/ext_localconf.php index 38a23ae..76dbc46 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -6,18 +6,3 @@ $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][\TYPO3\CMS\Core\Database\ReferenceIndex::class]['className'] = \NamelessCoder\AsyncReferenceIndexing\Database\ReferenceIndex::class; - -$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['extbase']['commandControllers'][] = - \NamelessCoder\AsyncReferenceIndexing\Command\AsyncReferenceIndexCommandController::class; - -$_EXTCONF = unserialize($_EXTCONF); -$GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$_EXTKEY] = $_EXTCONF; - -// Register signal -$dispatcher = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class); -$dispatcher->connect( - \TYPO3\CMS\Core\Database\ReferenceIndex::class, - 'shouldExcludeTableFromReferenceIndex', - \NamelessCoder\AsyncReferenceIndexing\Slot\ReferenceIndexSlot::class, - 'shouldExcludeTableFromReferenceIndex' -); \ No newline at end of file