* WIP GridField action menu work, the gist of the idea is using a new gridfield component * Add delete action to actions menu * Actions are added automatically to action menu (allows for extension) * Add test and minor changes * Add docs and minor changes * Refactor ActionMenuItem into distinct types, general ActionMenu cleanup * Add icons and fix title * Pass columnName, so it can be used by components * Update test to open and find action menu buttons * Add section in changelog upgrade section for GridField_ActionMenu
16 KiB
title: GridField summary: How to use the GridField class for managing tabular data.
GridField
GridField is SilverStripe's implementation of data grids. The main purpose of the FormField
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.
use SilverStripe\Forms\GridField\GridField;
$field = new GridField($name, $title, $list);
Each GridField
is built from a number of components grouped into the GridFieldConfig. Without any components,
a GridField
has almost no functionality. The GridFieldConfig
instance and the attached GridFieldComponent are
responsible for all the user interactions including formatting data to be readable, modifying data and performing any
actions such as deleting records.
mysite/code/Page.php
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\CMS\Model\SiteTree;
class Page extends SiteTree
{
public function getCMSFields()
{
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Pages',
new GridField('Pages', 'All pages', SiteTree::get())
);
return $fields;
}
}
This will display a bare bones GridField
instance under Pages
tab in the CMS. As we have not specified the
GridField
configuration, the default configuration is an instance of GridFieldConfig_Base which provides:
- GridFieldToolbarHeader
- GridFieldSortableHeader
- GridFieldFilterHeader
- GridFieldDataColumns
- GridFieldPageCount
- GridFieldPaginator
The configuration of those GridFieldComponent
instances and the addition or subtraction of components is done through
the getConfig()
method on GridField
.
mysite/code/Page.php
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\CMS\Model\SiteTree;
class Page extends SiteTree
{
public function getCMSFields()
{
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Pages',
$grid = new GridField('Pages', 'All pages', SiteTree::get())
);
// GridField configuration
$config = $grid->getConfig();
//
// Modification of existing components can be done by fetching that component.
// Consult the API documentation for each component to determine the configuration
// you can do.
//
$dataColumns = $config->getComponentByType(GridFieldDataColumns::class);
$dataColumns->setDisplayFields([
'Title' => 'Title',
'Link'=> 'URL',
'LastEdited' => 'Changed'
]);
return $fields;
}
}
With the GridFieldConfig
instance, we can modify the behavior of the GridField
.
use SilverStripe\Forms\GridField\GridFieldConfig;
use SilverStripe\Forms\GridField\GridFieldDataColumns;
// `GridFieldConfig::create()` will create an empty configuration (no components).
$config = GridFieldConfig::create();
// add a component
$config->addComponent(new GridFieldDataColumns());
// Update the GridField with our custom configuration
$gridField->setConfig($config);
GridFieldConfig
provides a number of methods to make setting the configuration easier. We can insert a component
before another component by passing the second parameter.
use SilverStripe\Forms\GridField\GridFieldFilterHeader;
use SilverStripe\Forms\GridField\GridFieldDataColumns;
$config->addComponent(new GridFieldFilterHeader(), GridFieldDataColumns::class);
We can add multiple components in one call.
$config->addComponents(
new GridFieldDataColumns(),
new GridFieldToolbarHeader()
);
Or, remove a component.
use SilverStripe\Forms\GridField\GridFieldDeleteAction;
$config->removeComponentsByType(GridFieldDeleteAction::class);
Fetch a component to modify it later on.
use SilverStripe\Forms\GridField\GridFieldFilterHeader;
$component = $config->getComponentByType(GridFieldFilterHeader::class)
Here is a list of components for use bundled with the core framework. Many more components are provided by third-party modules and extensions.
- GridField_ActionMenu
- GridFieldToolbarHeader
- GridFieldSortableHeader
- GridFieldFilterHeader
- GridFieldDataColumns
- GridFieldDeleteAction
- GridFieldViewButton
- GridFieldEditButton
- GridFieldExportButton
- GridFieldPrintButton
- GridFieldPaginator
- GridFieldDetailForm
Bundled GridFieldConfig
As a shortcut, GridFieldConfig
subclasses can define a list of GridFieldComponent
objects to use. This saves
developers manually adding each component.
GridFieldConfig_Base
A simple read-only and paginated view of records with sortable and searchable headers.
use SilverStripe\Forms\GridField\GridFieldConfig_Base;
$config = GridFieldConfig_Base::create();
$gridField->setConfig($config);
// Is the same as adding the following components..
// .. new GridFieldToolbarHeader()
// .. new GridFieldSortableHeader()
// .. new GridFieldFilterHeader()
// .. new GridFieldDataColumns()
// .. new GridFieldPageCount('toolbar-header-right')
// .. new GridFieldPaginator($itemsPerPage)
GridFieldConfig_RecordViewer
Similar to GridFieldConfig_Base
with the addition support of the ability to view a GridFieldDetailForm
containing
a read-only view of the data record.
use SilverStripe\Forms\GridField\GridFieldConfig_RecordViewer;
$config = GridFieldConfig_RecordViewer::create();
$gridField->setConfig($config);
// Same as GridFieldConfig_Base with the addition of
// .. new GridFieldViewButton(),
// .. new GridFieldDetailForm()
GridFieldConfig_RecordEditor
Similar to GridFieldConfig_RecordViewer
with the addition support to edit or delete each of the records.
$config = GridFieldConfig_RecordEditor::create();
$gridField->setConfig($config);
// Same as GridFieldConfig_RecordViewer with the addition of
// .. new GridFieldAddNewButton(),
// .. new GridFieldEditButton(),
// .. new GridFieldDeleteAction()
GridFieldConfig_RelationEditor
Similar to GridFieldConfig_RecordEditor
, but adds features to work on a record's has-many or many-many relationships.
As such, it expects the list used with the GridField
to be a instance of RelationList
.
$config = GridFieldConfig_RelationEditor::create();
$gridField->setConfig($config);
This configuration adds the ability to searched for existing records and add a relationship
(GridFieldAddExistingAutocompleter
).
Records created or deleted through the GridFieldConfig_RelationEditor
automatically update the relationship in the
database.
GridField_ActionMenu
The GridField_ActionMenu
component provides a dropdown menu which automatically bundles GridField actions into a react based dropdown. It is included by default on GridFieldConfig_RecordEditor
and GridFieldConfig_RelationEditor
configs.
To add it to a GridField, add the GridField_ActionMenu
component and any action(s) that implement GridField_ActionMenuItem
(such as GridFieldEditButton
or GridFieldDeleteAction
) to the GridFieldConfig
.
use SilverStripe\Forms\GridField\GridFieldConfig;
use SilverStripe\Forms\GridField\GridFieldDataColumns;
// `GridFieldConfig::create()` will create an empty configuration (no components).
$config = GridFieldConfig::create();
// add a component
$config->addComponent();
$config->addComponents(
new GridFieldDataColumns(),
new GridFieldEditButton(),
new GridField_ActionMenu()
);
// Update the GridField with our custom configuration
$gridField->setConfig($config);
GridFieldDetailForm
The GridFieldDetailForm
component drives the record viewing and editing form. It takes its' fields from
DataObject->getCMSFields()
method but can be customised to accept different fields via the
GridFieldDetailForm::setFields() method.
use SilverStripe\Forms\GridField\GridFieldDetailForm;
$form = $gridField->getConfig()->getComponentByType(GridFieldDetailForm::class);
$form->setFields(new FieldList(
new TextField('Title')
));
many_many_extraFields
The component also has the ability to load and save data stored on join tables when two records are related via a
"many_many" relationship, as defined through DataObject::$many_many_extraFields. While loading and saving works
transparently, you need to add the necessary fields manually, they're not included in the getCMSFields()
scaffolding.
These extra fields act like usual form fields, but need to be "namespaced" in order for the GridField
logic to detect
them as fields for relation extra data, and to avoid clashes with the other form fields.
The namespace notation is ManyMany[<extradata-field-name>]
, so for example ManyMany[MyExtraField]
.
use SilverStripe\Forms\TextField;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
use SilverStripe\ORM\DataObject;
class Team extends DataObject
{
private static $db = [
'Name' => 'Text'
];
public static $many_many = [
'Players' => 'Player'
];
}
class Player extends DataObject
{
private static $db = [
'Name' => 'Text'
];
public static $many_many = [
'Teams' => 'Team'
];
public static $many_many_extraFields = [
'Teams' => [
'Position' => 'Text'
]
];
public function getCMSFields()
{
$fields = parent::getCMSFields();
if($this->ID) {
$teamFields = singleton('Team')->getCMSFields();
$teamFields->addFieldToTab(
'Root.Main',
// The "ManyMany[<extradata-name>]" convention
new TextField('ManyMany[Position]', 'Current Position')
);
$config = GridFieldConfig_RelationEditor::create();
$config->getComponentByType('GridFieldDetailForm')->setFields($teamFields);
$gridField = new GridField('Teams', 'Teams', $this->Teams(), $config);
$fields->findOrMakeTab('Root.Teams')->replaceField('Teams', $gridField);
}
return $fields;
}
}
Flexible Area Assignment through Fragments
GridField
layouts can contain many components other than the table itself, for example a search bar to find existing
relations, a button to add those, and buttons to export and print the current data. The GridField
has certain defined
areas called fragments
where these components can be placed.
The goal is for multiple components to share the same space, for example a header row. The built-in components:
header
/footer
: Renders in a<thead>
/<tfoot>
, should contain table markupbefore
/after
: Renders before/after the actual<table>
buttons-before-left
/buttons-before-right
/buttons-after-left
/buttons-after-right
: Renders in a shared row before the table. Requires GridFieldButtonRow.
These built-ins can be used by passing the fragment names into the constructor of various components. Note that some GridFieldConfig classes will already have rows added to them. The following example will add a print button at the bottom right of the table.
use SilverStripe\Forms\GridField\GridFieldButtonRow;
use SilverStripe\Forms\GridField\GridFieldPrintButton;
$config->addComponent(new GridFieldButtonRow('after'));
$config->addComponent(new GridFieldPrintButton('buttons-after-right'));
Creating your own Fragments
Fragments are designated areas within a GridField
which can be shared between component templates. You can define
your own fragments by using a \$DefineFragment' placeholder in your components' template. This example will simply create an area rendered before the table wrapped in a simple
use SilverStripe\Forms\GridField\GridField_HTMLProvider;
class MyAreaComponent implements GridField_HTMLProvider
{
public function getHTMLFragments( $gridField)
{
return [
'before' => '<div class="my-area">$DefineFragment(my-area)</div>'
];
}
}
Now you can add other components into this area by returning them as an array from your GridFieldComponent::getHTMLFragments() implementation:
use SilverStripe\Forms\GridField\GridField_HTMLProvider;
class MyShareLinkComponent implements GridField_HTMLProvider
{
public function getHTMLFragments( $gridField)
{
return [
'my-area' => '<a href>...</a>'
];
}
}
Your new area can also be used by existing components, e.g. the GridFieldPrintButton
new GridFieldPrintButton('my-area');
Creating a Custom GridFieldComponent
Customizing a GridField
is easy, applications and modules can provide their own GridFieldComponent
instances to add
functionality. See How to Create a GridFieldComponent.
Creating a Custom GridField_ActionProvider
GridField_ActionProvider provides row level actions such as deleting a record. See How to Create a GridField_ActionProvider.
Saving the GridField State
GridState
is a class that is used to contain the current state and actions on the GridField
. It's transfered
between page requests by being inserted as a hidden field in the form.
The GridState_Component
sets and gets data from the GridState
.