Parent-Child Relations and IRRE 

Data Model describes which fields exist and what each one means. This page describes how those fields are kept consistent while editors work: TYPO3's IRRE (Inline Relational Record Editing) infrastructure on the backend-editing side, and the DataHandler hook pipeline that runs on every save, move, copy, and delete.

IRRE in TYPO3, briefly 

IRRE is TYPO3 Core's mechanism for editing a one-to-many relation inline, inside the parent record's own edit form. With foreign_table and foreign_field, the relation is stored directly through a field on the child record rather than through an intermediate MM table. A TCA field of type => inline with foreign_table and foreign_field tells FormEngine: this parent's children are whichever rows in foreign_table have foreign_field pointing back at this record's uid.

Grid Elements uses exactly this configuration, and nothing more exotic. tx_gridelements_children is that inline field, with foreign_table = tt_content and foreign_field = tx_gridelements_container. This matters architecturally, because it means the backend editing widget and the frontend data-fetching path described in Nesting resolve a container's children through the same underlying relation. There is no separate storage format for "children as edited in the backend" versus "children as rendered on the frontend".

Backend editing behavior 

A few TCA settings on tx_gridelements_children shape how the inline widget behaves, worth naming because they explain what an editor sees:

  • appearance.enabledControls disables the widget's own new and dragdrop controls. Children are not created by clicking "new" inside the container's form, they are created through the New Content Element wizard or dragged in from the page module, both of which write tx_gridelements_container directly.
  • overrideChildTca defaults a newly inline-created child's colPos to -1, matching the sentinel described on Data Model.
  • foreign_sortby = sorting means children use the ordinary tt_content sorting field. There is no separate Grid Elements sort order to keep in sync.

The DataHandler pipeline 

Persistent consistency is not left to TCA and IRRE alone. Grid Elements hooks into TYPO3's DataHandler at three points, routed through Classes/Hooks/DataHandler.php and implemented in Classes/DataHandler/:

Hook Responsibility
processDatamap_preProcessFieldArray (PreProcessFieldArray) Runs before a tt_content/pages save is persisted. Keeps colPos, tx_gridelements_container and tx_gridelements_columns consistent with each other, guards against container cycles and dangerous shortcut references before they can be written, and keeps a child's language in sync with its container's language.
processDatamap_afterDatabaseOperations (AfterDatabaseOperations) Runs after the save. Unless automatic unused-column correction has been disabled in the extension configuration, it re-evaluates affected elements against a changed grid or backend layout, moving elements to or from the "unused elements" state described on Data Model, and propagates relation changes to translated copies via checkAndUpdateTranslatedElements().
processCmdmap (ProcessCmdmap) Runs on copy/move commands. Implements "Paste as Reference" by creating a CType = shortcut record instead of duplicating content, and keeps container child counts in sync on copy, move and delete.

A separate pair of hooks, processDatamap_beforeStart and processCmdmap_beforeStart, gate these same operations against a column's allowed/disallowed/maxitems configuration before any of the above runs. That is a restriction-enforcement concern, not a relation-consistency one, and is described on Restrictions, Validation & Permissions.

Keeping counts and translations in sync 

Whenever a save or command changes which container an element belongs to, AbstractDataHandler::doGridContainerUpdate() adjusts the affected containers' tx_gridelements_children counters (see Data Model for what that counter means). It is called from the preprocessing and command-map handling paths as needed when children are moved, copied, attached or detached.

Translations need their own synchronization, because IRRE's foreign_field relation does not automatically re-parent a translated child to a translated container. checkAndUpdateTranslatedElements() propagates a container or child's relation change to its translated counterparts, re-pointing them at the matching container in their own language where one exists, and clearing the relation where it does not, rather than leaving a translated child silently attached to a different language's container.

Cycle protection 

A container becoming its own direct or indirect container would make both backend editing and frontend rendering recurse forever. Classes/Helper/ContainerCycleGuard.php prevents this at two points:

  • Direct or indirect self-reference. getContainerAncestorChain() walks tx_gridelements_container upward from a candidate container to the root, and wouldCreateContainerCycle() rejects an assignment that would place a container inside its own ancestor chain. This runs both on a direct field edit (PreProcessFieldArray::setFieldEntries()) and on a drag-and-drop or cut/paste move (Hooks\DataHandler::processCmdmap_beforeStart()).
  • Shortcut references into an ancestor. A CType = shortcut element referencing one of its own current or prospective ancestor containers would create the same kind of rendering loop indirectly. shortcutReferencesForbiddenContainer() protects individual shortcut edits and moves. When an entire Grid Element subtree is relocated, subtreeReferencesForbiddenContainer() scans its descendants for shortcut references that would become cyclic in the new ancestor chain.

A blocked cmdmap operation is rejected and removed from the command map. During direct datamap editing, the offending relation or reference fields are removed from the incoming field array so the invalid relationship cannot be persisted. In both cases a flash message is shown to the editor.