Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save tiller1010/8488c26a760e9a5cdf07341759691ca6 to your computer and use it in GitHub Desktop.

Select an option

Save tiller1010/8488c26a760e9a5cdf07341759691ca6 to your computer and use it in GitHub Desktop.
SilverStripe Subclass extension points

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);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment