[Data Object Editor] Many to one relation - display mode "Inline search" - #3960
Conversation
The class definition editor lets you pick "Inline search" (displayMode 'combo') on a manyToOneRelation and stores it, but the object data component never read it and always rendered the path reference input — only manyToManyObjectRelation implemented the combo branch. The setting was effectively dead UI. Adds a single-select inline search field for manyToOneRelation, rendered whenever displayMode is 'combo' and the relation allows objects of exactly one class. Everything else keeps the path reference input. Option labels are resolved through the field's pathFormatterClass and fall back to the object path when none is configured. Because the format-path endpoint resolves the field definition from a concrete object, the source object is taken from the row being edited in the grid, the first selected row in batch edit, and the element context in the object editor — a listing's element context is the folder, which the endpoint rejects. Searching always goes to the server: filtering the options client side would only match the rendered labels, so typing a name would find nothing whenever the labels are object paths. Resolves pimcore#889 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Verdict: Needs changes. The PR activates inline search for eligible many-to-one relations.
Changes:
- Adds the combo field with server-side search and formatted labels.
- Supports object editor, grid editing, and batch editing.
- Adds display-mode selection tests.
Review assessment:
- Root cause addressed at
dynamic-type-object-data-many-to-one-relation.tsx:49-60. - Mixed asset/document/folder configurations are incorrectly routed to object-only search at
:110-112. - Inherited styling is lost at
many-to-one-relation-combo-field.tsx:84. - The feature-specific component is unnecessarily exposed through the SDK at
index.ts:20-24. - Tests cover component selection but not search, pagination, or label resolution. No breaking API change was found.
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
dynamic-type-object-data-many-to-one-relation.tsx |
Selects combo rendering and forwards grid row IDs. |
dynamic-type-object-data-many-to-one-relation.test.ts |
Tests display-mode routing. |
many-to-one-relation/index.ts |
Exports the combo component. |
many-to-one-relation-combo-field.tsx |
Implements the single-select UI. |
use-relation-source-object-id.ts |
Resolves the source object context. |
use-combo-field-data.ts |
Implements search, pagination, and label formatting. |
- Restrict inline search to object-only relations. The combo searches a single data object class, so a relation that also accepts assets, documents or object folders would make those targets unselectable. Those configurations keep the path reference input, with a regression case per mixed type. - Forward the inherited state to Select, so combo-mode fields keep the inherited styling the path input already shows. - Move the component next to the owning object-data dynamic type, mirroring the many-to-many combo, and drop the barrel export that exposed a feature-specific implementation as a central SDK component. - Cover the data hook: class id resolution, server-side searching, the debounced term, paging on scroll and label resolution through the path formatter. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ValeriaMaltseva
left a comment
There was a problem hiding this comment.
Hi @alexbaat, and thanks a lot for your contribution! The PR already looks good.
One thing I think we need before merging: the class definition editor is not updated. field-definition-many-to-one-relation-form-fields.tsx:151-158 still offers Display mode → "Inline search" for every configuration, including the ones usesInlineSearch() now rejects - more than one allowed class, assets or documents allowed, folder-only. There the setting saves and then silently does nothing, which is the dead-UI symptom pimcore/platform-version#287 describes, just moved to a different configuration.
Could you mirror the usesInlineSearch() condition there and disable the "Inline search" option when it does not apply, with the reason in the Form.Item tooltip? The form currently watches only displayMode, so it would also need objectsAllowed, assetsAllowed, documentsAllowed and classes. field-definition-many-to-many-object-relation-form-fields.tsx:25,44-56 does the same kind of reconciliation for visibleFields if you want a reference.
Please keep the option in the list and disabled rather than removing it - with a stored displayMode: 'combo' antd would otherwise render the raw value as the label. No need to migrate stored values.
The class definition editor offered Display mode -> "Inline search" for every manyToOneRelation, including the configurations the data component rejects: more than one allowed class, assets or documents allowed, or folder-only. There the setting saved and then did nothing, which is the same dead UI the field itself was meant to fix. The condition moves out of DynamicTypeObjectDataManyToOneRelation into supportsInlineSearch(), so the editor and the data component cannot drift apart. The editor now watches objectsAllowed, assetsAllowed, documentsAllowed and classes alongside displayMode, disables the option when the relation is not eligible and explains why in the Form.Item tooltip. The option stays in the list rather than being removed: with a stored displayMode 'combo' antd would otherwise render the raw value as its label. Stored values are left untouched.
|
|
Hi @ValeriaMaltseva, thanks for the review — good catch, the editor side was Shared condition. The eligibility check moved out of Editor.
Added to all seven locale files. The tooltip is only set while the option is No reconciliation. I deliberately did not mirror the Tests. |
|
One or more custom setup steps configured for this repository failed during this Copilot code review run: Setup steps run before each review. If the review above is missing context, or no review was posted at all, the failing step above may be the cause. See the workflow run for failure details, fix your setup steps configuration, and re-request a review. Note You can configure setup steps for Copilot code review separately from Copilot cloud agent with a |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 18 changed files in this pull request and generated no new comments.
Suppressed comments (2)
assets/js/src/core/modules/element/dynamic-types/definitions/objects/data-related/components/many-to-one-relation/components/combo-field/hooks/use-combo-field-data.ts:196
- Path formatter results are HTML-capable: the existing relation input renders them through
SanitizeHtml, and the grid preview marks formatted paths as HTML. StoringformatedPathas a plain string here means a formatter returning markup (for example,<strong>Name</strong>) is displayed literally in the Select. Preserve the distinction between raw paths and formatted HTML, and render formatter labels through the sanitizer.
if (Number.isInteger(id) && typeof item.formatedPath === 'string' && item.formatedPath !== '') {
resolved.set(id, item.formatedPath)
assets/js/src/core/modules/element/dynamic-types/definitions/objects/data-related/components/many-to-one-relation/components/combo-field/many-to-one-relation-combo-field.tsx:48
- Batch edit never supplies
combinedFieldName: its data-object adapter passes onlyconfig.fieldDefinitionanddefaultFieldWidth, unlike the editor and grid adapters that explicitly derive this value. Consequently this isundefinedin batch edit,hasPathFormatterstays false, and configured path formatters are silently skipped even thoughuseRelationSourceObjectIdresolves a selected row. Pass the batch field path (or a reliable field-name fallback) into the combo data hook.
} = useComboFieldData({
allowedClasses: props.allowedClasses,
combinedFieldName: props.combinedFieldName,
objectId,
pathFormatterClass: props.pathFormatterClass,
value: props.value
ValeriaMaltseva
left a comment
There was a problem hiding this comment.
Hi @alexbaat! Thanks for the really quick update to the PR, and once again, thanks for your contribution. I'll merge it ;)



Resolves pimcore/platform-version#287
Supersedes #889, which tracked the same gap before tracking issues moved to
platform-version.Problem
The class definition editor offers Display mode → "Inline search" on a
manyToOneRelationand stores it correctly (displayMode: 'combo', persisted viaAbstractRelations::$displayMode). The object data component never reads it, though:DynamicTypeObjectDataManyToOneRelation.getObjectDataComponent()always rendersManyToOneRelation, the path reference input. OnlymanyToManyObjectRelationimplements a combo branch (labelled "Tag field" there).So picking "Inline search" changes nothing anywhere — both options render the same widget.
Change
Adds a single-select inline search field for
manyToOneRelation, used whendisplayMode === 'combo'and the relation is restricted to objects of exactly one class.The combo offers its options through a search on a single data object class, so it can only represent a relation restricted to exactly that. A relation that also accepts assets, documents or object folders keeps the path reference input, which can address every element type — otherwise those targets would become unselectable.
New files, next to the owning dynamic type and mirroring the existing many-to-many combo:
components/many-to-one-relation/components/combo-field/many-to-one-relation-combo-field.tsxcomponents/many-to-one-relation/components/combo-field/hooks/use-combo-field-data.ts— paged full-text search plus label resolutioncomponents/many-to-one-relation/components/combo-field/hooks/use-relation-source-object-id.tsNotes on three details that are easy to get wrong
Labels. A
manyToOneRelationhas novisibleFields, so option labels come from the field'spathFormatterClass(data-objects/format-path), falling back to the object path when none is configured.getAvailableDataObjectColumnConfigurationForRelationcannot be reused here — it throws for anything that is not a (Advanced)ManyToManyObjectRelation — hence a separate hook rather than reusing the many-to-manyuseComboFieldData.Source object.
format-pathresolves the field definition from a concrete object. That object is taken from the row being edited (grid cell edit modal, viagetGridCellEditComponent), the first selected row (batch edit), or the element context (object editor). Inside a listing the element context is the folder being listed, and the endpoint answers404 Object with ID: x not foundfor it, so it is only used outside a listing.Searching. Filtering happens server side (
filterOption={false}). Filtering the options client side only matches the rendered labels, so typing an object's name finds nothing whenever those labels are object paths.useClassDefinitionCollectionQuerymaps the allowed class name to its id rather thanuseClassDefinitions(), because that context is folder-scoped inside a listing (ClassDefinitionsProviderfilters its items byelementId) and would not know the related class.Tests
dynamic-type-object-data-many-to-one-relation.test.ts— the rendering decision: combo vs. path input per display mode, the object-only guard with a case per mixed configuration (assets, documents, object folders), and that the grid cell editor forwards the edited row id.use-combo-field-data.test.ts— class id resolution, the debounced full-text term, page reset on search, paging on scroll versus a fully loaded list, and label resolution through the path formatter including both fallbacks.npm run check-typesclean,npm run lintclean for the touched files,npm test265 passed.Manual verification
Verified against a project with ~8.000 objects in the related class and a
pathFormatterClassconfigured, in all three places the field renders — object editor, grid cell edit modal and batch edit — covering searching, paging, selecting, saving and clearing.Review feedback addressed
inheritedstate is forwarded toSelect, so combo-mode fields keep the inherited styling.