Skip to content

Architecture ​

PathRole
src/EasyAdminEmbeddedListBundle.phpConfiguration tree; every option becomes an easyadmin_embedded_list.<path> parameter; maps assets/ for AssetMapper.
src/Field/EmbeddedListField.phpThe field: form type, form theme, CSS class, script.
src/Field/Configurator/EmbeddedListConfigurator.phpGives a field the configured template and columns, and required: false before EasyAdmin guesses it.
src/Form/Type/EmbeddedListType.phpWorks out the list URL from the mapping or the options; renders the placeholder's variables.
src/EventListener/FragmentListener.phpOn a fragment request, swaps the layout and the index template and drops filters and batch actions.
src/EventListener/FragmentSessionListener.phpSaves the session of a fragment GET once the user is loaded.
src/Security/StatelessListCsrfTokenManager.phpRoutes the list's CSRF ids to the same-origin manager.
config/services.php, config/session.phpService definitions; the second only with session.enabled.
templates/Form theme, fragment layout, fragment index, field template.
assets/embedded-list.jsLoads lists, keeps their navigation inside them, runs their confirmed actions.

A list, end to end ​

  1. EasyAdmin builds the edit form. EmbeddedListConfigurator (priority 10 000, before EasyAdmin's CommonPreConfigurator) sets the template, the columns and required: false.
  2. EmbeddedListType::buildView() finds the target: from a PersistentCollection, its owner, the CRUD controller of its target entity and the other side of its mapping; otherwise the embedded_controller and embedded_filter_field options and the parent form's data. It generates the target index URL with filters[<filter>][comparison]== and filters[<filter>][value]=<id>.
  3. The form theme renders <div class="field-embedded-list" data-src="…"> with the header and the inline-actions flag. The field's script tag loads embedded-list.js as a module.
  4. The script fetches data-src with the header. On that request, FragmentSessionListener (kernel.controller) saves the session, then the index action runs as usual and returns its KeyValueStore.
  5. FragmentListener (kernel.view, priority 8, before EasyAdmin's CrudResponseListener) sets the layout template to the fragment layout in the context's template registry, renders the index through crud/index.html.twig of the bundle (which extends the configured one, passed as embedded_list_parent, and empties delete_form), and empties filters and batch_actions.
  6. CrudResponseListener renders it: the main block alone.
  7. The script swaps the HTML in, binds links, forms and confirmations, and dispatches ea.embedded-list.refreshed.

Why the template registry is read by reflection ​

EasyAdmin fills its template registry when it creates the admin context, from the dashboard and CRUD controllers' configureCrud(); the context factory is final and the registry is private to the context's I18nContext. Nothing public changes a template for one request, and the index template computes variables outside its blocks, so rendering one block alone is not an option either. FragmentListener::templateRegistry() is the only such read; the functional suite boots EasyAdmin, so a change upstream fails CI on the version matrix.

Released under the MIT License.