Architecture
| Path | Role |
|---|---|
src/EasyAdminEmbeddedListBundle.php | Configuration tree; every option becomes an easyadmin_embedded_list.<path> parameter; maps assets/ for AssetMapper. |
src/Field/EmbeddedListField.php | The field: form type, form theme, CSS class, script. |
src/Field/Configurator/EmbeddedListConfigurator.php | Gives a field the configured template and columns, and required: false before EasyAdmin guesses it. |
src/Form/Type/EmbeddedListType.php | Works out the list URL from the mapping or the options; renders the placeholder's variables. |
src/EventListener/FragmentListener.php | On a fragment request, swaps the layout and the index template and drops filters and batch actions. |
src/EventListener/FragmentSessionListener.php | Saves the session of a fragment GET once the user is loaded. |
src/Security/StatelessListCsrfTokenManager.php | Routes the list's CSRF ids to the same-origin manager. |
config/services.php, config/session.php | Service definitions; the second only with session.enabled. |
templates/ | Form theme, fragment layout, fragment index, field template. |
assets/embedded-list.js | Loads lists, keeps their navigation inside them, runs their confirmed actions. |
A list, end to end
- EasyAdmin builds the edit form.
EmbeddedListConfigurator(priority 10 000, before EasyAdmin'sCommonPreConfigurator) sets the template, the columns andrequired: false. EmbeddedListType::buildView()finds the target: from aPersistentCollection, its owner, the CRUD controller of its target entity and the other side of its mapping; otherwise theembedded_controllerandembedded_filter_fieldoptions and the parent form's data. It generates the target index URL withfilters[<filter>][comparison]==andfilters[<filter>][value]=<id>.- The form theme renders
<div class="field-embedded-list" data-src="…">with the header and the inline-actions flag. The field's script tag loadsembedded-list.jsas a module. - The script fetches
data-srcwith the header. On that request,FragmentSessionListener(kernel.controller) saves the session, then the index action runs as usual and returns itsKeyValueStore. FragmentListener(kernel.view, priority 8, before EasyAdmin'sCrudResponseListener) sets thelayouttemplate to the fragment layout in the context's template registry, renders the index throughcrud/index.html.twigof the bundle (which extends the configured one, passed asembedded_list_parent, and emptiesdelete_form), and emptiesfiltersandbatch_actions.CrudResponseListenerrenders it: themainblock alone.- 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.