TYPO3: Disable cache of custom DataProcessor output - typo3

I have an own DataProcessor, fetching and returing a random record.
This works as long as cache is disabled. How can I disable caching of this specific dataprocessor to display another result on every page load?
Like COA_INT would do?
My DataProcessor:
public function process(
ContentObjectRenderer $cObj,
array $contentObjectConfiguration,
array $processorConfiguration,
array $processedData): array
{
$heroResult = $this->heroQueryBuilder->fetchRandom($cObj->data['uid']);
$heros = $this->dataMapper->map(Hero::class, $heroResult);
if (count($heros)) {
$hero = $heros[0];
if ($hero instanceof Hero) {
$targetVariableName = $cObj->stdWrapValue('as', $processorConfiguration, 'hero');
$processedData[$targetVariableName] = $hero;
}
}
return $processedData;
}
My query builder statement, returning one result sorted random:
public function fetchRandom(int $pageUid, int $limit = 1): array
{
return $this->queryBuilder
->select('*')
->where(
$this->queryBuilder->expr()->eq('pid', $this->queryBuilder->createNamedParameter($pageUid, \PDO::PARAM_INT))
)
->addSelectLiteral('rand() AS random_sort')
->orderBy('random_sort')
->from(self::TABLE_NAME)
->setMaxResults($limit)
->execute()
->fetchAll();
}
Calling the dataProcessor in my page object:
page = PAGE
page {
10 = FLUIDTEMPLATE
10 {
dataProcessing {
20 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor
20 {
special = list
special.value = 1
as = rootpage
dataProcessing {
10 = Vendor\Extension\DataProcessing\HeroProcessor
10 {
as = hero
}
}
}
}
}
}
Rendering the result in my page layout
<f:if condition="{rootpage.0.hero}">
<div class="c-page__hero">
<f:render partial="Page/Hero/Hero" arguments="{hero:rootpage.0.hero}" />
</div>
</f:if>
How can I disable caching just for this specific DataProcessor? Do I have to convert the cObj e.g. by calling $cObj->convertToUserIntObject()? Any ideas?

Solved by creating a COA_INT object. Thought, there might be a better way without having to use f:cObject viewhelper.
lib.hero = COA_INT
lib.hero {
10 = FLUIDTEMPLATE
10 {
file = {$pmWebsite.view.partialRootPath}/Page/Hero/Hero.html
dataProcessing {
10 = Vendor\Extension\DataProcessing\HeroProcessor
10 {
pid = 1
as = hero
}
}
}
}

Related

TYPO3: clear cache by tag for custom content element when record in backend is changed

I have a sysfolder with records which get displayed in the frontend via a custom content element.
Now I have the problem that the frontend is not updated when a new record is added or an existing record is changed.
To clear the cache I'm using a hook in ext_localconf.php:
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc']['foobar'] =
\Vendor\Name\Hooks\DataHandler::class . '->clearCachePostProc';
The hook looks like this:
<?php
namespace Vendor\Name\Hooks;
use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class DataHandler implements SingletonInterface
{
public function clearCachePostProc(array $params): void
{
if (isset($params['table']) && $params['table'] === 'tx_foo_domain_model_bar') {
$cacheManager = GeneralUtility::makeInstance(CacheManager::class);
$cacheManager->flushCachesByTag('1642782027');
}
}
}
And the content element is implemented with FLUIDTEMPLATE and a dataprocessor:
tt_content {
foo_bar =< lib.contentElement
foo_bar {
templateName = myTemplate
stdWrap.cache {
key = tx_foo_domain_model_bar
tags = 1642782027
lifetime = default
}
dataProcessing {
10 = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
10 {
table = tx_foo_domain_model_bar
pidInList = {$foo.storage_pid}
as = foobar
}
}
}
}
Everything seems to work but when I hit Cmd+R/Ctrl+R and reload the page or visit it again via the navigation, the page is not updated with the latest content.
I implemented the following solution.
Within the Typoscript Setup add a addPageCacheTag to your custom content element:
tt_content {
foo_bar =< lib.contentElement
foo_bar {
templateName = FooBar
dataProcessing {
10 = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
10 {
table = tx_foo_domain_model_bar
pidInList = {$foo.storage_pid}
as = foobar
}
}
stdWrap.addPageCacheTags = foo_bar
}
}
Add the clearCacheCmd to the Page TSConfig from the storage page like:
TCEMAIN.clearCacheCmd = cacheTag:foo_bar
For testing, add this to your Fluid-Template and be sure to test with a separate browser (without BE login):
<div style="color: red;"><f:format.date format="d.m.Y - H:i:s">now</f:format.date></div>
Kudos to https://daniel-siepmann.de/posts/2019/typo3-content-caching.html
You could use the EXT:Fluid Page Cache
https://extensions.typo3.org/extension/fluid_page_cache
This EXT does all the things you need automatically.

TYPO3 MenuProcessor with dynamic uid

I want to use the MenuProcessor dynamic in my fluidtemplate.
Configured in TypoScript, I want to call it with the cObject ViewHelper and pass the uid of a page to it:
{f:cObject(typoscriptObjectPath: 'lib.menuTest', data:{menuId:'28'})}
This is what I have tried - it should be a special = directory with the the given uid in special.value = XXXXXX.
lib {
menuTest = FLUIDTEMPLATE
menuTest {
templateName = MenuTest
templateRootPaths {
10 = EXT:hatemplate/Resources/Private/Templates/
}
dataProcessing {
10 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor
10 {
special = directory
special.value = XXXXXX
levels = 1
as = menuItems
}
}
}
}
If I set a uid directly it works, but I don't know how to insert the variable. Has anyone a hint or a working solution?
Thank you
I have solved it with the help of a friend who is much more experienced in TypoScript.
I wasn't as wrong as I thought.
This is the Code in TypoScript. I added the tamplate,layout and partial paths for future copy/pasting :) :
lib {
menuDirectory = FLUIDTEMPLATE
menuDirectory {
templateName = MenuDirectory
layoutRootPaths {
10 = EXT:hatemplate/Resources/Private/Layouts/
}
templateRootPaths {
10 = EXT:hatemplate/Resources/Private/Templates/
}
partialRootPaths {
10 = EXT:hatemplate/Resources/Private/Partials/
}
dataProcessing {
10 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor
10 {
special = directory
special.value.field = menuId
levels = 1
as = directory
}
}
}
}
With this configured you can use the f:cObject ViewHelper like this:
<f:cObject typoscriptObjectPath="lib.menuDirectory" data="{menuId:1}" />
Or inline
{f:cObject(typoscriptObjectPath: 'lib.menuDirectory', data:{menuId:1})}
This renders the Items into the fluidtemplate:

Pass Fluid variable to TypoScript

I want to pass a variable (uid of category) in Fluid to a TypoScript :
<f:cObject typoscriptObjectPath="lib.testFluid" data="{setting.myvar}/>
Then i want to use the var to get all content elements in folder with pid 942 and the category {setting.myvar}
lib.testFluid = COA
lib.testFluid = CONTENT
lib.testFluid {
table = tt_content
select {
pidInList = 942
where = selected_categories = |
}
}
This does not work, it creates an MySql syntax error. I also tried using current = 1 instead of the where clause without success. I looked at post TYPO3: pass variable to typoscript via cObject? and I can recreate it but it does not work with my script. (TYPO3 8)
If i use
...
where = selected_categories = 13
....
the scrip will succesfully display all CE with category 13. How do i make it work with a var?
could you try this:
<f:cObject typoscriptObjectPath="lib.testFluid" data="{myvar: setting.myvar}/>
lib.testFluid = CONTENT
lib.testFluid {
table = tt_content
select {
pidInList = 942
where.data = field:myvar
where.intval = 1
where.wrap = selected_categories=|
}
}
hard to test for me but it might work ...
I had to solve it once with markers. I couldn't find another simpler way. I give you a very general solution which you may adapt to your needs. For example you could set the pid value via a typoscript setting which is more elegant than to put it in the snippet code. Please try:
<f:cObject typoscriptObjectPath="lib.testFluid" data="{category: setting.myvar, catPid: 942}" currrentValueKey="category" />
The related TypoScript snippet:
lib.testFluid = COA
lib.testFluid {
10 = LOAD_REGISTER
10 {
category.cObject = TEXT
category.cObject.value.current = 1
catPid.cObject = TEXT
catPid.cObject.value.dataWrap = { field: catPid }
}
20 = CONTENT
20 {
table = tt_content
select {
pidInList.cObject = TEXT
pidInList.cObject.dataWrap = {REGISTER:catPid}
where = selected_categories=###category###
markers {
category.data = REGISTER:category
}
}
}
30 = RESTORE_REGISTER
}

Typo3 mbl_newsevent post date to URL

I got an event glossary at my site and the user needs to register for that events with powermail. I post the event title to the URL and then the title gets inserted into a input field at my powermail form. But now I need the date of the event, too.
This is my code so far:
plugin.tt_news.mbl_newsevent{
registrationLink_typolink {
additionalParams.cObject = COA
additionalParams.cObject {
10 = TEXT
10 {
value = &tx_powermail_pi1[veranstaltung]={field:title}
insertData = 1
}
20 = TEXT
20 {
value = {field:tx_mblnewsevent_from}
insertData = 1
stdWrap.date = d.m.Y
stdWrap.outerWrap = &tx_powermail_pi1[datum]=|
}
}
}
output is (URL): http://preview.eloum.de/anmeldung-infotag/?tx_powermail_pi1%5Bveranstaltung%5D=Infotag%20eBusiness-Lotse%20Oberschwaben-Ulm%2028.03.2014&tx_powermail_pi1%5Bdatum%5D=01.01.1970
why 01.01.1970?
the timestamp is correct without that stdWrap.date = d.m.Y
EDIT: got it!
My solutioN:
plugin.tt_news.mbl_newsevent{
registrationLink_typolink {
additionalParams.cObject = COA
additionalParams.cObject {
10 = TEXT
10 {
value = &tx_powermail_pi1[veranstaltung]={field:title}
insertData = 1
}
20 = TEXT
20 {
field = tx_mblnewsevent_from
insertData = 1
date = d.m.Y
wrap = &tx_powermail_pi1[datum]=|
}
}
}}
Can you try
registrationLink_typolink {
...
additionalParams= &tx_powermail_pi1[veranstaltung]={field:title}&tx_powermail_pi1[datum]={field:datetime}
additionalParams.insertData = 1
}
... here's another try. I haven't tested it and I'll have to leave it there. So maybe you have to make some modifications. The general idea is that, as you can't say $date = makereadabledate($timestamp); and then use that in your template (as TS isn't a real programming language), you build the value you want to pass to additionalParams as a so called "cObject". In there, you can parse and wrap it. In the end, you would pass your string for further use to additionalParams. Hope you can make it work!
registrationLink_typolink {
...
additionalParams.cObject = COA
additionalParams.cObject {
10 = TEXT
10 {
value = &tx_powermail_pi1[veranstaltung]={field:title}
insertData = 1
}
20 = TEXT
20 {
value = {field.datetime} // or use "data"
insertData = 1
stdWrap.date = d.m.Y
stdWrap.outerWrap = &tx_powermail_pi1[datum]=|
// outerWrap: maybe not even necessary, the idea is not to interfere with the created string
// cf. http://blog.bartlweb.net/2011/02/die-reihenfolge-der-wichtigsten-wraps-in-typo3/
}
}
}

typoscript issue with LOAD_REGISTER and if condition to fill a list

What I'm trying to do is quite complex and an Extbase extension is involved...
Step by step, what I'm trying to do:
An Extbase plugin decides, if certain navigation elements should be marked.
This plugin has one action for each navigation element.
The returned value (0 or 1) from each action in TS is stored on the stack (LOAD_REGISTER).
A list of page UIDs is build by checking against the stored values (0,1).
The navigation COA is modified using this list of page UIDs.
Here is the typoscript code I'm using:
// load information, if pages lack info, into register
10 = LOAD_REGISTER
10 {
lacksAnfahrt {
cObject = USER_INT
cObject {
userFunc = tx_extbase_core_bootstrap->run
pluginName = Pa_klinik_data_edit
extensionName = Hplusinfo
controller = SpitalInfoPA
switchableControllerActions {
SpitalInfoPA {
1 = completeAnfahrt
}
}
}
}
lacksAktivitaeten < .lacksAnfahrt
lacksAktivitaeten.cObject.switchableControllerActions.SpitalInfoPA.1 = completeAktivitaeten
lacksBildergalerie < .lacksAnfahrt
lacksBildergalerie.cObject.switchableControllerActions.SpitalInfoPA.1 = completeBildergalerie
// build a list of PIDs that are going to be marked in navigation
lackPIDs.cObject = COA
lackPIDs.cObject {
10 = TEXT
10 {
value = {$config.PIDLists.anfahrt},
if {
value = 1
equals.data = register:lacksAnfahrt
}
}
20 < .10
20.value = {$config.PIDLists.bildergalerie},
20.if.equals.data = register:lacksBildergalerie
30 < .10
30.value = {$config.PIDLists.aktivitaeten},
30.if.equals.data = register:lacksAktivitaeten
// don't let the comma separated list end with a comma
99 = TEXT
99.value = 0
} // lackPIDs
} // REGISTER
// mark incomplete pages with a red exclamation mark
20 { // = HMENU
1 { // = TMENU
NO { // = 1
stdWrap.wrap = |<span class="warning lacksInfo">!</span>
stdWrap.wrap.if {
value.data = register:lackPIDs
isInList.field = uid
}
}
}
}
If i print out register:lacksBildergalerie and all the others, their values are correct (0 or 1).
But the lackPIDslist always empty (except of the 0 at the end)... There must be something wrong with the middle part:
10 {
value = {$nav.PIDLists.anfahrt},
if {
value = 1
equals.data = register:lacksAnfahrt
}
}
This evaluation seams to return false in any case.
I also tried with different if function like:
10 {
value = {$nav.PIDLists.anfahrt},
if {
isTrue.data = register:lacksAnfahrt
}
}
But this doesn't solve the problem.
Just overlooked that the other registers are using USER_INT as well