From a87b9885160eec3a8721f6961c97aaaa225bff1a Mon Sep 17 00:00:00 2001 From: Guy Sartorelli <36352093+GuySartorelli@users.noreply.github.com> Date: Fri, 7 Jun 2024 17:05:33 +1200 Subject: [PATCH] DOC Document scaffolding formfields for model relations (#527) --- .../00_Model/02_Relations.md | 2 + .../00_Model/11_Scaffolding.md | 87 +++++++++++++++++++ en/08_Changelogs/5.3.0.md | 25 ++++++ 3 files changed, 114 insertions(+) diff --git a/en/02_Developer_Guides/00_Model/02_Relations.md b/en/02_Developer_Guides/00_Model/02_Relations.md index 23e244b6a..6b8e79911 100644 --- a/en/02_Developer_Guides/00_Model/02_Relations.md +++ b/en/02_Developer_Guides/00_Model/02_Relations.md @@ -345,6 +345,8 @@ class Company extends DataObject } ``` +For more information see [scaffolding for relations](/developer_guides/model/scaffolding/#scaffolding-for-relations). + ## Dot notation {#dot-notation} To specify multiple `has_many`, `many_many`, `belongs_to`, or `belongs_many_many` relationships to the same model class (and as a general best practice) you can use dot notation to distinguish them like below: diff --git a/en/02_Developer_Guides/00_Model/11_Scaffolding.md b/en/02_Developer_Guides/00_Model/11_Scaffolding.md index 3b62e5bfb..ee9b3b346 100644 --- a/en/02_Developer_Guides/00_Model/11_Scaffolding.md +++ b/en/02_Developer_Guides/00_Model/11_Scaffolding.md @@ -15,6 +15,8 @@ customise those fields as required. An example is `DataObject`, Silverstripe CMS will automatically create your CMS interface so you can modify what you need, without having to define all of your form fields from scratch. +Note that the [`SiteTree`](api:SilverStripe\CMS\Model\SiteTree) edit form does not use scaffolded fields. + ```php namespace App\Model; @@ -83,6 +85,91 @@ You can also alter the fields of built-in and module `DataObject` classes by imp > [!NOTE] > `FormField` scaffolding takes [`$field_labels` config](#field-labels) into account as well. +## 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. + +> [!NOTE] +> Polymorphic `has_one` relations do not have scaffolded form fields. Usually these are managed via a `has_many` relation which points at the `has_one` relation. + +With the below example, the following form fields will be scaffolded: + +|relation|form field| +|---|---| +|`Child`|`MyCustomField`| +|`HasManyChildren`|`SearchableMultiDropdownField`| +|`ManyManyChildren`|`GridField`| + +```php +namespace App\Model; + +use SilverStripe\ORM\DataObject; + +class MyDataObject extends DataObject +{ + // ... + private static array $has_one = [ + 'Child' => MyChild::class, + ]; + + private static array $has_many = [ + 'HasManyChildren' => MyChild::class . '.Parent', + ]; + + private static array $many_many = [ + 'ManyManyChildren' => MyChild::class, + ]; +} +``` + +```php +namespace App\Model; + +use App\Form\MyCustomField; +use SilverStripe\Forms\FormField; +use SilverStripe\Forms\GridField\GridFieldAddExistingAutocompleter; +use SilverStripe\Forms\SearchableMultiDropdownField; +use SilverStripe\ORM\DataObject; + +class MyChild extends DataObject +{ + // ... + public function scaffoldFormFieldForHasOne( + string $fieldName, + ?string $fieldTitle, + string $relationName, + DataObject $ownerRecord + ): FormField { + // Return a form field that should be used for selecting this model type for has_one relations. + return MyCustomField::create($fieldName, $fieldTitle); + } + + public function scaffoldFormFieldForHasMany( + string $relationName, + ?string $fieldTitle, + DataObject $ownerRecord, + bool &$includeInOwnTab + ): FormField { + // If this should be in its own tab, set $includeInOwnTab to true, otherwise set it to false. + $includeInOwnTab = false; + // Return a form field that should be used for selecting this model type for has_many relations. + return SearchableMultiDropdownField::create($relationName, $fieldTitle, static::get()); + } + + public function scaffoldFormFieldForManyMany( + string $relationName, + ?string $fieldTitle, + DataObject $ownerRecord, + bool &$includeInOwnTab + ): FormField { + // The default implementation for this method returns a GridField, which we can modify. + $gridField = parent::scaffoldFormFieldForManyMany($relationName, $fieldTitle, $ownerRecord, $includeInOwnTab); + $gridField->getConfig()->removeComponentsByType(GridFieldAddExistingAutocompleter::class); + return $gridField; + } +} +``` + ## Searchable fields The `$searchable_fields` property uses a mixed array format that can be used to further customise your generated admin diff --git a/en/08_Changelogs/5.3.0.md b/en/08_Changelogs/5.3.0.md index 9e2e2053b..20b12144a 100644 --- a/en/08_Changelogs/5.3.0.md +++ b/en/08_Changelogs/5.3.0.md @@ -9,6 +9,7 @@ title: 5.3.0 (unreleased) - [Features and enhancements](#features-and-enhancements) - [High-level API for converting files](#file-converter) - [Validation for elemental content blocks when saving individual blocks](#elemental-validation) + - [Define scaffolded form fields for relations to `DataObject` models](#scaffolded-relation-formfields) - [Support for `JOIN` in SQL `UPDATE`](#sql-update-join) - [Other new features](#other-new-features) - [API changes](#api-changes) @@ -57,6 +58,30 @@ Elemental content blocks now support validation when saving or publishing indivi Validation can be added to a content block using standard [Model Validation and Constraints](https://docs.silverstripe.org/en/5/developer_guides/model/validation/#validation-and-constraints) by implementing a [`DataObject::validate()`](api:SilverStripe\ORM\DataObject::validate()) method on a content block, or by using [Form Validation](https://docs.silverstripe.org/en/5/developer_guides/forms/validation/#form-validation) where there are several options available. +### Define scaffolded form fields for relations to `DataObject` models {#scaffolded-relation-formfields} + +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. + +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. + +```php +namespace App\Model; + +use SilverStripe\ORM\DataObject; + +class MyParent extends DataObject +{ + // ... + private static array $has_one = [ + 'MyChild' => MyChild::class, + ]; +} +``` + +This means modules can pre-define the form field that should be used for their custom models, which reduces the amount of boilerplate code developers need to include in their `getCMSFields()` implementations. + +For more information see [scaffolding for relations](/developer_guides/model/scaffolding/#scaffolding-for-relations). + ### 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.