Version 4 supported
This version of Silverstripe CMS is still supported though will not receive any additional features. Go to documentation for the most recent stable version.

GridField

GridField can only be used with $list data sets that are of the type SS_List such as DataList or ArrayList.

GridField is Silverstripe CMS'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 = GridField::create($name, $title, $list);

GridField powers the automated data UI of ModelAdmin. For more information about ModelAdmin see the Customizing the CMS guide.

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.

// app/src/PageType/MyPage.php
namespace App\PageType;

use Page;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Forms\GridField\GridField;

class MyPage extends Page
{
    public function getCMSFields()
    {
        $fields = parent::getCMSFields();

        $fields->addFieldToTab(
            'Root.Pages',
            GridField::create('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:

The configuration of those GridFieldComponent instances and the addition or subtraction of components is done through the getConfig() method on GridField.

// app/src/PageType/MyPage.php
namespace App\PageType;

use Page;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldDataColumns;

class MyPage extends Page
{
    public function getCMSFields()
    {
        $fields = parent::getCMSFields();

        $fields->addFieldToTab(
            'Root.Pages',
            $grid = GridField::create('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(GridFieldDataColumns::create());

// 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\GridFieldDataColumns;
use SilverStripe\Forms\GridField\GridFieldFilterHeader;

// ...

$config->addComponent(GridFieldFilterHeader::create(), GridFieldDataColumns::class);

We can add multiple components in one call.

use SilverStripe\Forms\GridField\GridFieldDataColumns;
use SilverStripe\Forms\GridField\GridFieldToolbarHeader;

// ...

$config->addComponents(
    GridFieldDataColumns::create(),
    GridFieldToolbarHeader::create()
);

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.

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..
// ... GridFieldToolbarHeader::create()
// ... GridFieldSortableHeader::create()
// ... GridFieldFilterHeader::create()
// ... GridFieldDataColumns::create()
// ... GridFieldPageCount::create('toolbar-header-right')
// ... GridFieldPaginator::create($itemsPerPage)

GridFieldConfig_RecordViewer

The DataObject class displayed must define a canView() method that returns a boolean on whether the user can view this record.

Similar to GridFieldConfig_Base with the addition support of the ability to view a GridFieldDetailForm containing a read-only view of the data record.

The data row show must be a DataObject subclass. The fields displayed in the read-only view come from DataObject::getCMSFields().

use SilverStripe\Forms\GridField\GridFieldConfig_RecordViewer;

// ...

$config = GridFieldConfig_RecordViewer::create();
$gridField->setConfig($config);

// Same as GridFieldConfig_Base with the addition of
// ... GridFieldViewButton::create(),
// ... GridFieldDetailForm::create()

GridFieldConfig_RecordEditor

Permission control for editing and deleting the record uses the canEdit() and canDelete() methods on the DataObject object.

Similar to GridFieldConfig_RecordViewer with the addition support to edit or delete each of the records.

The data row show must be a DataObject subclass. The fields displayed in the edit view come from DataObject::getCMSFields().

use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor;

// ...

$config = GridFieldConfig_RecordEditor::create();
$gridField->setConfig($config);

// Same as GridFieldConfig_RecordViewer with the addition of
// ... GridFieldAddNewButton::create(),
// ... GridFieldEditButton::create(),
// ... GridFieldDeleteAction::create()

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.

use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;

// ...

$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;
use SilverStripe\Forms\GridField\GridFieldEditButton;
use SilverStripe\Forms\GridField\GridField_ActionMenu;

// ...

// `GridFieldConfig::create()` will create an empty configuration (no components).
$config = GridFieldConfig::create();

// add a component
$config->addComponent();

$config->addComponents(
    GridFieldDataColumns::create(),
    GridFieldEditButton::create(),
    GridField_ActionMenu::create()
);

// 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 App\Forms\FieldList;
use App\Forms\TextField;
use SilverStripe\Forms\GridField\GridFieldDetailForm;

$form = $gridField->getConfig()->getComponentByType(GridFieldDetailForm::class);
$form->setFields(FieldList::create(
    TextField::create('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].

namespace App\Model;

use SilverStripe\ORM\DataObject;

class Team extends DataObject
{
    private static $db = [
        'Name' => 'Text',
    ];

    private static $belongs_many_many = [
        'Players' => Player::class,
    ];
}
namespace App\Model;

use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
use SilverStripe\Forms\GridField\GridFieldDetailForm;
use SilverStripe\Forms\TextField;
use SilverStripe\ORM\DataObject;

class Player extends DataObject
{
    private static $db = [
        'Name' => 'Text',
    ];

    private static $many_many = [
        'Teams' => Team::class,
    ];

    private 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
                TextField::create('ManyMany[Position]', 'Current Position')
            );

            $config = GridFieldConfig_RelationEditor::create();
            $config->getComponentByType(GridFieldDetailForm::class)->setFields($teamFields);

            $gridField = GridField::create('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 markup
  • before/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(GridFieldButtonRow::create('after'));
$config->addComponent(GridFieldPrintButton::create('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 <div>.

Please note that in templates, you'll need to escape the dollar sign on \$DefineFragment. These are specially processed placeholders as opposed to native template syntax.

namespace App\Form\GridField;

use SilverStripe\Forms\GridField\AbstractGridFieldComponent;
use SilverStripe\Forms\GridField\GridField_HTMLProvider;

class MyAreaComponent extends AbstractGridFieldComponent 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:

namespace App\Form\GridField;

use SilverStripe\Forms\GridField\AbstractGridFieldComponent;
use SilverStripe\Forms\GridField\GridField_HTMLProvider;

class MyShareLinkComponent extends AbstractGridFieldComponent 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

use SilverStripe\Forms\GridField\GridFieldPrintButton;

GridFieldPrintButton::create('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 transferred between page requests by being inserted as a hidden field in the form.

The GridState_Component sets and gets data from the GridState.

Saving GridField_FormAction state

By default state used for performing form actions is saved in the session and tagged with a key like gf_abcd1234. In some cases session may not be an appropriate storage method. The storage method can be configured:

Name: mysitegridfieldconfig
After: gridfieldconfig
---
SilverStripe\Core\Injector\Injector:
  SilverStripe\Forms\GridField\FormAction\StateStore:
    class: SilverStripe\Forms\GridField\FormAction\AttributeStore

The AttributeStore class configures action state to be stored in the DOM and sent back on the request that performs the action. Custom storage methods can be created and used by implementing the StateStore interface and configuring Injector in a similar fashion.

API documentation