From 246cfbbb634422468feb7e3eb6a9c95ce6cd7d6c Mon Sep 17 00:00:00 2001 From: lacatoire Date: Mon, 4 May 2026 15:57:13 +0200 Subject: [PATCH] Propagate embedded controllers' field assets to parent form page When a parent form embeds another CRUD controller via AssociationField::renderAsEmbeddedForm() or CollectionField::useEntryCrudForm(), the embedded controller's fields (TextEditorField, FileField, nested CollectionField, etc.) carry their own CSS/JS assets. Those live on the embedded EntityDto, never on the parent field, so AbstractCrudController::getFieldAssets() (which only walks top-level fields) never sees them: the parent form page renders without the editor JS, the collection JS, etc., and the nested widgets break visually and functionally. Merge each embedded field's assets back into the parent FieldDto's own assets right after the embedded EntityDto's fields are processed. The parent's assets filter (loadedOn) keeps the per-page semantics. Closes #6127 --- .../Configurator/AssociationConfigurator.php | 18 ++++++++++++++---- .../Configurator/CollectionConfigurator.php | 13 +++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/Field/Configurator/AssociationConfigurator.php b/src/Field/Configurator/AssociationConfigurator.php index c30b90b339..09e9603b7c 100644 --- a/src/Field/Configurator/AssociationConfigurator.php +++ b/src/Field/Configurator/AssociationConfigurator.php @@ -430,10 +430,20 @@ private function configureCrudForm(FieldDto $field, EntityDto $entityDto, string $crudPageName = Crud::PAGE_EDIT; } - $field->setFormTypeOption( - 'entityDto', - $this->createEntityDto($targetEntityFqcn, $targetCrudControllerFqcn, $targetCrudControllerAction, $targetCrudControllerPageName, $crudPageName), - ); + $embeddedEntityDto = $this->createEntityDto($targetEntityFqcn, $targetCrudControllerFqcn, $targetCrudControllerAction, $targetCrudControllerPageName, $crudPageName); + $field->setFormTypeOption('entityDto', $embeddedEntityDto); + + // The assets declared by the embedded controller's fields (e.g. TextEditorField, + // FileField, a nested CollectionField...) live on the embedded EntityDto, not on + // the parent AssociationField. Propagate them up so that + // AbstractCrudController::getFieldAssets(), which only walks top-level fields, + // picks them up and outputs the required CSS/JS on the form page that hosts the + // embedded CRUD form. See #6127. + $assets = $field->getAssets(); + foreach ($embeddedEntityDto->getFields() ?? [] as $embeddedField) { + $assets = $assets->mergeWith($embeddedField->getAssets()); + } + $field->setAssets($assets); } /** diff --git a/src/Field/Configurator/CollectionConfigurator.php b/src/Field/Configurator/CollectionConfigurator.php index 5b9b47ebce..6add33a426 100644 --- a/src/Field/Configurator/CollectionConfigurator.php +++ b/src/Field/Configurator/CollectionConfigurator.php @@ -214,6 +214,19 @@ private function configureEntryType(FieldDto $fieldDto, EntityDto $entityDto, Ad $fieldDto->setFormTypeOption('prototype_options.entityDto', $newEntityDto); $fieldDto->setFormTypeOptionIfNotSet('prototype_data', $createEntryEntity()); $fieldDto->setFormTypeOptionIfNotSet('entry_options.empty_data', $createEntryEntity); + + // The assets declared by entry fields (e.g. TextEditorField, FileField, a nested + // CollectionField...) live on the entry EntityDto, not on the parent CollectionField. + // Propagate them up so that AbstractCrudController::getFieldAssets(), which only + // walks top-level fields, picks them up and outputs the required CSS/JS on the + // form page that hosts the embedded CRUD form. See #6127. + $assets = $fieldDto->getAssets(); + foreach ([$editEntityDto, $newEntityDto] as $entryEntityDto) { + foreach ($entryEntityDto->getFields() ?? [] as $entryField) { + $assets = $assets->mergeWith($entryField->getAssets()); + } + } + $fieldDto->setAssets($assets); } /**