From bebebedbbc0332f008453a02f180910ca371536c Mon Sep 17 00:00:00 2001
From: brandonkelly <brandon@pixelandtonic.com>
Date: Fri, 8 Nov 2024 17:18:07 -0800
Subject: [PATCH] Only look for duplicate UUIDs within project config data

Resolves #16032
---
 CHANGELOG.md                                  |  1 +
 .../m230511_215903_content_refactor.php       | 33 ++++++++++++-------
 2 files changed, 22 insertions(+), 12 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5e4960b5dc8..2d712f6d87e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
 ## Unreleased
 
 - Fixed a bug where the `utils/fix-field-layout-uids` command was misidentifying missing/duplicate UUID issues.
+- Fixed an error that could occur when upgrading to Craft 5, if unused field layouts contained duplicate UUIDs. ([#16032](https://github.com/craftcms/cms/issues/16032))
 
 ## 5.4.10.1 - 2024-11-07
 
diff --git a/src/migrations/m230511_215903_content_refactor.php b/src/migrations/m230511_215903_content_refactor.php
index 67fb80b4ea2..360be680725 100644
--- a/src/migrations/m230511_215903_content_refactor.php
+++ b/src/migrations/m230511_215903_content_refactor.php
@@ -3,10 +3,10 @@
 namespace craft\migrations;
 
 use Craft;
-use craft\db\Migration;
 use craft\db\Query;
 use craft\db\Table;
 use craft\elements\User;
+use craft\fieldlayoutelements\CustomField;
 use craft\helpers\ArrayHelper;
 use yii\console\Exception;
 
@@ -22,18 +22,26 @@ public function safeUp(): bool
     {
         // Before anything else, be absolutely certain that all custom fields' layout elements have unique UUIDs
         $uids = [];
-        $fieldsService = Craft::$app->getFields();
-        foreach ($fieldsService->getAllLayouts() as $fieldLayout) {
-            $typeLabel = class_exists($fieldLayout->type) ? $fieldLayout->type::lowerDisplayName() : $fieldLayout->type;
-            $a = in_array(mb_strtolower($typeLabel[0] ?? ''), ['a', 'e', 'i', 'o', 'u']) ? 'An' : 'A';
-            foreach ($fieldLayout->getCustomFieldElements() as $layoutElement) {
-                if (!isset($layoutElement->uid)) {
-                    throw new Exception("$a $typeLabel field layout element is missing its UUID. Reinstall Craft CMS ^4.4.14 and run `utils/fix-field-layout-uids` before upgrading to Craft CMS 5.");
-                }
-                if (isset($uids[$layoutElement->uid])) {
-                    throw new Exception("$a $typeLabel field layout element has a duplicate UUID. Reinstall Craft CMS ^4.4.14 and run `utils/fix-field-layout-uids` before upgrading to Craft CMS 5.");
+
+        $projectConfigService = Craft::$app->getProjectConfig();
+        $layoutConfigs = array_merge(
+            array_values($projectConfigService->find(fn(array $item, string $itemPath) => str_ends_with($itemPath, '.fieldLayout'))),
+            ...array_values($projectConfigService->find(fn(array $item, string $itemPath) => str_ends_with($itemPath, '.fieldLayouts'))),
+        );
+
+        foreach ($layoutConfigs as $layoutConfig) {
+            foreach ($layoutConfig['tabs'] ?? [] as $tabConfig) {
+                foreach ($tabConfig['elements'] ?? [] as $elementConfig) {
+                    if (($elementConfig['type'] ?? null) === CustomField::class) {
+                        if (empty($elementConfig['uid'])) {
+                            throw new Exception('A field layout element is missing its UUID. Reinstall Craft CMS ^4.4.14 and run `utils/fix-field-layout-uids` before upgrading to Craft CMS 5.');
+                        }
+                        if (isset($uids[$elementConfig['uid']])) {
+                            throw new Exception('A field layout element has a duplicate UUID. Reinstall Craft CMS ^4.4.14 and run `utils/fix-field-layout-uids` before upgrading to Craft CMS 5.');
+                        }
+                        $uids[$elementConfig['uid']] = true;
+                    }
                 }
-                $uids[$layoutElement->uid] = true;
             }
         }
 
@@ -99,6 +107,7 @@ public function safeUp(): bool
             }
             $indexedMatrixFieldConfigs[$matrixFieldUid] = $matrixFieldConfig;
         }
+        $fieldsService = Craft::$app->getFields();
         foreach ($projectConfig->get('matrixBlockTypes') ?? [] as $blockTypeUid => $blockTypeConfig) {
             if (!isset($indexedMatrixFieldConfigs[$blockTypeConfig['field']])) {
                 continue;