diff --git a/CHANGELOG.md b/CHANGELOG.md index 18dfda924ed..4f3d8709857 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Added `craft\events\SetEagerLoadedElementsEvent::$plan`. - `craft\base\ElementInterface::setEagerLoadedElements()` now has a `$plan` argument, which will be set to the eager-loading plan. - Fixed an error that could occur if eager-loading aliases conflicted with native eager-loading handles, such as `author`. ([#14057](https://github.com/craftcms/cms/issues/14057)) +- Fixed an error that occurred when indexing search keywords for an element with multiple instances of the same custom field. ([#13987](https://github.com/craftcms/cms/issues/13987)) ## 5.0.0-alpha.3 - 2023-12-21 diff --git a/src/services/Search.php b/src/services/Search.php index 2c263447bd0..d75102c2b35 100644 --- a/src/services/Search.php +++ b/src/services/Search.php @@ -146,23 +146,22 @@ public function indexElementAttributes(ElementInterface $element, ?array $fieldH } // Figure out which fields to update, and which to ignore - /** @var FieldInterface[] $updateFields */ - $updateFields = []; - /** @var string[] $ignoreFieldIds */ + $customFields = $element->getFieldLayout()?->getCustomFields() ?? []; + $updateFieldIds = []; $ignoreFieldIds = []; - $fieldLayout = $element->getFieldLayout(); - if ($fieldLayout !== null) { + + if (!empty($customFields)) { if ($fieldHandles !== null) { $fieldHandles = array_flip($fieldHandles); } - foreach ($fieldLayout->getCustomFields() as $field) { + foreach ($customFields as $field) { if ($field->searchable) { // Are we updating this field's keywords? if ($fieldHandles === null || isset($fieldHandles[$field->handle])) { - $updateFields[] = $field; + $updateFieldIds[$field->id] = true; } else { // Leave its existing keywords alone - $ignoreFieldIds[] = (string)$field->id; + $ignoreFieldIds[$field->id] = true; } } } @@ -174,7 +173,12 @@ public function indexElementAttributes(ElementInterface $element, ?array $fieldH 'siteId' => $element->siteId, ]; if (!empty($ignoreFieldIds)) { - $deleteCondition = ['and', $deleteCondition, ['not', ['fieldId' => $ignoreFieldIds]]]; + $ignoreFieldIds = array_map(fn(int $fieldId) => (string)$fieldId, array_keys($ignoreFieldIds)); + $deleteCondition = [ + 'and', + $deleteCondition, + ['not', ['fieldId' => $ignoreFieldIds]], + ]; } Db::delete(Table::SEARCHINDEX, $deleteCondition); @@ -185,10 +189,15 @@ public function indexElementAttributes(ElementInterface $element, ?array $fieldH } // Update the custom fields' keywords - foreach ($updateFields as $field) { - $fieldValue = $element->getFieldValue($field->handle); - $keywords = $field->getSearchKeywords($fieldValue, $element); - $this->_indexKeywords($element, $keywords, fieldId: $field->id); + $keywords = []; + foreach ($customFields as $field) { + if (isset($updateFieldIds[$field->id])) { + $fieldValue = $element->getFieldValue($field->handle); + $keywords[$field->id][] = $field->getSearchKeywords($fieldValue, $element); + } + } + foreach ($keywords as $fieldId => $instanceKeywords) { + $this->_indexKeywords($element, implode(' ', $instanceKeywords), fieldId: $fieldId); } // Release the lock