Settings Pages
A settings page is a tenant-singleton form: it edits one row per tenant (keyed by tenant_id) instead of an addressable record. The NoerdSettingsPage trait makes such a page as slim as the simplest detail — tabs and fields come from a settings YAML, the component only declares which models the page edits.
Settings pages differ from details/pages in four hard rules:
- Always YAML-configured — the layout comes exclusively from
settings/{component}.yml. The noerd-pro layout manager never applies (no tenant or per-user overrides), and there is nocustom_attributesobject manager. - No grid — every field renders stacked, full width, in the built-in
settingstheme. Atheme:key in the YAML is ignored; the tenant-wide form theme (even an enforced one) does not apply either. - No URL model parameter — a settings page never declares
$detailPrimaryand never uses$modelId; the URL stays clean (/liefertool-settings, not?settingsId=). - No delete path — the singleton row is created on first save (
updateOrCreate(['tenant_id' => …])) and never deleted from the page.
Component
resources/views/components/{name}-page.blade.php — a settings component keeps the *-page suffix:
<?php
use Livewire\Component;
use Noerd\Traits\NoerdSettingsPage;
use Vendor\Module\Models\ModuleSettings;
new class extends Component {
use NoerdSettingsPage;
public array $settingsModels = [
'detailData' => ModuleSettings::class,
];
}; ?>
<x-noerd::page>
<x-slot:header>
<x-noerd::modal-title>{{ __('Module Settings') }}</x-noerd::modal-title>
</x-slot:header>
<x-noerd::tab-content :layout="$pageLayout" :modelId="$modelId"/>
<x-slot:footer>
<x-noerd::delete-save-bar :show-delete="false"/>
</x-slot:footer>
</x-noerd::page>$settingsModels maps public array properties to tenant-singleton models. Every key is the data array the YAML fields bind to; detailData is provided by the trait, extra keys are declared on the component. A settings page may edit SEVERAL models at once:
public array $settingsModels = [
'detailData' => LiefertoolSettings::class,
'coredata' => Coredata::class,
];
public array $coredata = [];On mount each model is resolved with firstOrNew(['tenant_id' => $tenantId]) and hydrated into its property; store() validates from the layout and persists every model with updateOrCreate(['tenant_id' => $tenantId], …) (stripping id, tenant_id and timestamps).
Custom mount / store
Like NoerdDetail, the trait methods are overridable. A custom mount() starts with $this->initSettings(); a custom store() (extra validation, cache busting) ends with the reusable tail:
public function store(): void
{
// ... custom validation, addError() + return on failure ...
$this->validateFromLayout();
$this->persistSettings();
$this->showSuccessIndicator = true;
}Settings YAML
app-configs/{app}/settings/{component}.yml — plus the module copy at app-modules/{module}/app-configs/{app}/settings/, both kept in sync like every other config. Resolution follows the standard search roots (project copy shadows module copy). Allowed keys: title, description, tabs, fields. Fields use the same field types, tab:, required:, showIf/showIfNot and helpText as detail YAMLs — colspan is irrelevant (every field is a full-width row).
title: Module Settings
tabs:
- number: 1
label: General
- number: 2
label: Notifications
fields:
- name: detailData.name
label: Name
type: text
required: true
- name: detailData.get_email
label: Send an email for each order?
type: checkbox
tab: 2
- name: detailData.notification_emails
label: Notification email addresses
type: text
tab: 2
showIf: detailData.get_email
helpText: Multiple addresses separated by commas.Loading goes through StaticConfigHelper::getSettingsFields(), which deliberately skips the layout-override hook and the tenant theme setting and forces theme: settings.
The settings theme
The built-in theme folder resources/views/themes/settings/ contains only a theme.yml (fullWidthRows: true — every element template falls back to the default theme). It is marked hidden: true, so it never appears in the Setup → System Settings theme picker. See Themes.
Install / update
The settings/ folder is published by the module install/update commands exactly like lists/, details/ and pages/ (HasModuleInstallation).
Reference
In-tree reference implementations: liefertool::liefertool-settings-page (two models, two tabs, custom store() validation, extra Blade in a tab2 slot) and liefertool::sms-page (the slimmest form — no methods at all). Trait mechanics are covered by tests/Feature/SettingsPageTraitTest.php.