Report about migration of SilverStripe from 2.4 to 3.1
These days I've been working on a migration of a project from SilverStripe 2.4.x to a 3.1.x version. There are already some decent reports on this issue:
So the following text will be more about my personal migration experience and may overlap in some points with those articles.
Convert Models, public static becomes private static
All static model properties for the Database Mapper must be now private instead of public, so for instance:
<?php class MyPage extends Page {
public static $db = [];
}
becomes
<?php
class MyPage extends Page {
private static $db = [];
}
The same is for summary_fields
, indexes
, has_one
, has_many
etc …
Templates: from control to loop
All control
sections are replaced by loop
. So
<% control Children %><li>$Title</li>
becomes
<li>$Title</li>
This can be easily replaced with a regex on all *.ss template files:
replace with
and replace with
.
Replace obsolete form field classes and fix tab's naming in getCMSFields
- FieldSet -> FieldList
- ImageField -> UploadField
- DataObjectSet -> ArrayList
- …
The tabs will be renamed from Root.Content.Main
to Root.Main
and so on (Content
is not existing anymore).
From DataObjectManager to GridField
Since the SS versions below version 3 didn't provide a feature to manage 1:n and n:m relations in the CMS, most of the time the popular module DataObjectManager helped out. It provided a sortable-feature, bulk upload and 1:n and n:m (editable) relations. Both features can now be managed by the GridField with these optional addons (all are manageable via Composer):
- SortableGridField to sort DataObjects
- GridFieldBulkEditingTools to upload many files to a DataObject with a file relation
- GridFieldRelationHandler for also managing has_one relations
From ImageDataObjectManager to SortableGridField
Lets say we have a sortable GalleryImage:
<?php class GalleryImage extends DataObject {
private static $db = [
'Title' => 'Varchar(255)',
'SortOrder' => 'Int', // we have to attach this field for the sortable grid field explicitly
];
private static $has_one = [
'Image' => 'Image',
];
}
First we have to increase the SortOrder
(DataObjectManager started at 0, GridField starts at 1):
UPDATE GalleryImage SET SortOrder=SortOrder+1 WHERE 1;
Finnaly we have to replace in getCMSFields
:
<?php function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Content.Main', new ImageDataObjectManager($this, 'Images', 'GalleryImage', 'Image'));
return $fields;
}
with
<?php function getCMSFields() {
$fields = parent::getCMSFields();
$config = GridFieldConfig_RelationEditor::create();
$config->addComponent(new GridFieldSortableRows('SortOrder'));
$gridField = new GridField("GalleryImage", "Gallery Images", $this->GalleryImages(), $config);
// optional if you want specific rows
$gridField->getConfig()->getComponentByType('GridFieldDataColumns')->setDisplayFields([
"Title" => "MyTitle",
"Image.CMSThumbnail" => "Images",
]);
$fields->addFieldToTab('Root.Main', $gridField);
return $fields;
}
I recommend to define a method on your page model to get preconfigured GridFields (saves a lot of repeating code). For instance:
<?php protected function fieldForSortableManyCMSFields($title, $model, $fieldMapping = null) {
$relation = array_keys($model)[0];
$modelClass = $model[array_keys($model)[0]];
$config = GridFieldConfig_RelationEditor::create();
// optional: set a pagination number
$config->getComponentByType('GridFieldPaginator')->setItemsPerPage(100);
$config->addComponent(new GridFieldSortableRows('SortOrder'));
$gridField = new GridField($relation, $title, $this->{$relation}(), $config);
if (is_array($fieldMapping)) {
$gridField->getConfig()->getComponentByType('GridFieldDataColumns')->setDisplayFields($fieldMapping);
}
return $gridField;
}
So that you can easily call in getCMSFields
later:
<?php function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Images', $this->fieldForSortableManyCMSFields("Gallery Images", [ "Images" => "GalleryImage" ], ["Title" => "Title", "Image.CMSThumbnail" => "Image"]));
return $fields;
}