Skip to content

Commit

Permalink
DOC Document changes to FormScaffolder options.
Browse files Browse the repository at this point in the history
  • Loading branch information
GuySartorelli committed Aug 12, 2024
1 parent 9c5801a commit 7290280
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 22 deletions.
76 changes: 57 additions & 19 deletions en/02_Developer_Guides/00_Model/11_Scaffolding.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.`<relationname>`" 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.
Expand Down
27 changes: 24 additions & 3 deletions en/08_Changelogs/5.3.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.

Expand All @@ -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.
Expand Down

0 comments on commit 7290280

Please sign in to comment.