2014-10-22 12:47:56 +02:00
title: Extensions
summary: Extensions and DataExtensions let you modify and augment objects transparently.
# Extensions and DataExtensions
2017-07-03 03:22:12 +02:00
An [Extension ](api:SilverStripe\Core\Extension ) allows for adding additional functionality to a [Object ](api:Object ) or modifying existing functionality
without the hassle of creating a subclass. Developers can add Extensions to any [Object ](api:Object ) subclass within core, modules
2014-10-22 12:47:56 +02:00
or even their own code to make it more reusable.
2017-07-03 03:22:12 +02:00
Extensions are defined as subclasses of either [DataExtension ](api:SilverStripe\ORM\DataExtension ) for extending a [DataObject ](api:SilverStripe\ORM\DataObject ) subclass or
the [Extension ](api:SilverStripe\Core\Extension ) class for non DataObject subclasses (such as [Controller ](api:SilverStripe\Control\Controller ))
2014-10-22 12:47:56 +02:00
2016-10-13 18:39:46 +02:00
< div class = "info" markdown = "1" >
For performance reasons a few classes are excluded from receiving extensions, including `Object` , `ViewableData`
and `RequestHandler` . You can still apply extensions to descendants of these classes.
< / div >
2014-10-22 12:47:56 +02:00
**mysite/code/extensions/MyMemberExtension.php**
2017-08-03 02:51:32 +02:00
```php
2017-08-07 05:11:17 +02:00
use SilverStripe\ORM\DataExtension;
2017-08-05 00:45:24 +02:00
2017-08-07 05:11:17 +02:00
class MyMemberExtension extends DataExtension
{
2014-10-22 12:47:56 +02:00
2017-08-07 05:11:17 +02:00
private static $db = [
'DateOfBirth' => 'SS_Datetime'
];
2014-10-22 12:47:56 +02:00
2017-08-07 05:11:17 +02:00
public function SayHi()
{
// $this->owner refers to the original instance. In this case a `Member` .
return "Hi " . $this->owner->Name;
}
}
2017-08-03 05:35:09 +02:00
2017-08-03 02:51:32 +02:00
```
2014-10-22 12:47:56 +02:00
< div class = "info" markdown = "1" >
Convention is for extension class names to end in `Extension` . This isn't a requirement but makes it clearer
< / div >
After this class has been created, it does not yet apply it to any object. We need to tell SilverStripe what classes
we want to add the `MyMemberExtension` too. To activate this extension, add the following via the [Configuration API ](../configuration ).
**mysite/_config/app.yml**
2017-08-03 02:51:32 +02:00
```yml
2017-10-26 02:22:02 +02:00
SilverStripe\Security\Member:
2017-08-07 05:11:17 +02:00
extensions:
- MyMemberExtension
2017-08-03 02:51:32 +02:00
```
2014-10-22 12:47:56 +02:00
Alternatively, we can add extensions through PHP code (in the `_config.php` file).
2017-08-03 02:51:32 +02:00
```php
2017-10-26 02:22:02 +02:00
SilverStripe\Security\Member::add_extension('MyMemberExtension');
2017-08-03 02:51:32 +02:00
```
2014-10-22 12:47:56 +02:00
This class now defines a `MyMemberExtension` that applies to all `Member` instances on the website. It will have
transformed the original `Member` class in two ways:
2017-07-03 03:22:12 +02:00
* Added a new [DBDatetime ](api:SilverStripe\ORM\FieldType\DBDatetime ) for the users date of birth, and;
2014-10-22 12:47:56 +02:00
* Added a `SayHi` method to output `Hi <User>`
From within the extension we can add more functions, database fields, relations or other properties and have them added
to the underlying `DataObject` just as if they were added to the original `Member` class but without the need to edit
that file directly.
### Adding Database Fields
Extra database fields can be added with a extension in the same manner as if they were placed on the `DataObject` class
they're applied to. These will be added to the table of the base object - the extension will actually edit the $db,
$has_one etc.
**mysite/code/extensions/MyMemberExtension.php**
2017-08-03 02:51:32 +02:00
```php
2017-08-07 05:11:17 +02:00
use SilverStripe\ORM\DataExtension;
2017-08-05 00:45:24 +02:00
2017-08-07 05:11:17 +02:00
class MyMemberExtension extends DataExtension
{
2014-10-22 12:47:56 +02:00
2017-08-07 05:11:17 +02:00
private static $db = [
'Position' => 'Varchar',
];
2014-10-22 12:47:56 +02:00
2017-08-07 05:11:17 +02:00
private static $has_one = [
'Image' => 'Image',
];
2014-10-22 12:47:56 +02:00
2017-08-07 05:11:17 +02:00
public function SayHi()
{
// $this->owner refers to the original instance. In this case a `Member` .
return "Hi " . $this->owner->Name;
}
}
2017-08-03 05:35:09 +02:00
2017-08-03 02:51:32 +02:00
```
2014-10-22 12:47:56 +02:00
**mysite/templates/Page.ss**
2017-08-03 02:51:32 +02:00
```ss
2017-08-07 05:11:17 +02:00
$CurrentMember.Position
$CurrentMember.Image
2017-08-03 02:51:32 +02:00
```
2014-10-22 12:47:56 +02:00
## Adding Methods
2017-07-03 03:22:12 +02:00
Methods that have a unique name will be called as part of the `__call` method on [Object ](api:Object ). In the previous example
2014-10-22 12:47:56 +02:00
we added a `SayHi` method which is unique to our extension.
**mysite/templates/Page.ss**
2017-08-03 02:51:32 +02:00
```ss
2017-08-07 05:11:17 +02:00
< p > $CurrentMember.SayHi< / p >
2014-10-22 12:47:56 +02:00
2017-08-07 05:11:17 +02:00
// "Hi Sam"
2017-08-03 02:51:32 +02:00
```
2014-10-22 12:47:56 +02:00
**mysite/code/Page.php**
2017-08-03 02:51:32 +02:00
```php
2017-10-26 02:22:02 +02:00
use SilverStripe\Security\Security;
2017-08-07 05:11:17 +02:00
$member = Security::getCurrentUser();
echo $member->SayHi;
2014-10-22 12:47:56 +02:00
2017-08-07 05:11:17 +02:00
// "Hi Sam"
2017-08-03 02:51:32 +02:00
```
2014-10-22 12:47:56 +02:00
## Modifying Existing Methods
2015-04-02 04:51:51 +02:00
If the `Extension` needs to modify an existing method it's a little trickier. It requires that the method you want to
2016-03-30 02:17:28 +02:00
customise has provided an *Extension Hook* in the place where you want to modify the data. An *Extension Hook* is done
2017-08-07 02:09:39 +02:00
through the `extend()` method of the [Extensible ](api:SilverStripe\Core\Extensible ) trait.
2014-10-22 12:47:56 +02:00
2017-10-06 17:00:32 +02:00
**Member.php**
2014-10-22 12:47:56 +02:00
2017-08-03 02:51:32 +02:00
```php
2017-08-07 05:11:17 +02:00
public function getValidator()
{
// ..
$this->extend('updateValidator', $validator);
// ..
}
2017-08-03 02:51:32 +02:00
```
2014-10-22 12:47:56 +02:00
Extension Hooks can be located anywhere in the method and provide a point for any `Extension` instances to modify the
variables at that given point. In this case, the core function `getValidator` on the `Member` class provides an
`updateValidator` hook for developers to modify the core method. The `MyMemberExtension` would modify the core member's
validator by defining the `updateValidator` method.
**mysite/code/extensions/MyMemberExtension.php**
2017-08-03 02:51:32 +02:00
```php
2017-08-07 05:11:17 +02:00
use SilverStripe\ORM\DataExtension;
2017-08-05 00:45:24 +02:00
2017-08-07 05:11:17 +02:00
class MyMemberExtension extends DataExtension
{
2014-10-22 12:47:56 +02:00
2017-08-07 05:11:17 +02:00
// ..
2014-10-22 12:47:56 +02:00
2017-08-07 05:11:17 +02:00
public function updateValidator($validator)
{
// we want to make date of birth required for each member
$validator->addRequiredField('DateOfBirth');
}
}
2017-08-03 02:51:32 +02:00
```
2014-10-22 12:47:56 +02:00
< div class = "info" markdown = "1" >
The `$validator` parameter is passed by reference, as it is an object.
< / div >
Another common example of when you will want to modify a method is to update the default CMS fields for an object in an
extension. The `CMS` provides a `updateCMSFields` Extension Hook to tie into.
2017-08-03 02:51:32 +02:00
```php
2017-08-07 05:11:17 +02:00
use SilverStripe\Forms\TextField;
use SilverStripe\AssetAdmin\Forms\UploadField;
use SilverStripe\ORM\DataExtension;
2017-08-05 00:45:24 +02:00
2017-08-07 05:11:17 +02:00
class MyMemberExtension extends DataExtension
{
2014-10-22 12:47:56 +02:00
2017-08-07 05:11:17 +02:00
private static $db = [
'Position' => 'Varchar',
];
2014-10-22 12:47:56 +02:00
2017-08-07 05:11:17 +02:00
private static $has_one = [
'Image' => 'Image',
];
2014-10-22 12:47:56 +02:00
2017-08-07 05:11:17 +02:00
public function updateCMSFields(FieldList $fields)
{
$fields->push(new TextField('Position'));
$fields->push($upload = new UploadField('Image', 'Profile Image'));
$upload->setAllowedFileCategories('image/supported');
}
}
2017-08-03 05:35:09 +02:00
2017-08-03 02:51:32 +02:00
```
2014-10-22 12:47:56 +02:00
< div class = "notice" markdown = "1" >
If you're providing a module or working on code that may need to be extended by other code, it should provide a *hook*
which allows an Extension to modify the results.
< / div >
2017-08-03 02:51:32 +02:00
```php
2017-08-07 05:11:17 +02:00
public function Foo()
{
$foo = // ..
2014-10-22 12:47:56 +02:00
2017-08-07 05:11:17 +02:00
$this->extend('updateFoo', $foo);
2014-10-22 12:47:56 +02:00
2017-08-07 05:11:17 +02:00
return $foo;
}
2017-08-03 02:51:32 +02:00
```
2014-10-22 12:47:56 +02:00
The convention for extension hooks is to provide an `update{$Function}` hook at the end before you return the result. If
you need to provide extension hooks at the beginning of the method use `before{..}` .
## Owner
2017-07-03 03:22:12 +02:00
In your [Extension ](api:SilverStripe\Core\Extension ) class you can only refer to the source object through the `owner` property on the class as
2014-10-22 12:47:56 +02:00
`$this` will refer to your `Extension` instance.
2017-08-03 02:51:32 +02:00
```php
2017-08-07 05:11:17 +02:00
use SilverStripe\ORM\DataExtension;
2017-08-05 00:45:24 +02:00
2017-08-07 05:11:17 +02:00
class MyMemberExtension extends DataExtension
{
2014-10-22 12:47:56 +02:00
2017-08-07 05:11:17 +02:00
public function updateFoo($foo)
{
// outputs the original class
var_dump($this->owner);
}
}
2017-08-03 02:51:32 +02:00
```
2014-10-22 12:47:56 +02:00
## Checking to see if an Object has an Extension
2017-08-07 02:09:39 +02:00
To see what extensions are currently enabled on an object, use the [getExtensionInstances() ](api:SilverStripe\Core\Extensible::getExtensionInstances( )) and
[hasExtension() ](api:SilverStripe\Core\Extensible::hasExtension( )) methods of the [Extensible ](api:SilverStripe\Core\Extensible ) trait.
2017-08-03 02:51:32 +02:00
```php
2017-08-07 05:11:17 +02:00
$member = Security::getCurrentUser();
2014-10-22 12:47:56 +02:00
2017-08-07 05:11:17 +02:00
print_r($member->getExtensionInstances());
if($member->hasExtension('MyCustomMemberExtension')) {
// ..
}
2017-08-03 02:51:32 +02:00
```
2014-10-22 12:47:56 +02:00
2017-08-07 02:09:39 +02:00
## Extension injection points
2014-10-22 12:47:56 +02:00
2017-08-07 02:09:39 +02:00
`Extensible` has two additional methods, `beforeExtending` and `afterExtending` , each of which takes a method name and a
callback to be executed immediately before and after `extend()` is called on extensions.
2014-10-22 12:47:56 +02:00
This is useful in many cases where working with modules such as `Translatable` which operate on `DataObject` fields
that must exist in the `FieldList` at the time that `$this->extend('UpdateCMSFields')` is called.
< div class = "notice" markdown = '1' >
Please note that each callback is only ever called once, and then cleared, so multiple extensions to the same function
require that a callback is registered each time, if necessary.
< / div >
Example: A class that wants to control default values during object initialization. The code needs to assign a value
if not specified in `self::$defaults` , but before extensions have been called:
2017-08-03 02:51:32 +02:00
```php
2017-08-07 05:11:17 +02:00
function __construct() {
$self = $this;
2014-10-22 12:47:56 +02:00
2017-08-07 05:11:17 +02:00
$this->beforeExtending('populateDefaults', function() use ($self) {
if(empty($self->MyField)) {
$self->MyField = 'Value we want as a default if not specified in $defaults, but set before extensions';
}
});
2014-10-22 12:47:56 +02:00
2017-08-07 05:11:17 +02:00
parent::__construct();
}
2017-08-03 02:51:32 +02:00
```
2014-10-22 12:47:56 +02:00
Example 2: User code can intervene in the process of extending cms fields.
< div class = "notice" markdown = "1" >
This method is preferred to disabling, enabling, and calling field extensions manually.
< / div >
2017-08-03 02:51:32 +02:00
```php
2017-08-07 05:11:17 +02:00
public function getCMSFields()
{
$this->beforeUpdateCMSFields(function($fields) {
// Include field which must be present when updateCMSFields is called on extensions
$fields->addFieldToTab("Root.Main", new TextField('Detail', 'Details', null, 255));
});
$fields = parent::getCMSFields();
// ... additional fields here
return $fields;
}
2017-08-03 02:51:32 +02:00
```
2014-10-22 12:47:56 +02:00
## Related Documentaion
* [Injector ](injector/ )
## API Documentation
2017-07-03 03:22:12 +02:00
* [Extension ](api:SilverStripe\Core\Extension )
* [DataExtension ](api:SilverStripe\ORM\DataExtension )