[BUGFIX] Coalesce hook calls in DataHandler::processRemapStack() 52/51552/17
authorThomas Hohn <thomas@hohn.dk>
Mon, 6 Feb 2017 11:01:25 +0000 (12:01 +0100)
committerOliver Hader <oliver.hader@typo3.org>
Thu, 30 Mar 2017 10:16:02 +0000 (12:16 +0200)
DataHandler's hook processDatamap_afterDatabaseOperations is processed
in two ways. In case modifications do not contain any new relation that
just has been created, the hook is executed directly. If that's not the
case, executing this hook is deferred and will happen after the remap
stack has been processed.

Calling the hook directly happens exactly once for each modified record,
where invocations in DataHandler::processRemapStack() might happen more
than once, depending on the amount of relation fields that contain new
references and have been remapped.

This change coalesces these invocations which results that the hooks
processDatamap_afterDatabaseOperations is exactly called once for each
modified record - which is the expected behavior.

Change-Id: Ib7e65ce170c8f9ba8f7577b79073b1ed9213a0b9
Resolves: #79635
Releases: master, 7.6
Reviewed-on: https://review.typo3.org/51552
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Reviewed-by: Thomas Hohn <thomas@hohn.dk>
Tested-by: Thomas Hohn <thomas@hohn.dk>
Reviewed-by: Frank Naegler <frank.naegler@typo3.org>
Tested-by: Oliver Hader <oliver.hader@typo3.org>
Reviewed-by: Oliver Hader <oliver.hader@typo3.org>
typo3/sysext/core/Classes/DataHandling/DataHandler.php
typo3/sysext/core/Tests/Functional/DataHandling/DataHandler/HookTest.php

index 3a669f4..ecb3f74 100644 (file)
@@ -6076,6 +6076,7 @@ class DataHandler
         // Processes the remap stack:
         if (is_array($this->remapStack)) {
             $remapFlexForms = [];
+            $hookPayload = [];
 
             foreach ($this->remapStack as $remapAction) {
                 // If no position index for the arguments was set, skip this remap action:
@@ -6151,18 +6152,18 @@ class DataHandler
 
                     $remapFlexForms[$flexFormId][$flexFormPath] = $newValue;
                 }
-                // Process waiting Hook: processDatamap_afterDatabaseOperations:
+
+                // Collect elements that shall trigger processDatamap_afterDatabaseOperations
                 if (isset($this->remapStackRecords[$table][$rawId]['processDatamap_afterDatabaseOperations'])) {
                     $hookArgs = $this->remapStackRecords[$table][$rawId]['processDatamap_afterDatabaseOperations'];
-                    // Update field with remapped data:
-                    $hookArgs['fieldArray'][$field] = $newValue;
-                    // Process waiting hook objects:
-                    $hookObjectsArr = $hookArgs['hookObjectsArr'];
-                    foreach ($hookObjectsArr as $hookObj) {
-                        if (method_exists($hookObj, 'processDatamap_afterDatabaseOperations')) {
-                            $hookObj->processDatamap_afterDatabaseOperations($hookArgs['status'], $table, $rawId, $hookArgs['fieldArray'], $this);
-                        }
+                    if (!isset($hookPayload[$table][$rawId])) {
+                        $hookPayload[$table][$rawId] = [
+                            'status' => $hookArgs['status'],
+                            'fieldArray' => $hookArgs['fieldArray'],
+                            'hookObjects' => $hookArgs['hookObjectsArr'],
+                        ];
                     }
+                    $hookPayload[$table][$rawId]['fieldArray'][$field] = $newValue;
                 }
             }
 
@@ -6171,6 +6172,23 @@ class DataHandler
                     $this->updateFlexFormData($flexFormId, $modifications);
                 }
             }
+
+            foreach ($hookPayload as $tableName => $rawIdPayload) {
+                foreach ($rawIdPayload as $rawId => $payload) {
+                    foreach ($payload['hookObjects'] as $hookObject) {
+                        if (!method_exists($hookObject, 'processDatamap_afterDatabaseOperations')) {
+                            continue;
+                        }
+                        $hookObject->processDatamap_afterDatabaseOperations(
+                            $payload['status'],
+                            $tableName,
+                            $rawId,
+                            $payload['fieldArray'],
+                            $this
+                        );
+                    }
+                }
+            }
         }
         // Processes the remap stack actions:
         if ($this->remapStackActions) {
index 6c72ea7..ee32d0e 100644 (file)
@@ -204,13 +204,6 @@ class HookTest extends AbstractDataHandlerActionTestCase
                     'fieldArray' => [
                         'header' => 'Testing #1',
                         self::FIELD_ContentHotel => 1,
-                    ],
-                ],
-                // @todo Fix the double invocation for this tt_content record
-                [
-                    'table' => self::TABLE_Content,
-                    'fieldArray' => [
-                        'header' => 'Testing #1',
                         self::FIELD_Categories => 1,
                     ],
                 ],