From 425fdc4ec7e1d99c687a2b9f50d799ed9361f5d2 Mon Sep 17 00:00:00 2001 From: brandonkelly Date: Sun, 28 Dec 2025 10:39:24 -0800 Subject: [PATCH 1/2] Refactor FieldLayout::getCardBodyElements() --- src/controllers/FieldsController.php | 4 +- src/helpers/Cp.php | 4 +- src/models/FieldLayout.php | 103 ++++++++------------------- 3 files changed, 33 insertions(+), 78 deletions(-) diff --git a/src/controllers/FieldsController.php b/src/controllers/FieldsController.php index 17579d340c4..5f92690345c 100644 --- a/src/controllers/FieldsController.php +++ b/src/controllers/FieldsController.php @@ -630,12 +630,12 @@ public function actionRenderCardPreview() $fieldLayout->setCardView( array_column($cardElements, 'value') - ); // this fully takes care of attributes, but not fields + ); $fieldLayout->setCardThumbAlignment($thumbAlignment); return $this->asJson([ - 'previewHtml' => Cp::cardPreviewHtml($fieldLayout, $cardElements, $showThumb), + 'previewHtml' => Cp::cardPreviewHtml($fieldLayout, showThumb: $showThumb), ]); } diff --git a/src/helpers/Cp.php b/src/helpers/Cp.php index d01b88b5630..6b3cdb55571 100644 --- a/src/helpers/Cp.php +++ b/src/helpers/Cp.php @@ -2935,7 +2935,7 @@ private static function _thumbManagementHtml(FieldLayout $fieldLayout, array $co * Returns HTML for the card preview based on selected fields and attributes. * * @param FieldLayout $fieldLayout - * @param array $cardElements + * @param array $cardElements (deprecated) * @return string * @throws \Throwable */ @@ -2976,7 +2976,7 @@ public static function cardPreviewHtml(FieldLayout $fieldLayout, array $cardElem Html::beginTag('div', ['class' => 'card-body']); // get body elements (fields and attributes) - $cardElements = $fieldLayout->getCardBodyElements(null, $cardElements); + $cardElements = $fieldLayout->getCardBodyElements(); foreach ($cardElements as $cardElement) { if ($cardElement instanceof CustomField) { diff --git a/src/models/FieldLayout.php b/src/models/FieldLayout.php index 7315302d483..c3a395e53cf 100644 --- a/src/models/FieldLayout.php +++ b/src/models/FieldLayout.php @@ -793,7 +793,7 @@ public function getConfig(): ?array public function resetUids(): void { $this->uid = StringHelper::UUID(); - $cardView = $this->getCardView(); + $cardViewReplacements = []; foreach ($this->getTabs() as $tab) { $tab->uid = StringHelper::UUID(); @@ -801,15 +801,18 @@ public function resetUids(): void foreach ($tab->getElements() as $element) { $oldUid = $element->uid; $element->uid = StringHelper::UUID(); - - $cardViewPos = array_search("layoutElement:$oldUid", $cardView); - if ($cardViewPos !== false) { - $cardView[$cardViewPos] = "layoutElement:$element->uid"; - } + $cardViewReplacements["layoutElement:$oldUid"] = "layoutElement:$element->uid"; } } - $this->setCardView($cardView); + // update the card view items + // (look for `layoutElement:x` anywhere in the item, in case it also + // includes a content block field UUID) + $cardViewItems = []; + foreach ($this->getCardView() as $item) { + $cardViewItems[] = strtr($item, $cardViewReplacements); + } + $this->setCardView($cardViewItems); } /** @@ -1064,88 +1067,40 @@ public function getCardBodyAttributes(): array * Returns the fields and attributes that should be used in element card bodies in the correct order. * * @param ElementInterface|null $element + * @param array $cardElements (deprecated) * @return array * @since 5.5.0 */ public function getCardBodyElements(?ElementInterface $element = null, array $cardElements = []): array { + $cardElements = []; + // get attributes that should show in a card $attributes = $this->getCardBodyAttributes(); - $layoutElements = []; - - if (empty($cardElements)) { - // index field layout elements by prefix + uid - foreach ($this->getCardBodyFields($element) as $layoutElement) { - $layoutElements["layoutElement:$layoutElement->uid"] = $layoutElement; - } - - foreach ($this->getGeneratedFields() as $field) { - if (($field['name'] ?? '') !== '') { - $layoutElements["generatedField:{$field['uid']}"] = [ - 'html' => $element ? ($element->getGeneratedFieldValues()[$field['uid']] ?? '') : Html::encode($field['name']), + foreach ($this->getCardView() as $key) { + $cardElement = null; + if (str_starts_with($key, 'layoutElement:')) { + $uid = StringHelper::removeLeft($key, 'layoutElement:'); + $cardElement = $this->getElementByUid($uid); + } elseif (str_starts_with($key, 'generatedField:')) { + $uid = StringHelper::removeLeft($key, 'generatedField:'); + $field = $this->getGeneratedFieldByUid($uid); + if ($field) { + $cardElement = [ + 'html' => $element ? ($element->getGeneratedFieldValues()[$uid] ?? '') : Html::encode($field['name']), ]; } + } else { + $cardElement = $attributes[$key] ?? null; } - } else { - // we only need to worry about body fields as the attributes are taken care of via getCardBodyAttributes() - foreach ($cardElements as $cardElement) { - if (str_starts_with($cardElement['value'], 'layoutElement:')) { - $uid = str_replace('layoutElement:', '', $cardElement['value']); - $layoutElement = $this->getElementByUid($uid); - if ($layoutElement === null) { - $fieldId = $cardElement['fieldId']; - if ($fieldId) { - $field = Craft::$app->getFields()->getFieldById($fieldId); - $layoutElement = new CustomField(); - $layoutElement->setField($field); - } else { - // this will kick in for native field that have just been dragged into the field layout designer - $fieldLabel = $cardElement['fieldLabel']; - if ($fieldLabel) { - $layoutElement['value'] = $layoutElement; - $layoutElement['label'] = $fieldLabel; - } - } - } - $layoutElements[$cardElement['value']] = $layoutElement; - } elseif (str_starts_with($cardElement['value'], 'generatedField:')) { - $uid = str_replace('generatedField:', '', $cardElement['value']); - $field = $this->getGeneratedFieldByUid($uid); - if ($field) { - $layoutElements[$cardElement['value']] = [ - 'html' => $element ? ($element->getGeneratedFieldValues()[$uid] ?? '') : Html::encode($field['name']), - ]; - } elseif (isset($cardElement['fieldLabel'])) { - $layoutElements[$cardElement['value']] = [ - 'html' => Html::encode($cardElement['fieldLabel']), - ]; - } - } + if ($cardElement) { + $cardElements[$key] = $cardElement; } } - // get the card view config - array of all the attributes, fields and generated fields that should be shown in the card - $cardViewValues = $this->getCardView(); - - // filter out any generated fields that shouldn't show in the card - $layoutElements = array_filter( - $layoutElements, - fn($key) => !str_starts_with($key, 'generatedField:') || in_array($key, $cardViewValues), - ARRAY_FILTER_USE_KEY - ); - - $elements = array_merge($layoutElements, $attributes); - - // make sure we don't have any cardViewValues that are no longer allowed to show in cards - $cardViewValues = array_filter($cardViewValues, fn($value) => isset($elements[$value])); - - // return elements in the order specified in the config - return array_replace( - array_flip($cardViewValues), - $elements - ); + return $cardElements; } /** From 8539230a889e205a8c058a7f28123fad4ce2cf35 Mon Sep 17 00:00:00 2001 From: brandonkelly Date: Mon, 29 Dec 2025 05:46:56 -0800 Subject: [PATCH 2/2] Release notes [ci skip] --- CHANGELOG-WIP.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG-WIP.md b/CHANGELOG-WIP.md index cb041a28103..e12adfda57f 100644 --- a/CHANGELOG-WIP.md +++ b/CHANGELOG-WIP.md @@ -136,6 +136,8 @@ - Deprecated `Craft.BaseElementIndex::selectDefaultSource()`. - Deprecated `Craft.BaseElementIndex::selectSource()`. - Deprecated `Craft.BaseElementIndex::selectSourceByKey()`. +- Deprecated the `$cardElements` argument in `craft\helpers\Cp::cardPreviewHtml()`. +- Deprecated the `$cardElements` argument in `craft\models\FieldLayout::getCardBodyElements()`. ### System - GraphQL API responses now set their `Content-Type` header to `application/graphql-response+json`.