Johannes Laxander Ich habe heute Nacht eine Weile mit deinem Use-Case experimentiert und eine funktionierende Lösung gefunden.
So sieht der Hook nun aus:
<?php
declare(strict_types=1);
namespace Khpy\Khpy03sitepackage\Hooks;
use Doctrine\DBAL\Exception;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Utility\GeneralUtility;
final class DatamapAutoEndtime
{
/**
* Setzt das Enddatum automatisch anhand der verknüpften Offers,
* falls es noch nicht gesetzt ist.
*
* @param array<string,mixed> $fieldArray Eingehende Werte des Datensatzes
* @throws Exception
*/
public function processDatamap_preProcessFieldArray(
array &$fieldArray,
string $table,
?string $id,
DataHandler $dataHandler
): void {
// Nur für tt_content
if ($table !== 'tt_content') {
return;
}
// Nur für CType 'khpy_lastminute-offers'
if (($fieldArray['CType'] ?? '') !== 'khpy_lastminute-offers') {
return;
}
$latestOfferEnddateFromDatabase = 0;
// Maximales offer_enddate der referenzierten Offers aus DB lesen
$offerUids = GeneralUtility::intExplode(',', $fieldArray['lastminute_offers']);
if (!empty($offerUids)) {
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
$queryBuilder = $connectionPool->getQueryBuilderForTable('lastminute_offers');
$latestOfferEnddateFromDatabase = (int)$queryBuilder
->selectLiteral('MAX(offer_enddate)')
->from('lastminute_offers')
->where(
$queryBuilder->expr()->in(
'uid',
$queryBuilder->createNamedParameter($offerUids, Connection::PARAM_INT_ARRAY)
)
)
->executeQuery()
->fetchOne();
}
// Enddaten aus der aktuellen DataHandler-Datamap berücksichtigen
$latestOfferEnddateFromDatamap = 0;
foreach ($dataHandler->datamap['lastminute_offers'] ?? [] as $offerRow) {
$offerEnddateValue = $offerRow['offer_enddate'] ?? null;
if (!$offerEnddateValue) {
continue;
}
$offerEnddateTimestamp = is_numeric($offerEnddateValue)
? (int)$offerEnddateValue
: strtotime((string)$offerEnddateValue);
if ($offerEnddateTimestamp > $latestOfferEnddateFromDatamap) {
$latestOfferEnddateFromDatamap = $offerEnddateTimestamp;
}
}
// Wenn Datamap aktueller ist → überschreiben
if ($latestOfferEnddateFromDatamap > $latestOfferEnddateFromDatabase) {
$latestOfferEnddateFromDatabase = $latestOfferEnddateFromDatamap;
}
// Kein valides Enddatum gefunden → nichts tun
if ($latestOfferEnddateFromDatabase === 0) {
return;
}
// Enddatum 3 Tage vorher berechnen
$calculatedEndtime = $latestOfferEnddateFromDatabase - 3 * 24 * 60 * 60;
// Optional: nicht in die Vergangenheit setzen
$now = time();
if ($calculatedEndtime < $now) {
$calculatedEndtime = $now;
}
// Enddatum ins Record-Feld schreiben
$fieldArray['endtime'] = $calculatedEndtime;
}
}
In deiner bisherigen Beschreibung unklar war, wie damit umgegangen werden soll, wenn mehrere lastminute_offers Datensätze vorkommen. Der Hook nimmt in diesem Fall das größte gefundene Datum als Basis für die Berechnung.
Darüber hinaus wird nun auch berücksichtigt, wenn vor dem Speichern noch Veränderungen vorgenommen wurden. Diese Informationen stehen in der sog. Datamap des Datahandlers aufrufbar über $dataHandler->datamap.
Noch unklar war mir, ob offer_expirydate auch eine Rolle spielen sollte. Das habe ich aber erstmal nicht eingebaut, da bisher nur die Rede von offer_enddate war.
Hier auch mal meine funktionierende Test-Extension:
Ich denke damit hast du die perfekte Basis für weitere Anpassungen. Die Frage, wie Feldwerte automatisch in Abhängigkeit von anderen Feldwerten definiert werden können - und das sogar aus IRRE-Kindelementen - würde ich damit als geklärt betrachten 😉
Edit:
Die zusätzlichen Bedingungen im ->where() statement des Query Builders:
$queryBuilder->expr()->eq('deleted', 0),
$queryBuilder->expr()->eq('hidden', 0)
habe ich nun entfernt, da dieser standardmäßig bereits die folgenden "Restrictions" mitbringt:
\TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
\TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction
\TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction
\TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction
Es wird also standardmäßig bereits die Frontend-Relevanz, sogar nach Start-/Endtime-Konfiguration, berücksichtigt.
→ siehe Dokumentation: Restriction builder → Main construct