For coders TYPO3 Tech Corner

[TYPO3] Caching ein- oder ausschalten in Extbase?

[TYPO3] Caching ein- oder ausschalten in Extbase?

Caches sind etwas tolles. Diese entlasten Server und damit auch die Umwelt, sofern sie gut eingesetzt werden. Dazu kommt, dass sich auch das Ranking bei Google & Co. verbessert, wenn die Seite schnell geladen werden kann. Aber Caches sind auch manchmal ganz schön nervig. Vor allem bei der Entwicklung eines neuen Features oder wenn es eine Änderung an einem Datensatz gab, aber diese Änderung erst Tage später ersichtlicht wird.

Fangen wir am besten von vorne an. Seit der Einführung von Extbase lassen sich einzelne Views ganz einfach vom Caching ausschließen, während per Default alle Views fürs Caching vorbereitet wurden. So bald eine Ansicht cachebar ist, kann diese auch in den Suchergebnissen von typo3/cms-indexed-search gefunden oder mit Hilfe der Extension staticfilecache statisch abgelegt werden. Aber selbst ohne diese Vorteile wird die Ansicht auch so schon deutlich schneller ausgeliefert.

Gibt es jedoch eine Interaktion mit Benutzereingaben im Frontend - wenn beispielsweise ein Benutzerprofil gepflegt werden soll, oder wenn ein Filter die Ergebnisse einer Listenansicht reduzieren soll - wird ein Caching schwierig bis unmöglich. Bei einem einfachen Filter könnte man sich eventuell noch mit Links mit cHash Werten behelfen, aber bei größeren Formularen stößt man schnell an die Grenzen der Machbarkeit. Im schlimmsten Fall kann man sogar Datenschutzprobleme aufreißen, wenn beispielsweise ein Bewerbungsformular gecacht werden soll, bei einem Validierungsfehler aber dann die Werte des Vorgängers plötzlich sichtbar werden (Anmerkung vom Autor: Alles schon erlebt).
Vereinfacht kann man sich also merken, dass vor allem Detailansichten gecacht werden sollten, alle anderen Ansichten - gerade mit Interaktion der Besucher - sollte man vom Caching ausnehmen, wenn man sich unsicher ist.

Das kann dann beispielsweise so aussehen in configurePlugin() - hier wird listAction() und listAjaxAction() vom Caching ausgenommen:

\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin( 'users', 'Pi1', [ \In2code\Users\Controller\UserController::class => 'list, listAjax, detail' ], [ \In2code\Users\Controller\UserController::class => 'list, listAjax' ] );

ClearCacheCmd and Cache tags

Gibt es jedoch die Möglichkeit, dass der Benutzer sein Profil im Frontend pflegen kann, soll auch gleich der Cache seiner eigenen Detailansicht verfallen. Und hier kann man wunderbar Cache Tags zum Einsatz bringen. Im nachfolgenden Beispiel werden für alle Detalansichten Tags im Cache erzeugt (Tag: tx_users_detail). Darüber hinaus auch noch individuelle Tags wie tx_users_user_123 (wobei 123 die UID des Datensatzes ist):

public function initializeDetailAction() { $cacheTags = [ 'tx_users_detail', 'tx_users_user_' . (int)$this->request->getArgument('user') ]; $tsfeController = $GLOBALS['TSFE']; if ($tsfeController !== null) { $tsfeController->addCacheTags($cacheTags); } } public function detailAction(User $user = null): void { // Detail action stuff }

Wie ihr den Cache der Detailseiten per Page TSConfig löschen könnt, sobald ein Redakteur eine Änderung im Backend (z.B. in einem Sysfolder) vornimmt, ist vielen bereits bekannt (siehe offizielle Dokumentation hierzu):

TCEMAIN.clearCacheCmd = cacheTag:tx_users_detail

Aber nun wollen wir den Cache einer einzelnen Seite löschen, sobald der Benutzer sein Profil im Frontend in einem entsprechenden Extbase-Formular geändert hat:

public function updateAction(User $user): void { $cacheManager = GeneralUtility::makeInstance(CacheManager::class); $cacheManager->flushCachesByTag('tx_users_user_' . $user->getUid()); // Update and redirect stuff }

Oder den Cache aller Detailseiten, wenn wir alle Benutzer beispielsweise nächtlich aus LDAP mit Hilfe eines Symfony command erneut importieren:

$cacheManager = GeneralUtility::makeInstance(CacheManager::class); $cacheManager->flushCachesByTag('tx_users_detail');

Übrigens: Bei der Nutzung von flushCachesByTag() werden auch statische Cachedateien gelöscht, wenn ihr die Extension staticfilecache im Einsatz habt.

Eigene Caches nutzen mit dem CacheManager

Wollt ihr Teile aus eurer ungecachten Ansicht selber cachen, könnt ihr auf den CacheManager in TYPO3 setzen. Hierzu braucht es eine Registrierung in der ext_localconf.php:

/** * Initialize cache manager configuration for EXT:users */ if (!is_array($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['users_filter'])) { $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['users_filter'] = []; }

Wenn ihr das FrontendInterface in eurer Klasse "injecten" wollt, so könnt ihr das in einer Configuration/Services.yaml definieren:

services: cache.users_filter: class: TYPO3\CMS\Core\Cache\Frontend\FrontendInterface factory: ['@TYPO3\CMS\Core\Cache\CacheManager', 'getCache'] arguments: ['users_filter']

Wenn man nun eine "teure" Datenbankabfrage über das Repository machen würde, kann man an Stelle dieser auch einen CacheService nutzen:

<?php declare(strict_types=1); namespace In2code\Users\Domain\Service; use In2code\Users\Utility\FrontendUtility; use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface; /** * Class GetFilterObjectsService */ class GetFilterObjectsService { /** * @var FrontendInterface|null */ private ?FrontendInterface $cache = null; /** * @var array */ protected array $objects = []; /** * Cache lifetime in seconds * * @var int */ protected int $lifetime = 86400; /** * GetFilterObjectsService constructor. * @param FrontendInterface $cache */ public function __construct(FrontendInterface $cache) { $this->cache = $cache; } /** * @return array */ public function get(): array { $objectsFromCache = $this->getObjectsFromCache(); if ($objectsFromCache === []) { // Call your Repository stuff here to git your objects $objects = $this->objectRepository->findBySomething(); $this->cacheObjects($objects); return $objects; } returN $objectsFromCache; } /** * @param array $objects * @return void */ protected function cacheObjects(array $objects): void { $this->cache->set( md5(FrontendUtility::getCurrentPageIdentifier() . 'users_filter_object'), $objects, ['users_filter_object'], $this->lifetime ); } /** * @return array */ protected function getObjectsFromCache(): array { return (array)$this->cache->get( md5(FrontendUtility::getCurrentPageIdentifier() . 'users_filter_object') ); } }

Und: Die offizielle Dokumentation hierzu findet ihr wieder auf der docs.typo3.org website.

Abschließend kann ich auch nur die Erweiterung staticfilecache empfehlen. Diese kann euer TYPO3 um den Faktor 230 verschnellern. Glaubt ihr nicht? Probiert es einfach mal aus. Als Sahnehäubchen oben drauf zeigt euch die Extension in einem eigenen Backend Modul auch gleich an, welche Seiten nicht gecacht werden können. So kommt ihr groben Fehlern schneller auf die Spur.

Zurück

Kennst du das: Immer nur schnell schnell?

Wie wäre es einmal mit Zeit und Respekt für Codequalität? Arbeiten im Team? Automatisierte Tests?

Komm zu uns