[BUGFIX] Catch DatabaseRecordException when editing record with deleted relation 50/55450/3
authorAndreas Fernandez <a.fernandez@scripting-base.de>
Wed, 24 Jan 2018 15:50:17 +0000 (16:50 +0100)
committerAndreas Wolf <andreas.wolf@typo3.org>
Sat, 27 Jan 2018 18:34:52 +0000 (19:34 +0100)
Editing a record with a deleted related record leads to an uncaught
DatabaseRecordException. This patch catches the exception and
ignores such records, but logs a warning.

Due to possible errors occurring with certain DBMS (e.g. MySQL
strict) columns may require a default value now in TCA.

Resolves: #83412
Releases: master, 8.7
Change-Id: I5adaf385443350ce245dd83da6e5f1a16d9c9afb
Reviewed-on: https://review.typo3.org/55450
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Reiner Teubner <rteubner@me.com>
Tested-by: Reiner Teubner <rteubner@me.com>
Reviewed-by: Andreas Wolf <andreas.wolf@typo3.org>
Tested-by: Andreas Wolf <andreas.wolf@typo3.org>
typo3/sysext/backend/Classes/Form/FormDataProvider/TcaInline.php

index ae42629..57056ad 100644 (file)
@@ -23,6 +23,8 @@ use TYPO3\CMS\Backend\Form\InlineStackProcessor;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Database\RelationHandler;
+use TYPO3\CMS\Core\Log\Logger;
+use TYPO3\CMS\Core\Log\LogManager;
 use TYPO3\CMS\Core\Messaging\FlashMessage;
 use TYPO3\CMS\Core\Messaging\FlashMessageService;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -156,7 +158,20 @@ class TcaInline extends AbstractDatabaseRecordProvider implements FormDataProvid
             // @todo: format of databaseRow for this field and separate the child compilation to an own provider?
             if ($result['inlineCompileExistingChildren']) {
                 foreach ($connectedUids as $childUid) {
-                    $result['processedTca']['columns'][$fieldName]['children'][] = $this->compileChild($result, $fieldName, $childUid);
+                    try {
+                        $result['processedTca']['columns'][$fieldName]['children'][] = $this->compileChild($result, $fieldName, $childUid);
+                    } catch (DatabaseRecordException $e) {
+                        // The child could not be compiled, probably it was deleted and a dangling mm record exists
+                        $this->getLogger()->warning(
+                            $e->getMessage(),
+                            [
+                                'table' => $childTableName,
+                                'uid' => $childUid,
+                                'exception' => $e
+                            ]
+                        );
+                        continue;
+                    }
                 }
             }
         } elseif ($mode === 'keep') {
@@ -203,21 +218,60 @@ class TcaInline extends AbstractDatabaseRecordProvider implements FormDataProvid
                 // localized but miss default language record
                 $fieldNameWithDefaultLanguageUid = $GLOBALS['TCA'][$childTableName]['ctrl']['transOrigPointerField'];
                 foreach ($connectedUidsOfLocalizedOverlay as $localizedUid) {
-                    $localizedRecord = $this->getRecordFromDatabase($childTableName, $localizedUid);
+                    try {
+                        $localizedRecord = $this->getRecordFromDatabase($childTableName, $localizedUid);
+                    } catch (DatabaseRecordException $e) {
+                        // The child could not be compiled, probably it was deleted and a dangling mm record exists
+                        $this->getLogger()->warning(
+                            $e->getMessage(),
+                            [
+                                'table' => $childTableName,
+                                'uid' => $localizedUid,
+                                'exception' => $e
+                            ]
+                        );
+                        continue;
+                    }
                     $uidOfDefaultLanguageRecord = $localizedRecord[$fieldNameWithDefaultLanguageUid];
                     if (in_array($uidOfDefaultLanguageRecord, $connectedUidsOfDefaultLanguageRecord)) {
                         // This localized child has a default language record. Remove this record from list of default language records
                         $connectedUidsOfDefaultLanguageRecord = array_diff($connectedUidsOfDefaultLanguageRecord, [$uidOfDefaultLanguageRecord]);
                     }
                     // Compile localized record
-                    $compiledChild = $this->compileChild($result, $fieldName, $localizedUid);
+                    try {
+                        $compiledChild = $this->compileChild($result, $fieldName, $localizedUid);
+                    } catch (DatabaseRecordException $e) {
+                        // The child could not be compiled, probably it was deleted and a dangling mm record exists
+                        $this->getLogger()->warning(
+                            $e->getMessage(),
+                            [
+                                'table' => $childTableName,
+                                'uid' => $localizedUid,
+                                'exception' => $e
+                            ]
+                        );
+                        continue;
+                    }
                     $result['processedTca']['columns'][$fieldName]['children'][] = $compiledChild;
                 }
                 if ($showPossible) {
                     foreach ($connectedUidsOfDefaultLanguageRecord as $defaultLanguageUid) {
                         // If there are still uids in $connectedUidsOfDefaultLanguageRecord, these are records that
                         // exist in default language, but are not localized yet. Compile and mark those
-                        $compiledChild = $this->compileChild($result, $fieldName, $defaultLanguageUid);
+                        try {
+                            $compiledChild = $this->compileChild($result, $fieldName, $defaultLanguageUid);
+                        } catch (DatabaseRecordException $e) {
+                            // The child could not be compiled, probably it was deleted and a dangling mm record exists
+                            $this->getLogger()->warning(
+                                $e->getMessage(),
+                                [
+                                    'table' => $childTableName,
+                                    'uid' => $defaultLanguageUid,
+                                    'exception' => $e
+                                ]
+                            );
+                            continue;
+                        }
                         $compiledChild['isInlineDefaultLanguageRecordInLocalizedParentContext'] = true;
                         $result['processedTca']['columns'][$fieldName]['children'][] = $compiledChild;
                     }
@@ -490,4 +544,12 @@ class TcaInline extends AbstractDatabaseRecordProvider implements FormDataProvid
     {
         return $GLOBALS['LANG'];
     }
+
+    /**
+     * @return Logger
+     */
+    protected function getLogger()
+    {
+        return GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
+    }
 }