Problem/Motivation

`field_pagedesigner_content` is translatable, so each node translation can reference its own
container element. `PagedesignerService::createContainer()` (src/PagedesignerService.php:190-198)
deliberately skips the `entity` back-reference while the node is still unsaved:

if (!empty($entity) && !$entity->isNew()) {
$container->entity->entity = $entity;
}

`saveContainer()` (:141-150), called from `pagedesigner_entity_insert()`
(pagedesigner.module:153-156), is what repairs it afterwards — but it only handles the
translation being saved. Containers belonging to the other translations keep `entity = NULL`
permanently, and every element created beneath them inherits the gap
(HandlerPluginBase::copy():170,184; CompoundHandlerBase::copy():300 and patch():347,384).

Nothing self-heals: `entity` is not `setRequired(TRUE)`, has no constraint, and
`Element::preSave()` never derives it.

The visible consequence is a **completely blank pagedesigner**. `ElementResource::patch()`
does:

if (!$this->updateLock($entity->entity->entity, $identifier)) { // line 239

and `updateLock()` is typed `ContentEntityInterface $entity` (:292), so a NULL back-reference
throws a TypeError before anything is saved. Because every subsequent editor request hits the
same element, the page never loads. Same unguarded dereference in `delete()` (:276). Note that
`$element->entity->entity` is also NULL when `entity` points at a node that has since been
deleted.

Found on a production site (D10, de/fr/it): one node had three separate containers — `de` with
`entity` set, `fr` and `it` with NULL — and all ~396 elements of its `fr` tree had no node
reference.

Secondary, same origin: `saveContainer()` unconditionally does `$container->entity = $entity`.
When a node is cloned by a module that copies field values by reference (quick_node_clone only
deep-clones entity_reference_revisions fields), the new node ends up sharing the original's
container and takes over its back-reference. On the same site: 24 containers shared by 2-7
nodes. Deleting any of them triggers `pagedesigner_entity_predelete()` ->
`clearPagedesigner()`, which hard-deletes the shared tree and blanks the sibling pages.

Steps to reproduce

  1. Enable translation on a content type with a `pagedesigner_item` field.
  2. Create a node with several translations in one save.
  3. Check `pagedesigner_element_field_data`: the default translation's container has `entity`
    set, the others are NULL.
  4. Open a non-default translation in the pagedesigner and change any element — the PATCH
    fails with a TypeError and the page stays blank.

Proposed resolution

1. Guard `ElementResource::patch()` and `delete()`: throw
`UnprocessableEntityHttpException` when the back-reference resolves to NULL, so one broken
element causes one failed request instead of an unusable page.
2. `saveContainer()`: iterate `getTranslationLanguages(TRUE)` and repair every translation's
container. Write `entity` only when it is empty or already this node — if it points at a
different existing node, log a warning and leave it alone, rather than stealing another
node's container.
3. `Element::preSave()`: when `entity` is empty and `container` is set, derive the target from
the container. Future gaps then self-heal and the inheritance cascade stops.

Note for (3): `pagedesigner_layout` legitimately creates detached template trees with *both*
`entity` and `container` NULL (LayoutResource.php:156), so the `container` check is required to
leave those untouched.

Remaining tasks

- Patch + tests. `tests/src/Kernel/EndToEnd/KernelNodeTest.php:139` already asserts
`$node->id() === $container->entity->target_id`; add a multi-translation case, and one
asserting `saveContainer()` does not reassign a container owned by another node.
- Separate issues, out of scope here: `pagedesigner_gallery` creates `gallery_item` /
`gallery_gallery` elements with no `entity` and no `container` on every gallery edit
(GalleryGallery.php:84-93, Gallery.php:122); and `pagedesigner_debug`'s
`Cleaner::getPagedesignerOrphans()` classifies a NULL-`entity` container as an orphan and
hard-deletes its entire tree, which is a data-loss risk in the repair tooling.

User interface changes

API changes

Data model changes

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

guillaumepacilly created an issue. See original summary.

guillaumepacilly’s picture

Issue summary: View changes

guillaumepacilly’s picture

Status: Active » Needs review

guillaumepacilly’s picture

Status: Needs review » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

guillaumepacilly’s picture

Status: Fixed » Closed (fixed)