diff --git a/en/02_Developer_Guides/00_Model/11_Scaffolding.md b/en/02_Developer_Guides/00_Model/11_Scaffolding.md index ee9b3b346..f69deed0e 100644 --- a/en/02_Developer_Guides/00_Model/11_Scaffolding.md +++ b/en/02_Developer_Guides/00_Model/11_Scaffolding.md @@ -44,47 +44,85 @@ class MyDataObject extends DataObject > [!TIP] > It is typically considered a good practice to wrap your modifications in a call to [`beforeUpdateCMSFields()`](api:SilverStripe\ORM\DataObject::beforeUpdateCMSFields()) - the `updateCMSFields()` extension hook is already triggered by `parent::getCMSFields()`, so this is how you ensure any new fields are added before extensions update your fieldlist. -To fully customise your form fields, start with an empty FieldList. +To define the form fields yourself without using scaffolding, use the `mainTabOnly` option in [`DataObject.scaffold_cms_fields_settings`](api:SilverStripe\ORM\DataObject->scaffold_cms_fields_settings). See [scaffolding options](#scaffolding-options) for details. ```php namespace App\Model; +use SilverStripe\Forms\CheckboxSetField; +use SilverStripe\Forms\TextField; +use SilverStripe\Forms\TextareaField; use SilverStripe\ORM\DataObject; class MyDataObject extends DataObject { // ... + private static array $scaffold_cms_fields_settings = [ + 'mainTabOnly' => true, + ]; + public function getCMSFields() { - $fields = FieldList::create( - TabSet::create( - 'Root', - Tab::create( - 'Main', - CheckboxSetField::create('IsActive', 'Is active?'), - TextField::create('Title'), - TextareaField::create('Content') - ->setRows(5) - ) - ) - ); - - $this->extend('updateCMSFields', $fields); + $this->beforeUpdateCMSFields(FieldList $fields) { + $fields->addFieldsToTab('Root.Main', [ + CheckboxSetField::create('IsActive', 'Is active?'), + TextField::create('Title'), + TextareaField::create('Content')->setRows(5), + ]); + } - return $fields; + return parent::getCMSFields(); } } ``` -> [!TIP] -> It is good practice to invoke the `updateCMSFields()` extension hook afterward, so that extensions in modules can apply their functionality to your field list. - You can also alter the fields of built-in and module `DataObject` classes by implementing `updateCMSFields()` in [your own Extension](/developer_guides/extending/extensions). > [!NOTE] > `FormField` scaffolding takes [`$field_labels` config](#field-labels) into account as well. +## Scaffolding options + +`FormScaffolder` has several options that modify the way it scaffolds form fields. + +|option|description| +|---|---| +|`tabbed`|Use tabs for the scaffolded fields. All database fields and `has_one` fields will be in a "Root.Main" tab. Fields representing `has_many` and `many_many` relations will either be in "Root.Main" or in "Root.``" tabs.| +|`mainTabOnly`|Only set up the "Root.Main" tab, but skip scaffolding actual form fields or relation tabs. If `tabbed` is false, the `FieldList` will be empty.| +|`restrictFields`|Allow list of field names. If populated, any database fields and fields representing `has_one` relations not in this array won't be scaffolded.| +|`ignoreFields`|Deny list of field names. If populated, database fields and fields representing `has_one` relations which *are* in this array won't be scaffolded.| +|`fieldClasses`|Optional mapping of field names to subclasses of `FormField`.| +|`includeRelations`|Whether to include `has_many` and `many_many` relations.| +|`restrictRelations`|Allow list of field names. If populated, form fields representing `has_many` and `many_many` relations not in this array won't be scaffolded.| +|`ignoreRelations`|Deny list of field names. If populated, form fields representing `has_many` and `many_many` relations which *are* in this array won't be scaffolded.| + +You can set these options for the scaffolding of the fields in your model's `getCMSFields()` field list by setting the [`DataObject.scaffold_cms_fields_settings`](api:SilverStripe\ORM\DataObject->scaffold_cms_fields_settings) configuration property. + +```php +namespace App\Model; + +use SilverStripe\Forms\HiddenField; +use SilverStripe\ORM\DataObject; + +class MyDataObject extends DataObject +{ + // ... + + private static array $scaffold_cms_fields_settings = [ + 'includeRelations' => false, + 'ignoreFields' => [ + 'MyDataOnlyField', + ], + 'fieldClasses' => [ + 'MyHiddenField' => HiddenField::class, + ], + ]; +} +``` + +You can also set this configuration in [extensions](/developer_guides/extending/extensions), for example if your extension is adding new database fields that you don't want to be edited via form fields in the CMS. + ## Scaffolding for relations Form fields are also automatically scaffolded for `has_one`, `has_many`, and `many_many` relations. These have sensible default implementations, and you can also customise what form field will be used for any given `DataObject` model. diff --git a/en/08_Changelogs/5.3.0.md b/en/08_Changelogs/5.3.0.md index f43cede97..78ddff5bc 100644 --- a/en/08_Changelogs/5.3.0.md +++ b/en/08_Changelogs/5.3.0.md @@ -10,7 +10,7 @@ title: 5.3.0 (unreleased) - [High-level API for converting files](#file-converter) - [Improve customisability of rendered images](#image-rendering) - [Validation for inline-editable elemental blocks](#elemental-validation) - - [Define scaffolded form fields for relations to `DataObject` models](#scaffolded-relation-formfields) + - [`FormField` scaffolding for `DataObject` models](#scaffolding) - [Support for `JOIN` in SQL `UPDATE`](#sql-update-join) - [Autologin token regeneration changes](#autologin-token-regeneration) - [Other new features](#other-new-features) @@ -82,9 +82,13 @@ Validation can be added to a content block using standard [Model Validation and Elemental data is no longer sent when saving the parent `DataObject` (usually a `Page`) that contains an [`ElementalAreaField`](api:DNADesign\Elemental\Forms\ElementalAreaField). Instead, when saving the parent `DataObject`, all the child inline-editable elements that have unsaved changes are inline saved at the same time. This change was done to consolidate the saving process down to a single code path. The code that was previously used to process any element data sent with the parent data has been removed. -### Define scaffolded form fields for relations to `DataObject` models {#scaffolded-relation-formfields} +### `FormField` scaffolding for `DataObject` models {#scaffolding} -Most `DataObject` classes will rely on some amount of automatic scaffolding of form fields in their [`getCMSFields()`](api:SilverStripe\ORM\DataObject::getCMSFields()) implementations. However, it's common for modules to provide a specialised form field which is intended to always be used with a specific `DataObject` class. In those cases, even though you always want to use that form field with that class, you have to copy some boilerplate code from the module's documentation to set it up. +We've made a few improvements to how form fields are scaffolded for [`DataObject::getCMSFields()`](api:SilverStripe\ORM\DataObject::getCMSFields()). + +#### Define scaffolded form fields for relations {#scaffolding-relation-formfields} + +Most `DataObject` classes will rely on some amount of automatic scaffolding of form fields in their `getCMSFields()` implementations. However, it's common for modules to provide a specialised form field which is intended to always be used with a specific `DataObject` class. In those cases, even though you always want to use that form field with that class, you have to copy some boilerplate code from the module's documentation to set it up. You can now define what form fields should be used when scaffolding form fields for `has_one`, `has_many`, and `many_many` relations. This is defined on the class on the child-side of the relationship. For example, for the below `has_one` relation you would implement [`scaffoldFormFieldForHasOne()`](api:SilverStripe\ORM\DataObject::scaffoldFormFieldForHasOne()) on the `MyChild` class. @@ -106,6 +110,23 @@ This means modules can pre-define the form field that should be used for their c For more information see [scaffolding for relations](/developer_guides/model/scaffolding/#scaffolding-for-relations). +#### `FormScaffolder` options {#scaffolding-options} + +The [`FormScaffolder`](api:SilverStripe\Forms\FormScaffolder) class (which is responsible for scaffolding form fields for `getCMSFields()`) has some new options: + +|option|description| +|---|---| +|`mainTabOnly`|Only set up the "Root.Main" tab, but skip scaffolding actual form fields or relation tabs. If `tabbed` is false, the `FieldList` will be empty.| +|`ignoreFields`|Deny list of field names. If populated, database fields and fields representing `has_one` relations which *are* in this array won't be scaffolded.| +|`ignoreRelations`|Deny list of field names. If populated, form fields representing `has_many` and `many_many` relations which *are* in this array won't be scaffolded.| +|`restrictRelations`|Allow list of field names. If populated, form fields representing `has_many` and `many_many` relations not in this array won't be scaffolded.| + +In particular, the `ignoreFields` and `ignoreRelations` options are useful ways to prevent form fields from being scaffolded at all. You can use this instead of calling `$fields->removeFieldByName()`, for example if you are adding database fields that you don't want to be edited with form fields in the CMS. + +These options are now also configurable using the new [`DataObject.scaffold_cms_fields_settings`](api:SilverStripe\ORM\DataObject->scaffold_cms_fields_settings) configuration property. + +See the [scaffolding](/developer_guides/model/scaffolding/#scaffolding-options) section for more details. + ### Support for `JOIN` in SQL `UPDATE` {#sql-update-join} The [`SQLUpdate`](api:SilverStripe\ORM\Queries\SQLUpdate) class now supports all of the same `JOIN` operations (using the same methods) that [`SQLSelect`](api:SilverStripe\ORM\Queries\SQLSelect) does.