From e66e5b9ebf3732e953ed72e69d44b4250e45f84d 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 0d8c5d9f97..da004ac609 100644 --- a/src/Field/Configurator/AssociationConfigurator.php +++ b/src/Field/Configurator/AssociationConfigurator.php @@ -354,10 +354,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 6dcdb623ea..9352d23fa0 100644 --- a/src/Field/Configurator/CollectionConfigurator.php +++ b/src/Field/Configurator/CollectionConfigurator.php @@ -169,6 +169,19 @@ private function configureEntryType(FieldDto $fieldDto, EntityDto $entityDto, Ad $fieldDto->setFormTypeOption('entry_type', CrudFormType::class); $fieldDto->setFormTypeOption('entry_options.entityDto', $editEntityDto); $fieldDto->setFormTypeOption('prototype_options.entityDto', $newEntityDto); + + // 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); } /**