Problem/Motivation
EntityInlineForm::entityForm() builds the inline entity's own form display directly against the same $form_state object as the parent (outer) form:
$form_display = $this->getFormDisplay($entity, $entity_form['#form_mode']);
$form_display->buildForm($entity, $entity_form, $form_state);
$form_state is a single, form-wide object — isCached()/setCached() have no concept of nesting. If any field widget on the inline entity calls $form_state->setCached(FALSE) for its own, purely local reasons, that call disables caching for the entire outer form, not just the inline entity's sub-form.
This is exactly what the office_hours module's widgets do, deliberately and for their own legitimate reasons:
// OfficeHoursComplexWeekWidget::formElement()
// Make form_state not cached since we will update it in ajax callback.
$form_state->setCached(FALSE);
(Also present in OfficeHoursExceptionsWidget::formElement().)
When an entity reference field using such a widget is embedded via Inline Entity Form (e.g. inline_entity_form_complex) inside a parent form that itself relies on form caching for its own Ajax interactions (for example, a node edit form that also has a Media Library field), the parent form's caching gets silently disabled the moment the inline entity's sub-form is built. Every subsequent Ajax interaction on the parent form is then forced into an uncached, from-scratch rebuild.
Impact
On a from-scratch rebuild, Inline Entity Form's own per-entity "is this entity currently open for editing" state ($form_state->get(['inline_entity_form', $ief_id, 'entities', $delta, 'form'])) resets to its default (collapsed/closed), because that state was never re-established (there was no cache to restore it from). This means the inline entity's "Update"/"Cancel" buttons don't exist in the rebuilt tree, so Drupal's Form API falls back to treating some unrelated, earlier-registered Ajax button elsewhere on the parent form as the triggering element — in our case, a Media Library "Add media" button, which then opens the wrong dialog when the user actually clicked the inline entity's "Update" button. See core issue #3174361: Media Library modal opens randomly on AJAX requests for the general "wrong triggering element" symptom this produces.
Steps to reproduce
1. Create a content type with two fields: (a) an entity reference field using the inline_entity_form_complex widget, referencing a bundle that has an office_hours (or office_hours exceptions) field; (b) a Media Library field.
2. Edit a node of that type. Click "Edit" on an existing inline-referenced entity to open its nested edit form.
3. Click the inline entity's "Update" button.
4. Instead of updating the inline entity, the Media Library "Add media" dialog opens.
Proposed resolution
A nested entity form should not be able to override the parent form's own caching decision. Snapshot $form_state->isCached() immediately before building the nested entity's form display, and restore it afterward if a nested widget turned it off:
$parent_form_was_cached = $form_state->isCached();
$form_display->buildForm($entity, $entity_form, $form_state);
if ($parent_form_was_cached && !$form_state->isCached()) {
$form_state->setCached();
}| Comment | File | Size | Author |
|---|---|---|---|
| #2 | inline_entity_form-3617904-preserve-parent-cache.patch | 1.38 KB | matthiasm11 |
Comments
Comment #2
matthiasm11 commentedPatch in attachment.
To be used in combination with the patch from #3617898: Form cache is deleted after a submission that never actually executed (e.g. an Ajax-only button), stranding the form's cached state.