Skip to content

Commit

Permalink
DOC Document scaffolding formfields for model relations (#527)
Browse files Browse the repository at this point in the history
  • Loading branch information
GuySartorelli authored Jun 7, 2024
1 parent 3539845 commit a87b988
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 0 deletions.
2 changes: 2 additions & 0 deletions en/02_Developer_Guides/00_Model/02_Relations.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
87 changes: 87 additions & 0 deletions en/02_Developer_Guides/00_Model/11_Scaffolding.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down
25 changes: 25 additions & 0 deletions en/08_Changelogs/5.3.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit a87b988

Please sign in to comment.