Extending a shipped form¶
Ship view/adminhtml/form/<id>.json with the id of a form Nebula ships, and the engine merges your file over it. The recipes below are either real overlays from the Nebula modules or worked examples built the same way.
Change a field's type¶
Name the field and give it a new type. Four lines turn the CMS page content field into a plain textarea:
{
"fieldsets": {
"content": {
"fields": {
"content": { "type": "textarea" }
}
}
}
}
Every other key on the field, its label and position included, stays as shipped. The type may be one a module of yours registered through RendererPool, not only a built-in one; see Form field types.
Add a fieldset rendered by a snippet¶
The passkey module adds a sign-in security panel to the admin user form:
{
"fieldsets": {
"nebula_passkey_security": {
"label": "Sign-in security",
"position": 25,
"open": true,
"renderer": "snippet.nebula_passkey_admin_status"
}
}
}
A fieldset with a renderer has no fields of its own; the snippet's template renders the whole panel, with a view model bound in DI. See Snippets.
Add a section to a layout-tree form¶
The customer form is a layout tree, not a fieldset map. Layout nodes are a list whose items carry id, so an overlay reaches into the tree by id:
{
"layout": [
{
"id": "main_row",
"children": [
{
"id": "account_col",
"children": [
{
"id": "nebula_passkey_security_section",
"type": "section",
"label": "Sign-in security",
"renderer": "snippet.nebula_passkey_customer_status"
}
]
}
]
}
]
}
main_row and account_col exist in the shipped tree, so they are merged rather than duplicated. The new section has a new id, so it is appended to the column's children. Give every node you add an id, and use position to place it among siblings.
Add a field¶
{
"fieldsets": {
"general": {
"fields": {
"vendor_campaign": {
"label": "Campaign",
"type": "select",
"optionsSource": "vendor.campaigns",
"position": 45
}
}
}
}
}
The field renders and posts with the rest of the form, prefixed if the form has a fieldPrefix. Whether it is saved depends on the save controller. Core's CMS block controller hands the POST to the block model, and the model's resource writes only real columns, so a new field needs a column (or an extension attribute plugin on the save) to persist. The provider must also return a value for it on load; for a shipped provider, that is a plugin on the provider's getData().
Change labels, defaults, or required flags¶
{
"fieldsets": {
"general": {
"fields": {
"title": { "label": "Block name", "note": "Shown only in the admin" },
"is_active": { "default": "0" }
}
}
}
}
Remove a field or a fieldset¶
{
"fieldsets": {
"general": { "fields": { "identifier": { "$remove": true } } },
"legacy": { "$remove": true }
}
}
Removing a field a save controller requires produces a validation error on save. Removing a required core field is a decision the controller enforces, not the form.
$remove on a field is in the form schema. On a fieldset it is not, so the merge honours it but bin/magento nebula:lint reports the file.
Change a button or the save target¶
{
"settings": {
"saveUrl": "vendor/block/save",
"buttons": [ { "label": "Preview", "url": "vendor/block/preview", "idParam": "block_id", "class": "ghost" } ]
}
}
A button url is a route, not a template. The record id is appended as a route param named by idParam, which defaults to the form's own identifier param, and there is no {{...}} interpolation to write the id into the path yourself. A button that needs the posted fields rather than the id uses submitsForm with action instead. The full list of button keys is in Buttons.
Buttons carry no id, so an overlay's buttons list replaces the shipped one rather than merging into it. Repeat the buttons you want to keep.
Rules to follow¶
Overlays follow the id, and tabbed variants have a different id. Product, CMS page, CMS block and landing page forms have a tabbed variant selected in configuration, with the id <entity>_edit_tabs. An overlay on cms_page_edit does not apply to cms_page_edit_tabs. Ship both files if the merchant may use either.
Sequence after the module you overlay. Add it to your module.xml <sequence>.
Never write an empty object. "fields": {} deletes the fields.
EAV forms are overlaid the same way through overrides.fields.<code> and layout; see EAV forms.
Validate and clear. bin/magento nebula:lint, then cache:clean nebula_definitions layout in default mode or nebula:compile in production mode.
Check the page. Open the form, submit it empty, save a record, reopen it and confirm your field kept its value.