Relation Table Tabs
Resource detail pages can show relation fields as table tabs below the main detail card.
Use relation table tabs when the related records are important enough to browse, attach or manage from the parent detail page.
Field Requirement
Relation table tabs come from fields that implement FieldWithTable and are configured to display as a table.
BelongsToManyField::make('users', __('common.users'))
->setRelatedResource(qore()->getResourceOrFail('users'))
->setIsDisplayedAsTable();
The exact relation field methods depend on the relation type, but the detail page logic is the same: fields shown as tables are removed from the normal detail table and added as tabs.
How The Detail Page Builds Tabs
ResourceHasDetailPage::addRelationTables() loops over getFieldsForDetail($model). For each table field it adds a TabItem with:
- the field label as the tab label;
- a
SubNodepointing togetFieldTableEndpoint($model->getKey(), $field->name); - the relation count from
getTableCount($model).
That means relation tables load independently from the parent detail page.
Why SubNodes Are Used
Relation tables can have their own pagination, sorting, filtering, create forms, attach forms, detach actions and reloads. Loading them as SubNodes keeps that state separate from the parent detail page and avoids rebuilding the entire page for every table interaction.
Custom Detail Layouts
If you override detailLayout(), relation table tabs are still added after your detail layout by the default detail page response.
public function detailLayout(Model $model): Node
{
return (new CardNode(__('common.overview')))
->addChild(new TextNode($this->modelTitle($model)));
}
Only override getDetailPage() when you need to replace the whole detail-page shell, including breadcrumbs, actions, alerts and relation tabs.
Create And Attach Workflows
Relation fields with table support can create a new related record and immediately attach it to the current model:
resources/{resourceName}/{modelId}/attach-form/{fieldName}/{relatedResourceName}
resources/{resourceName}/{modelId}/attach/{fieldName}/{relatedResourceName}
The create-and-attach form is another backend FormNode, so it can use resource fields, validation and form success reloads.
BelongsToMany relation tables also include an attach-existing workflow by default when the current user may update the parent model:
resources/{resourceName}/{modelId}/attach-existing-form/{fieldName}/{relatedResourceName}
resources/{resourceName}/{modelId}/attach-existing/{fieldName}/{relatedResourceName}
The choices in this form come from the related resource's optionsQuery(). Qore uses the same query to resolve the submitted model, so the query must include every record that may be attached.
Create and attach-existing are registered as table quick actions. Relation tables render quick actions as an Add dropdown, so a table with only one available action still uses the same control shape as a table with both Create and Attach existing.
Detach Workflow
BelongsToMany relation tables include a Detach bulk action by default when the current user may update the parent model. The action removes the selected pivot records from the relation without deleting the related models.
Developers can disable attach or detach per field:
BelongsToManyField::make('users', __('common.users'))
->setIsDisplayedAsTable()
->setCanAttach(false)
->setCanDetach(false);
When Not To Use A Table Tab
Keep a relation field in the normal detail card when it is small and mostly descriptive, such as an owner, category or status relation.
Use a tab when the relation is a list the user will scan, filter or manage.