SilverStripe DataObject subclasses cannot override a base class method with an extension point ($this->extend()) and define an extension point themselves.
This will cause extend to be called twice, which can lead to duplication.
<?php
class ExampleDataObject extends DataObject
{
public function getCMSFields()
{
$fields = FieldList::create(
...
);
// Base class uses extension point for cms fields
$this->extend('updateCMSFields', $fields);
return $fields;
}
public function getTestVal()
{
$val = 'foo';
// Base class uses extension point for a value
$this->extend('updateTestVal', $val);
return $val;
}
}If a subclass were to override any methods and use an extension point, it would cause duplication.
To work around this, use beforeUpdateCMSFields and/or beforeExtending.
This will allow anything added in the subclass to be visible in a DataExtension, without causing duplication.
<?php
class ExampleDataObjectSubclass extends ExampleDataObject
{
/*
DOES NOT WORK.
This could cause FormFields added in a DataExtension to be added more than once.
This is usually only an issue when form Tabs are used, as existing FormFields may be skipped.
*/
// public function getCMSFields()
// {
// $fields = parent::getCMSFields();
// $fields->push(TextField::create('SubclassField'));
// $this->extend('updateCMSFields', $fields);
// return $fields;
// }
public function getCMSFields()
{
$this->beforeUpdateCMSFields(function ($fields) {
$fields->push(TextField::create('SubclassField', 'A subclass field that will be accessible in updateCMSFields because of the parent extension point'));
});
// Will call the parent $this->extend()
$fields = parent::getCMSFields();
return $fields;
}
/*
DOES NOT WORK
This would call updateTestVal twice, with the first call not including the added "baz"
*/
// public function getTestVal()
// {
// $val = parent::getTestVal();
// $val .= 'baz';
// $this->extend('updateTestVal', $val);
// return $val;
// }
public function getTestVal()
{
$this->beforeExtending('updateTestVal', function (&$val) {
$val .= 'baz';
});
// Will call the parent $this->extend()
$val = parent::getTestVal();
return $val;
}
}By using beforeUpdateCMSFields and beforeExtending, we can reference FormFields and values added in the subclass without causing duplication.
<?php
class ExampleDataObjectSubclassExtension extends DataExtension
{
public function updateCMSFields($fields)
{
$fields->insertBefore('SubclassField', TextField::create('test', 'test', $this->owner->getTestVal()));
}
public function updateTestVal(&$val)
{
$val = str_replace('baz', 'bar', $val);
}
}
ExampleDataObjectSubclass::add_extension(ExampleDataObjectSubclassExtension::class);