From 7aaf5358c96a8293a3f83f661d229ed9382edc0b Mon Sep 17 00:00:00 2001 From: Guy Sartorelli Date: Mon, 27 Nov 2023 15:36:04 +1300 Subject: [PATCH 1/2] DOC Document using GridField with arbitrary data --- .../03_Forms/Field_types/04_GridField.md | 32 +++++++++++-------- .../How_Tos/03_Create_a_GridFieldComponent.md | 2 +- .../04_Create_a_GridField_ActionProvider.md | 4 +-- en/04_Changelogs/5.2.0.md | 11 +++++++ 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/en/02_Developer_Guides/03_Forms/Field_types/04_GridField.md b/en/02_Developer_Guides/03_Forms/Field_types/04_GridField.md index c11a38ca6..3073c0772 100644 --- a/en/02_Developer_Guides/03_Forms/Field_types/04_GridField.md +++ b/en/02_Developer_Guides/03_Forms/Field_types/04_GridField.md @@ -9,6 +9,8 @@ icon: table [GridField](api:SilverStripe\Forms\GridField\GridField) is Silverstripe CMS's implementation of data grids. The main purpose of this field type is to display tabular data in a format that is easy to view and modify. It can be thought of as a HTML table with some tricks. +Usually `GridField` is used with `DataObject` records - but it can be used with data that isn't represented by `DataObject` records as well. See [using `GridField` with arbitrary data](/developer_guides/forms/using_gridfield_with_arbitrary_data/) for more information. + [info] `GridField` powers the automated data UI of [ModelAdmin](api:SilverStripe\Admin\ModelAdmin). For more information about `ModelAdmin` see the [Customizing the CMS](/developer_guides/customising_the_admin_interface) guide. @@ -30,10 +32,6 @@ a `GridField` has almost no functionality. The `GridFieldConfig` instance and th responsible for all the user interactions including formatting data to be readable, modifying data and performing any actions such as deleting records. -[warning] -Some `GridField` components expect the list to be an instance of `DataList` and won't work with `ArrayList`. -[/warning] - ```php // app/src/PageType/MyPage.php namespace App\PageType; @@ -223,11 +221,10 @@ Similar to `GridFieldConfig_Base` with the addition support of the ability to vi a read-only view of the data record. [info] -The data row show must be a `DataObject` subclass. The fields displayed in the read-only view come from -`DataObject::getCMSFields()`. +Each record in the list must have an `ID` field, and the value of that field must be a positive integer. -The `DataObject` subclass displayed must define a `canView()` method that returns a boolean on whether the user can view -this record. +If the class representing your data has a `getCMSFields()` method, the return value of that method will be used for the fields displayed in the read-only view. +Otherwise, you'll need to pass in a [`FieldList`](api:SilverStripe\Forms\FieldList) to [`GridFieldDetailForm::setFields()`](api:SilverStripe\Forms\GridField\GridFieldDetailForm::setFields()). [/info] ```php @@ -246,13 +243,20 @@ $gridField->setConfig($config); Similar to `GridFieldConfig_RecordViewer` with the addition support to edit or delete each of the records. [info] -The data row show must be a `DataObject` subclass. The fields displayed in the edit view come from -`DataObject::getCMSFields()`. +Each record in the list must have an `ID` field, and the value of that field must be a positive integer. + +If the class representing your data has a `getCMSFields()` method, the return value of that method will be used for the fields displayed in the read-only view. +Otherwise, you'll need to pass in a [`FieldList`](api:SilverStripe\Forms\FieldList) to [`GridFieldDetailForm::setFields()`](api:SilverStripe\Forms\GridField\GridFieldDetailForm::setFields()). [/info] +[warning] +The class representing your data _must_ implement [`DataObjectInterface`](api:SilverStripe\ORM\DataObjectInterface) so that your records can be edited. + +See [using `GridField` with arbitrary data](/developer_guides/forms/using_gridfield_with_arbitrary_data/) for more information. +[/warning] + [alert] -Permission control for editing and deleting the record uses the `canEdit()` and `canDelete()` methods on the -`DataObject` object. +Permission control for editing and deleting the record uses the `canEdit()` and `canDelete()` methods on the class that represents your data. [/alert] ```php @@ -319,8 +323,8 @@ $gridField->setConfig($config); ## `GridFieldDetailForm` -The `GridFieldDetailForm` component drives the record viewing and editing form. By default it takes its fields from the -[`DataObject->getCMSFields()`](api:SilverStripe\ORM\DataObject::getCMSFields()) method but can be customised to accept different fields via the +The `GridFieldDetailForm` component drives the record viewing and editing form. By default it takes its fields from the `getCMSFields()` method +(e.g. [`DataObject->getCMSFields()`](api:SilverStripe\ORM\DataObject::getCMSFields())) method but can be customised to accept different fields via the [GridFieldDetailForm::setFields()](api:SilverStripe\Forms\GridField\GridFieldDetailForm::setFields()) method. ```php diff --git a/en/02_Developer_Guides/03_Forms/How_Tos/03_Create_a_GridFieldComponent.md b/en/02_Developer_Guides/03_Forms/How_Tos/03_Create_a_GridFieldComponent.md index 3f9fd057f..1b28f0dde 100644 --- a/en/02_Developer_Guides/03_Forms/How_Tos/03_Create_a_GridFieldComponent.md +++ b/en/02_Developer_Guides/03_Forms/How_Tos/03_Create_a_GridFieldComponent.md @@ -37,7 +37,7 @@ Action providers run actions. Action providers often also implement `GridField_A Examples: -- A delete action provider that deletes a DataObject. +- A delete action provider that deletes a record. - An export action provider that will export the current list to a CSV file. See [Basic GridField custom action boilerplate](./create_a_gridfield_actionprovider#custom-action-boilerplate) for an example of implementing this component. diff --git a/en/02_Developer_Guides/03_Forms/How_Tos/04_Create_a_GridField_ActionProvider.md b/en/02_Developer_Guides/03_Forms/How_Tos/04_Create_a_GridField_ActionProvider.md index 550ca60ef..84f9bba6c 100644 --- a/en/02_Developer_Guides/03_Forms/How_Tos/04_Create_a_GridField_ActionProvider.md +++ b/en/02_Developer_Guides/03_Forms/How_Tos/04_Create_a_GridField_ActionProvider.md @@ -74,7 +74,7 @@ class GridFieldCustomAction extends AbstractGridFieldComponent implements public function getColumnContent($gridField, $record, $columnName) { - if (!$record->canEdit()) { + if (!$record->hasMethod('canEdit') || !$record->canEdit()) { return null; } @@ -230,7 +230,7 @@ class GridFieldCustomAction extends AbstractGridFieldComponent implements private function getCustomAction($gridField, $record) { - if (!$record->canEdit()) { + if (!$record->hasMethod('canEdit') || !$record->canEdit()) { return; } diff --git a/en/04_Changelogs/5.2.0.md b/en/04_Changelogs/5.2.0.md index 9c9662daf..e61755490 100644 --- a/en/04_Changelogs/5.2.0.md +++ b/en/04_Changelogs/5.2.0.md @@ -8,6 +8,7 @@ title: 5.2.0 (unreleased) - [Features and enhancements](#features-and-enhancements) - [New ORM features](#new-orm-features) + - [GridField components now work with arbitrary data](#gridfield-arbitrary-data) - [ErrorPage allowed codes configuration](#errorpage-allowed-codes-configuration) - [Create random passwords for new users](#create-random-passwords-for-new-users) - [Buttons to select all files and deselect all files](#bulk-action-buttons) @@ -77,6 +78,16 @@ $query->selectField('"my custom title" AS "Title"'); $query->setAllowCollidingFieldStatements(true); ``` +### GridField components now work with arbitrary data {#gridfield-arbitrary-data} + +It has historically been difficult to use a `GridField` to display data that isn't represented by `DataObject` records - and even more difficult to edit that data. + +We have removed several barriers to using the `GridField` to display arbitrary data, and improved error messaging when specific information cannot be dynamically identified, such as which columns to display and what form fields to use when viewing or editing data. + +This applies to all `GridFieldComponent` classes in `silverstripe/framework` except for [`GridFieldAddExistingAutocompleter`](api:SilverStripe\Forms\GridField\GridFieldAddExistingAutocompleter) and [`GridFieldLevelup`](api:SilverStripe\Forms\GridField\GridFieldLevelup), which both explicitly require the model class for the associated `GridField` to be a subclass of `DataObject`. + +See [using `GridField` with arbitrary data](/developer_guides/forms/using_gridfield_with_arbitrary_data/) for more information. + ### ErrorPage allowed codes configuration By default, all available error codes are present in the dropdown in the CMS. This can be overwhelming and there are a few (looking at you, 418) that can From 3657480d00064ced7eb47dd9c240687dfd78e775 Mon Sep 17 00:00:00 2001 From: Guy Sartorelli Date: Mon, 11 Dec 2023 12:36:24 +1300 Subject: [PATCH 2/2] DOC Document gridfield with arbitrary data --- .../07_Using_GridField_With_Arbitrary_Data.md | 433 ++++++++++++++++++ .../03_Forms/Field_types/04_GridField.md | 6 +- en/04_Changelogs/5.2.0.md | 2 +- 3 files changed, 438 insertions(+), 3 deletions(-) create mode 100644 en/02_Developer_Guides/03_Forms/07_Using_GridField_With_Arbitrary_Data.md diff --git a/en/02_Developer_Guides/03_Forms/07_Using_GridField_With_Arbitrary_Data.md b/en/02_Developer_Guides/03_Forms/07_Using_GridField_With_Arbitrary_Data.md new file mode 100644 index 000000000..44bae2569 --- /dev/null +++ b/en/02_Developer_Guides/03_Forms/07_Using_GridField_With_Arbitrary_Data.md @@ -0,0 +1,433 @@ +--- +title: Using GridField with Arbitrary Data +summary: Details about using the GridField class for managing data which isn't represented by DataObject models. +icon: table +--- + +# Using `GridField` with arbitrary data + +[`GridField`](api:SilverStripe\Forms\GridField\GridField) is often used for displaying and editing `DataObject` records - but it can be used with other data as well. You might have data that is pulled from an API for example, which you want to display in the admin area of your Silverstripe CMS project. + +[info] +This document assumes you're familiar with `GridField` - see the [`GridField` documentation](/developer_guides/forms/field_types/gridfield/) for information about using `GridField`. +[/info] + +Data which isn't represented by `DataObject` records can come in two forms: + +- truely arbitrary data wrapped in [`ArrayData`](api:SilverStripe\View\ArrayData) +- data which has some specific class to represent it. + +Both are supported by `GridField`, provided the class representing the data is some subclass of [`ViewableData`](api:SilverStripe\View\ViewableData). + +Some grid field components may require specific information, such as which columns to display or how to represent the data in a form. Depending on how you're representing your data, you might need to call specific methods on those components to pass that information in, or you might instead choose to implement methods in your data representation class which the components can call to get that information. + +## Representing data with `ArrayData` + +Regardless of how you get your data, whether it's from a web API or some other source, you'll need to store it in an `ArrayList`. For best results, each record should also be explicitly instantiated as an `ArrayData` object in the list. + +[hint] +The `ID` field shown here isn't necessary if you only want to view the records as rows in the `GridField`, but if you want to be able to view *each* record in a read-only form view, the `ID` field is mandatory. + +See [viewing data in a form](#arraydata-view) for more information. +[/hint] + +```php +use SilverStripe\ORM\ArrayList; +use SilverStripe\View\ArrayData; + +$list = ArrayList::create([ + ArrayData::create([ + 'ID' => 1, + 'FieldName' => 'This is an item', + ]), + ArrayData::create([ + 'ID' => 2, + 'FieldName' => 'This is a different item', + ]), +]); +``` + +### Displaying data as rows in a `GridField` {#arraydata-display-as-rows} + +For displaying your data as rows in a `GridField`, you can rely on the default `GridFieldConfig` object that the field will build for itself, with some small changes. + +You'll need to tell the `GridField` which fields in your data should be displayed in the grid view. You do this by passing an associative array of field names to labels into [`GridFieldDataColumns::setDisplayFields()`](SilverStripe\Forms\GridField\GridFieldDataColumns::setDisplayFields()). + +```php +use SilverStripe\Forms\GridField\GridField; +use SilverStripe\Forms\GridField\GridFieldDataColumns; + +$gridField = GridField::create('MyData', 'My data', $list); +$columns = $gridField->getConfig()->getComponentByType(GridFieldDataColumns::class); +$columns->setDisplayFields([ + 'FieldName' => 'Column Header Label', +]); +``` + +If you don't want filtering functionality, you'll also need to remove the [`GridFieldFilterHeader`](api:SilverStripe\Forms\GridField\GridFieldFilterHeader) component from the gridfield: + +```php +use SilverStripe\Forms\GridField\GridFieldFilterHeader; +$gridField->getConfig()->removeComponentsByType(GridFieldFilterHeader::class); +``` + +### Filtering data {#arraydata-filter} + +If you want to be able to filter your `GridField`, you will need to tell the `GridField` which fields to filter against and how to do so. The `GridFieldFilterHeader` uses a [`SearchContext`](api:SilverStripe\ORM\Search\SearchContext) implementation to do most of the heavy lifting. + +The [`BasicSearchContext`](api:SilverStripe\ORM\Search\BasicSearchContext) is designed to be used for data that isn't represented by `DataObject` records. + +```php +use SilverStripe\Forms\FieldList; +use SilverStripe\Forms\GridField\GridFieldFilterHeader; +use SilverStripe\Forms\HiddenField; +use SilverStripe\Forms\TextField; +use SilverStripe\ORM\Search\BasicSearchContext; + +// Instantiate a BasicSearchContext and tell it which fields to search against +$searchContext = BasicSearchContext::create(null); +$searchFields = [ + HiddenField::create(BasicSearchContext::config()->get('general_search_field_name')), + TextField::create('FeldName', 'Search Field Label'), +]; +// Pass the BasicSearchContext into the GridFieldFilterHeader component +$searchContext->setFields(FieldList::create($searchFields)); +$gridField->getConfig()->getComponentByType(GridFieldFilterHeader::class)->setSearchContext($searchContext); +``` + +### Exporting data {#arraydata-export} + +If you want to export or print your data, you don't have to do anything special - just make sure to include the `GridFieldExportButton` and `GridFieldPrintButton` components. + +```php +use SilverStripe\Forms\GridField\GridFieldExportButton; +use SilverStripe\Forms\GridField\GridFieldPrintButton; +$gridField->getConfig()->addComponents([ + GridFieldExportButton::create('buttons-before-left'), + GridFieldPrintButton::create('buttons-before-left'), +]); +``` + +These will both use the field list you [passed into `GridFieldDataColumns`](#arraydata-display-as-rows) to know which fields they should use - though you can explicitly call [`GridFieldExportButton::setExportColumns()`](api:SilverStripe\Forms\GridField\GridFieldExportButton::setExportColumns()) and [`GridFieldPrintButton::setPrintColumns()`](api:SilverStripe\Forms\GridField\GridFieldPrintButton::setPrintColumns()) if you want to export/print different columns than those displayed in the grid view. + +### Viewing data in a form {#arraydata-view} + +For data to be viewed in a read-only form, each record in the list must have an `ID` field, and the value of that field must be a positive integer. This is used in the URL for the form. Without it, the gridfield has no way to know which record it should be displaying in the form. + +You'll need to add a `GridFieldDetailForm` component to the `GridField` and tell it how to represent your data by passing a [`FieldList`](api:SilverStripe\Forms\FieldList) into [`GridFieldDetailForm::setFields()`](api:SilverStripe\Forms\GridField\GridFieldDetailForm::setFields()). + +[hint] +Because `ArrayData` doesn't implement a `canEdit()` method, the form will be implicitly turned into a read-only form for you. You don't need to worry about passing in read-only form fields. +[/hint] + +```php +use SilverStripe\Forms\FieldList; +use SilverStripe\Forms\GridField\GridFieldDetailForm; +use SilverStripe\Forms\GridField\GridFieldViewButton; +use SilverStripe\Forms\HiddenField; +use SilverStripe\Forms\TextField; + +$detailForm = GridFieldDetailForm::create(); +$detailForm->setFields(FieldList::create([ + HiddenField::create('ID'), + TextField::create('FeldName', 'View Field Label'), +])); +$gridField->getConfig()->addComponents([ + GridFieldViewButton::create(), + $detailForm, +]); +``` + +## Representing data in your own class + +As mentioned [in the preamble above](#using-gridfield-with-arbitrary-data), the class representing your data must be a subclass of `ViewableData` in order for it to be used in a `GridField`. + +Representing data in your own class adds some complexity, but empowers content authors to create, update and delete entries via the `GridField`. + +Note that all of the methods that this documentation implements can be ommitted, with exception of the [editing data in a form](#custom-edit) section. +However, if you omit these method implementations, you must instead pass the required information through to the relevant `GridField` components as shown in [representing data with `ArrayData`](#representing-data-with-arraydata) above. + +### Displaying data as rows in a `GridField` {#custom-display-as-rows} + +To represent your data as rows in a `GridField`, you can rely on the default `GridFieldConfig` object that the field will build for itself. If you implement a `summaryFields()` method in your data class, the `GridField` will call that method to find out what fields it should display. + +[hint] +The `ID` field shown here isn't necessary if you only want to view/edit the records as rows in the `GridField`, but if you want to be able to view *each* record in a read-only form view, the `ID` field is mandatory. + +See [viewing data in a form](#custom-view) for more information. +[/hint] + +```php +namespace App\Data; + +use SilverStripe\View\ViewableData; + +class DataRepresentation extends ViewableData +{ + private int $id; + + private string $title; + + public function __construct(int $id, string $title) + { + $this->id = $id; + $this->title = $title; + } + + public function getID(): int + { + return $this->id; + } + + public function getTitle(): string + { + return $this->title; + } + + /** + * Used to detect gridfield columns. + * @return string[] Associative array where the keys are field names and the values are display labels. + */ + public function summaryFields(): array + { + return ['Title' => 'Title']; + } +} +``` + +```php +use App\Data\DataRepresentation; +use SilverStripe\Forms\GridField\GridField; +use SilverStripe\ORM\ArrayList; + +$list = ArrayList::create([ + DataRepresentation::create(1, 'This is an item'), + DataRepresentation::create(2, 'This is a different item'), +]); + +$gridField = GridField::create('MyData', 'My data', $list); +``` + +If you don't want filtering functionality, you'll also need to remove the [`GridFieldFilterHeader`](api:SilverStripe\Forms\GridField\GridFieldFilterHeader) component from the gridfield: + +```php +use SilverStripe\Forms\GridField\GridFieldFilterHeader; +$gridField->getConfig()->removeComponentsByType(GridFieldFilterHeader::class); +``` + +### Filtering data {#custom-filter} + +If you want to be able to filter your `GridField`, you will need to tell the `GridField` which fields to filter against and how to do so. As shown in [filtering `ArrayData`](#arraydata-filter) above, you use a `BasicSearchContext` to do the heavy lifting here - but we don't have to explicitly pass it to the `GridField` if we implement the `getDefaultSearchContext()` method. + +What's more, we don't have to pass the search fields into the `BasicSearchContext` instance either if we implement a `scaffoldSearchFields()` method. + +[hint] +You can optionally implement the `i18n_singular_name()` method to return a localised string to represent the plural name of this model. This is used in the filter header as the placeholder text for the general search field. +[/hint] + +```php +namespace App\Data; + +use SilverStripe\Forms\FieldList; +use SilverStripe\Forms\HiddenField; +use SilverStripe\Forms\TextField; +use SilverStripe\ORM\Search\BasicSearchContext; +use SilverStripe\View\ViewableData; + +class DataRepresentation extends ViewableData +{ + // ... + + public function getDefaultSearchContext() + { + return BasicSearchContext::create(static::class); + } + + public function scaffoldSearchFields() + { + return FieldList::create([ + HiddenField::create(BasicSearchContext::config()->get('general_search_field_name')), + TextField::create('Title', 'Title'), + ]); + } + + // ... +} +``` + +No changes are required to the `GridField` components, assuming you didn't remove the `GridFieldFilterHeader` component. + +[hint] +The `BasicSearchContext` respects some (*but not all*) [`$searchable_fields` configuration options](/developer_guides/model/scaffolding/#searchable-fields), so you can implement a `searchableFields()` method in your class to further customise the `GridField` filtering experience. +[/hint] + +### Exporting data {#custom-export} + +Just like with `ArrayData`, to export or print data we don't need to do anything more than ensure the relevant components are in the `GridField` config. + +```php +use SilverStripe\Forms\GridField\GridFieldExportButton; +use SilverStripe\Forms\GridField\GridFieldPrintButton; +$gridField->getConfig()->addComponents([ + GridFieldExportButton::create('buttons-before-left'), + GridFieldPrintButton::create('buttons-before-left'), +]); +``` + +### Viewing data in a form {#custom-view} + +The same requirement of a positive integer `ID` field as described in [viewing `ArrayData` in a form](#arraydata-view) above applies here too. + +If the class representing your data has a `getCMSFields()` method, the return value of that method will be used for the fields displayed in form. + +If your class doesn't implement a `canEdit()` method, or it does and the method returns `false`, the form will be read-only. + +[hint] +You can optionally implement the `i18n_plural_name()` method to return a localised string to represent the singular name of this model. This is used in the add button, breadcrumbs, and toasts. +[/hint] + +```php +namespace App\Data; + +use SilverStripe\Forms\FieldList; +use SilverStripe\Forms\HiddenField; +use SilverStripe\Forms\TextField; +use SilverStripe\View\ViewableData; + +class DataRepresentation extends ViewableData +{ + // ... + + public function getCMSFields() + { + return FieldList::create([ + HiddenField::create('ID'), + TextField::create('FeldName', 'View Field Label'), + ]); + } + + // ... +} +``` + +You will need to have `GridFieldDetailForm` and `GridFieldViewButton` components in your `GridField` config in order to access the form view. + +```php +use SilverStripe\Forms\GridField\GridFieldDetailForm; +use SilverStripe\Forms\GridField\GridFieldViewButton; + +$gridField->getConfig()->addComponents([ + GridFieldViewButton::create(), + GridFieldDetailForm::create(), +]); +``` + +### Editing data in a form {#custom-edit} + +There are a few extra pre-requisites to allow content authors to edit data. + +The class representing your data *must* implement [`DataObjectInterface`](api:SilverStripe\ORM\DataObjectInterface) so that your records can be edited. + +For new records, the `write()` method *must* set the `ID` field on the record, so that the user is correctly redirected to the edit form of the new record after saving it. + +Records with no `ID` field or which have a non-numeric value for their `ID` field are considered new (unsaved) records. + +[hint] +If you have specific validation rules you want to apply, you can also implement a `getCMSCompositeValidator()` method as described in [validation in the CMS](/developer_guides/forms/validation/#validation-in-the-cms). +[/hint] + +```php +namespace App\Data; + +use LogicException; +use SilverStripe\ORM\DataObjectInterface; +use SilverStripe\View\ViewableData; + +class DataRepresentation extends ViewableData implements DataObjectInterface +{ + // ... + + public function write() + { + // Do whatever you need to write the record - e.g. send it to a web API + + // You MUST set the ID on newly created records + if (!$this->ID) { + $this->ID = $idFromApi; + } + } + + public function delete() + { + if (!$this->ID) { + throw new LogicException('delete() called on a record without an ID'); + } + + // Do whatever you need to delete the record - e.g. send a deletion request to a web API + + $this->ID = 0; + } + + /** + * Sets the value from the form. + * Since the data comes straight from a form it can't be trusted and you should make sure + * to validate / escape it as appropriate. + */ + public function setCastedField($fieldName, $val) + { + $this->$fieldName = $val; + } + + /** + * Determines if the current logged in user is allowed to edit this record. + */ + public function canEdit() + { + return true; + } + + /** + * Determines if the current logged in user is allowed to create new records. + * + * This method is optional - you only need this if you will allow creating new records. + * If the method isn't implemented, it's assumed that nobody is allowed to create them. + */ + public function canCreate() + { + return true; + } + + /** + * Determines if the current logged in user is allowed to delete records. + * + * This method is optional - you only need this if you will allow deleting records. + * If the method isn't implemented, it's assumed that nobody is allowed to delete them. + */ + public function canDelete() + { + return true; + } + + // ... +} +``` + +You should add a [`GridFieldEditButton`](api:SilverStripe\Forms\GridField\GridFieldEditButton) component to your `GridField` config. + +```php +use SilverStripe\Forms\GridField\GridFieldEditButton; +$gridField->getConfig()->addComponent(GridFieldEditButton::create()); +``` + +You can also enable creating new records and deleting records by adding the [`GridFieldAddNewButton`](api:SilverStripe\Forms\GridField\GridFieldAddNewButton) and [`GridFieldDeleteAction`](api:SilverStripe\Forms\GridField\GridFieldDeleteAction) components to your `GridField` config. + +[hint] +At this point your `GridField` config is essentially the same as a [`GridFieldConfig_RecordEditor`](api:SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor) - so you could set up your `GridField` like so: + +```php +use SilverStripe\Forms\GridField\GridField; +use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor; +$gridField = GridField::create('MyData', 'My data', $list, GridFieldConfig_RecordEditor::create()); +``` + +[/hint] diff --git a/en/02_Developer_Guides/03_Forms/Field_types/04_GridField.md b/en/02_Developer_Guides/03_Forms/Field_types/04_GridField.md index 3073c0772..8b68b65f8 100644 --- a/en/02_Developer_Guides/03_Forms/Field_types/04_GridField.md +++ b/en/02_Developer_Guides/03_Forms/Field_types/04_GridField.md @@ -9,7 +9,9 @@ icon: table [GridField](api:SilverStripe\Forms\GridField\GridField) is Silverstripe CMS's implementation of data grids. The main purpose of this field type is to display tabular data in a format that is easy to view and modify. It can be thought of as a HTML table with some tricks. -Usually `GridField` is used with `DataObject` records - but it can be used with data that isn't represented by `DataObject` records as well. See [using `GridField` with arbitrary data](/developer_guides/forms/using_gridfield_with_arbitrary_data/) for more information. +Usually `GridField` is used with `DataObject` records - but it can be used with data that isn't represented by `DataObject` records as well. + +See [using `GridField` with arbitrary data](/developer_guides/forms/using_gridfield_with_arbitrary_data/) for more information. [info] `GridField` powers the automated data UI of [ModelAdmin](api:SilverStripe\Admin\ModelAdmin). For more information about `ModelAdmin` see the @@ -250,7 +252,7 @@ Otherwise, you'll need to pass in a [`FieldList`](api:SilverStripe\Forms\FieldLi [/info] [warning] -The class representing your data _must_ implement [`DataObjectInterface`](api:SilverStripe\ORM\DataObjectInterface) so that your records can be edited. +The class representing your data *must* implement [`DataObjectInterface`](api:SilverStripe\ORM\DataObjectInterface) so that your records can be edited. See [using `GridField` with arbitrary data](/developer_guides/forms/using_gridfield_with_arbitrary_data/) for more information. [/warning] diff --git a/en/04_Changelogs/5.2.0.md b/en/04_Changelogs/5.2.0.md index e61755490..945f8ef3a 100644 --- a/en/04_Changelogs/5.2.0.md +++ b/en/04_Changelogs/5.2.0.md @@ -82,7 +82,7 @@ $query->setAllowCollidingFieldStatements(true); It has historically been difficult to use a `GridField` to display data that isn't represented by `DataObject` records - and even more difficult to edit that data. -We have removed several barriers to using the `GridField` to display arbitrary data, and improved error messaging when specific information cannot be dynamically identified, such as which columns to display and what form fields to use when viewing or editing data. +We have removed several barriers to using the `GridField` to display arbitrary data. Descriptive exceptions will be thrown when specific information cannot be dynamically identified, such as which columns to display and what form fields to use when viewing or editing data. Note that these new exceptions do not break backwards compatibility. Any scenario that will throw an exception now would have already done so - but the old exception would not have been sufficiently descriptive to quickly understand what changes are required to get a functioning `GridField`. This applies to all `GridFieldComponent` classes in `silverstripe/framework` except for [`GridFieldAddExistingAutocompleter`](api:SilverStripe\Forms\GridField\GridFieldAddExistingAutocompleter) and [`GridFieldLevelup`](api:SilverStripe\Forms\GridField\GridFieldLevelup), which both explicitly require the model class for the associated `GridField` to be a subclass of `DataObject`.