-
Notifications
You must be signed in to change notification settings - Fork 67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DOC Document new tab order and form scaffolder changes #549
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,47 +44,86 @@ 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\FieldList; | ||
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); | ||
|
||
return $fields; | ||
$this->beforeUpdateCMSFields(function (FieldList $fields) { | ||
$fields->addFieldsToTab('Root.Main', [ | ||
CheckboxSetField::create('IsActive', 'Is active?'), | ||
TextField::create('Title'), | ||
TextareaField::create('Content')->setRows(5), | ||
]); | ||
}); | ||
|
||
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.`<relationname>`" tabs.| | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Shouldn't these always be in the "Root. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The tab they're in is determined by the This option allows tabs to be used, but it doesn't force it. |
||
|`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. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved the existing form field scaffolding item as a sub-item in a new section, so now there's an overall formfield scaffolding section with both the existing docs and the new docs inside it. |
||
- [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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only change to this line (see removal of the same line above) was removing the API link since we're linking to that in the parent heading section instead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably worth mentioning that SiteTree subclasses would be auto-scaffolded, I know it's documented elsewhere though it's such a common use case I think it's worth mentioning here so people don't get confused why things aren't not working There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This PR is for CMS 5, where |
||
|
||
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. | ||
|
@@ -133,6 +154,7 @@ From 6.0 onwards, tokens will never be regenerated during session renewal, and t | |
- `silverstripe/graphql-devtools` contains a new `GraphQLSchemaInitTask` to help you initialise a basic GraphQL schema. | ||
- [`GridFieldDetailForm_ItemRequest`](api:SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest) now uses a [`PjaxResponseNegotiator`](api:SilverStripe\Control\PjaxResponseNegotiator) to handle PJAX responses for the save and publish actions. This aligns it with responses from other form submissions in the CMS. | ||
- Primitive types inside an iterable object will now be automatically converted to relevant [`DBField`](api:SilverStripe\ORM\FieldType\DBField) types so can be rendered in a template. This allows the use of `return ArrayList::create(['lorem', 123]);` from a [`ContentController`](api:SilverStripe\CMS\Controllers\ContentController) method that is then looped over in a template. | ||
- A new [`TabSet::changeTabOrder()`](api:SilverStripe\Forms\TabSet::changeTabOrder()) method has been added that allows you to change the order of tabs in a `TabSet`. See [tabbed forms](/developer_guides/forms/tabbed_forms/#change-the-order-of-tabs) for details. | ||
|
||
## API changes | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No longer relevant since we're calling the parent method.