Nested Tables Overview
Embedding a child table's rows inside a parent record's edit form with nested_table() - the shared XcrudNested API, and the five layouts a nested table can render in.
nested_table()
Call nested_table() on an Xcrud instance to embed a child table's rows inside the parent's edit form:
public function nested_table(
string $label,
string $parentField,
string $childTable,
string $childField,
?int $order = null
): XcrudNested
| Parameter | Type | Default | Description |
|---|---|---|---|
$label | string | required | Label shown for this nested table (tab name, section heading, etc., depending on position) |
$parentField | string | required | Column on the parent table whose value filters the child rows |
$childTable | string | required | The child table to embed (must be whitelisted) |
$childField | string | required | Column on the child table compared against $parentField's value |
$order | ?int | null | Position among sibling nested tables/the parent's own fields tab; omitted falls back to declaration order |
It returns an XcrudNested node - a mini CRUD grid ($childTable rows WHERE $childField = the parent row's own $parentField value) with its own Add/Edit/Delete. XcrudNested is a deliberate subset of Xcrud: no search box, no advanced-filter panel, no bulk actions, no sum(), no group_by_columns() at the nested level. What it does keep is enough to call nested_table() on the returned node again, nesting arbitrarily deep - a nested table can itself have further nested tables, each level getting the same treatment.
$xcrud = Xcrud::get_instance();
$xcrud->table('orders');
$xcrud->route('orders');
$orderdetails = $xcrud->nested_table('Order details', 'orderNumber', 'orderdetails', 'orderNumber');
$orderdetails->columns('productCode,quantityOrdered,priceEach');
$orderdetails->order_by('productCode', 'asc');
echo $xcrud->render();
The XcrudNested API
The most commonly used methods on a returned node:
| Method | Description |
|---|---|
columns($fieldList) | Restricts/orders which of the child table's whitelisted columns the nested grid shows |
fields($fieldList) | Restricts/orders which editable columns the nested grid's own Add/Edit form shows |
order_by($field, $dir = 'asc') | Sort order for the nested grid's rows |
limit($n) | Page size for the nested grid; default 100 |
relation(...) | Foreign-key dropdown for a nested field, same shape as the top-level relation() |
gallery($field, $childTable, $options) | Multi-image field on the nested grid's own Add/Edit form |
highlight() / highlight_row() | Conditional cell/row styling, same as the top-level methods |
intelligent_column_fit($enabled = true) | Per-node override of the responsive column-fit behavior |
default_tab($label, $order = null) | Labels/orders the tab that holds this node's OWN row fields, once it has nested_table() children of its own |
position() - where it renders
position($position = 'bottom') is the key layout control - it decides where a nested table shows up in its parent's edit form:
public function position(string $position = 'bottom'): self
| Value | Description |
|---|---|
tab | Default. Behind its own tab, alongside the parent's own fields tab |
bottom | Always-visible, full-width section appended directly below the parent's own fields |
bottom-fixed | Same as bottom, but sticky/pinned near the bottom of the panel, with a draggable resize handle and its own internal scroll (~35% height by default) |
left / right | A fixed-width sticky sidebar column alongside the parent's own fields, with a draggable width |
Only where a node renders changes - what it does once visible (columns, fields, relations, further nesting) stays identical regardless of position.