After getting used to the DataObjectManager in Silverstripe 2.4 and below I’ve been trying to achieve similar functionality using the new GridFields in Silverstripe 3. A fairly common situation I encounter is the need to handle many to many relationships. In the below example, I have Tour Pages, which can have many Activities associated with them and an Activity can also be associated with many Tour Pages.
We create the activity class:
MySite/Code/Activity.php
class Activity extends DataObject{
public static $db = array(
'Title' => 'Varchar(255)',
'Summary' => 'HTMLText'
);
static $belongs_many_many = array(
'TourPages' => 'TourPage'
);
public static $summary_fields = array(
'Title' => 'Title'
);
public function getCMSFields_forPopup() {
return new FieldList(
new TextField('Title', 'Title'),
new HTMLEditorField('Summary', 'Brief Summary'),
);
}
} |
class Activity extends DataObject{
public static $db = array(
'Title' => 'Varchar(255)',
'Summary' => 'HTMLText'
);
static $belongs_many_many = array(
'TourPages' => 'TourPage'
);
public static $summary_fields = array(
'Title' => 'Title'
);
public function getCMSFields_forPopup() {
return new FieldList(
new TextField('Title', 'Title'),
new HTMLEditorField('Summary', 'Brief Summary'),
);
}
}
We now need to add the relationship to the tour page and create the GridField: Note the unlinkrelation delete action. There was a change to the codebase in the last 7 days or so (at date of post) which changed the action of the delete button in GridFields to actually delete the item rather than unlink the relation. This adds in the unlink relation button so it will not affect any of your other relations.
MySite/Code/TourPage.php
static $many_many = array(
'Activities' => 'Activity'
);
function getCMSFields() {
$fields = parent::getCMSFields();
...
$gridFieldConfig = GridFieldConfig_RelationEditor::create()->addComponents(
new GridFieldDeleteAction('unlinkrelation')
);
$gridField = new GridField("Activities", "Activities", $this->Activities(), $gridFieldConfig);
$fields->addFieldToTab("Root.Activities", $gridField);
return $fields;
} |
static $many_many = array(
'Activities' => 'Activity'
);
function getCMSFields() {
$fields = parent::getCMSFields();
...
$gridFieldConfig = GridFieldConfig_RelationEditor::create()->addComponents(
new GridFieldDeleteAction('unlinkrelation')
);
$gridField = new GridField("Activities", "Activities", $this->Activities(), $gridFieldConfig);
$fields->addFieldToTab("Root.Activities", $gridField);
return $fields;
}
Once you have run a dev/build you will have something similar to the below.

You may have noticed the “Allow Drag and Drop” link to allow you to order these.
To allow Drag and Drop reordering, you will need to download the SortableGridField module and run a dev/build/flush=all. You will also need to add a few additional lines of code to the Tour Page.
Firstly you need to add a many_many_extraFields static to add an extra row to the linking table to store the sort order. In my example:
static $many_many_extraFields = array(
'Activities' => array(
'SortOrder' => "Int"
)
} |
static $many_many_extraFields = array(
'Activities' => array(
'SortOrder' => "Int"
)
}
Finally add the GridFieldSortableRows to your GridField config:
$gridFieldConfig = GridFieldConfig_RelationEditor::create()->addComponents(
new GridFieldDeleteAction('unlinkrelation'),
new GridFieldSortableRows('SortOrder') //add to allow sorting of many_many's
); |
$gridFieldConfig = GridFieldConfig_RelationEditor::create()->addComponents(
new GridFieldDeleteAction('unlinkrelation'),
new GridFieldSortableRows('SortOrder') //add to allow sorting of many_many's
);
You will also need to sort the GridField based on the sort order field created above when you are creating it:
$gridField = new GridField("Activities", "Activities", $this->Activities()->sort('SortOrder'), $gridFieldConfig); |
$gridField = new GridField("Activities", "Activities", $this->Activities()->sort('SortOrder'), $gridFieldConfig);
So the final code looks like this:
MySite/Code/Activity.php
class Activity extends DataObject{
public static $db = array(
'Title' => 'Varchar(255)',
'Summary' => 'HTMLText'
);
static $belongs_many_many = array(
'TourPages' => 'TourPage'
);
public static $summary_fields = array(
'Title' => 'Title'
);
public function getCMSFields_forPopup() {
return new FieldList(
new TextField('Title', 'Title'),
new HTMLEditorField('Summary', 'Brief Summary'),
);
}
} |
class Activity extends DataObject{
public static $db = array(
'Title' => 'Varchar(255)',
'Summary' => 'HTMLText'
);
static $belongs_many_many = array(
'TourPages' => 'TourPage'
);
public static $summary_fields = array(
'Title' => 'Title'
);
public function getCMSFields_forPopup() {
return new FieldList(
new TextField('Title', 'Title'),
new HTMLEditorField('Summary', 'Brief Summary'),
);
}
}
MySite/Code/TourPage.php
static $many_many = array(
'Activities' => 'Activity'
);
static $many_many_extraFields = array(
'Activities' => array(
'SortOrder' => "Int"
)
);
function getCMSFields() {
$fields = parent::getCMSFields();
...
$gridFieldConfig = GridFieldConfig_RelationEditor::create()->addComponents(
new GridFieldDeleteAction('unlinkrelation'),
new GridFieldSortableRows('SortOrder') //add to allow sorting of many_many's
);
$gridField = new GridField("Activities", "Activities", $this->Activities()->sort('SortOrder'), $gridFieldConfig);
$fields->addFieldToTab("Root.Activities", $gridField);
return $fields;
} |
static $many_many = array(
'Activities' => 'Activity'
);
static $many_many_extraFields = array(
'Activities' => array(
'SortOrder' => "Int"
)
);
function getCMSFields() {
$fields = parent::getCMSFields();
...
$gridFieldConfig = GridFieldConfig_RelationEditor::create()->addComponents(
new GridFieldDeleteAction('unlinkrelation'),
new GridFieldSortableRows('SortOrder') //add to allow sorting of many_many's
);
$gridField = new GridField("Activities", "Activities", $this->Activities()->sort('SortOrder'), $gridFieldConfig);
$fields->addFieldToTab("Root.Activities", $gridField);
return $fields;
}