Tree structured models

Tree structuring covers the many to one relation, where one record holds further sub-records. Editing the parent record shows a nested module for the records attached to it.
Parent record with a nested submodule tab
Opened nested submodule of a parent record
The $belongsToModel parameter defines the parent admin model. The child model is shown as a tab in the parent form and is not listed in the menu.
app/Models/OrdersItem.php
A required order_id column with a foreign key is created. The column name is the snake case parent class name with _id, also when the parent uses a custom $table. If a model is a child of several admin models, pass an array. Every parent gets its own nullable column.
Relations are available without defining them, see selecting related records.
The parent record is available while the child form is loaded, e.g. to change fields by the parent.
A child model can also be placed into a tab of the parent form.
Tree structures require a naming convention. Every model carries an English name in the singular. A child model starts with the parent name in the plural, followed by its own name in the singular. An order model named Order has an items model named OrdersItem. If that model has nested variants, the model is named OrdersItemsVariant.
When a parent is added to a table that already has rows, php artisan admin:migrate asks for a parent id for the existing rows. Fill them in code instead with a hook named by the column.

Recursive trees

A model related to itself builds a tree of unlimited depth, e.g. categories. Rows can be dragged between levels.
The root table lists rows without a parent, nested rows are loaded under their parent. Set $withRecursiveRows to load all rows at once.
onRecursiveDragAndDrop() is called for a row moved under another parent (null for the root level).

Children created before the parent

$withoutParent allows adding child rows in the form of a new, unsaved parent. They are attached when the parent is saved.
$nullableRelation only makes the foreign column nullable, e.g. for rows imported without a parent. The form is not changed.

Child form inside the parent form

A single child model can be merged into the parent form. Both records are validated and saved in one request, a missing child record is created when the parent is updated.

Relation to any model

$globalRelation makes a model attachable under any parent without a foreign key, e.g. notes or attachments. The parent is stored in the _table and _row_id columns, which are hidden when the model is serialized on the website.

Reordering records

Rows of every admin model can be reordered by drag and drop. The position is stored in an indexed _order column and rows are ordered by _order DESC, so a new row appears on top. Set the property to false to disable reordering.
Reordering is also disabled when $orderBy uses another column than _order, and in single record mode. No _order column is created then, and reorder requests are refused.
A new row gets the next position after all existing rows. When reordering is enabled on a table that already has rows, the rows are numbered during php artisan admin:migrate. React to a finished reorder with onUpdateOrder(), e.g. to flush a cached menu. A returned value is sent as the response.

Publishing records

Publishing is enabled on every admin model by default. The table gets an indexed published_at column, and each row gets a publish / hide button. Set the property, or a publishable() method, to false to turn publishing off.
New rows are published immediately with the current date. Outside of the administration (website, console, queues) the model returns only rows with published_at in the past, so a row can be scheduled by saving a future date.
The publishing scope can be turned off for the whole request or for one model, e.g. in a preview controller.
The administration always lists all rows. Publishing more selected rows toggles every row by its own state.

Single record mode

$single sets $minimum and $maximum to 1. The table is hidden and the page contains only the form of one record.
The value can be computed by a method:
Single models are not sortable, so no _order column is created. Publishing is not changed. A single child model is related to its parent as hasOne, e.g. $order->invoice.
Useful for static pages such as About us or Contact, or for application settings that do not need more than one row.

Module group

An admin model can be assigned to a menu group defined in the configuration. Use dot notation for nested groups.
Admin model assigned to a menu group
A group missing in the configuration uses its key as the name. The group can also be returned by a group() method. Root models are listed in the menu, child models only inside their parent. $inMenu overrides it.

Initially hidden tabs and groups

Tabs and field groups with an id can be hidden when the form opens, and shown later from a custom component.
A child model tab is identified by its table name.