# DataExtension
## Introduction
Extensions allow for adding additional functionality to a `[api:DataObject]` or
modifying existing functionality without the hassle of creating a subclass.
## Usage
Extensions are defined as subclasses of either `[api:DataExtension]` for
extending a `[api:DataObject]` subclass or the `[api:Extension]` class for non
DataObject subclasses (such as Controllers)
:::php
extend()]` method.
:::php
public function myFunc() {
$foo = // ..
$this->extend('alterFoo', $foo);
return $foo;
}
In this example, the myFunc() method adds a hook to allow `DataExtension`
subclasses added to the instance to define an `alterFoo($foo)` method to modify
the result of the method.
The `$foo` parameter is passed by reference, as it is an object.
### Accessing the original Object from an Extension
In your extension class you can refer to the source object through the `owner`
property on the class.
:::php
owner);
}
}
### Checking to see if an Object has an Extension
To see what extensions are currently enabled on an object, you can use
`[api:Object->getExtensionInstances()]` and `[api:Object->hasExtension($extension)]`.
## Implementation
### Adding extra 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 static variables on load.
The function should return a map where the keys are the names of the static
variables to update:
:::php
'Varchar',
);
private static $has_one = array(
'Image' => 'Image',
);
}
### Modifying CMS Fields
The member class demonstrates an extension that allows you to update the default
CMS fields for an object in an extension:
:::php
'Varchar',
);
private static $has_one = array(
'Image' => 'Image',
);
public function updateCMSFields(FieldList $fields) {
$fields->push(new TextField('Position'));
$fields->push(new UploadField('Image', 'Profile Image'));
}
}
### Adding/modifying fields prior to extensions
User code can intervene in the process of extending cms fields by using
`beforeUpdateCMSFields` in its implementation of `getCMSFields`. This can be
useful in cases where user code will add fields to a dataobject that should be
present in the `$fields` parameter when passed to `updateCMSFields` in
extensions.
This method is preferred to disabling, enabling, and calling cms field
extensions manually.
:::php
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;
}
### Object extension injection points
`Object` now has two additional methods, `beforeExtending` and `afterExtending`,
each of which takes a method name and a callback to be executed immediately
before and after `Object::extend()` is called on extensions.
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.
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.
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:
:::php
function __construct() {
$self = $this;
$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';
}
});
parent::__construct();
}
### Custom database generation
Some extensions are designed to transparently add more sophisticated
data-collection capabilities to your `DataObject`. For example, `[api:Versioned]`
adds version tracking and staging to any `DataObject` that it is applied to.
To do this, define an **augmentDatabase()** method on your extension. This will
be called when the database is rebuilt.
* You can query `$this->owner` for information about the data object, such as
the fields it has
* You can use **DB::requireTable($tableName, $fieldList, $indexList)** to set
up your new tables. This function takes care of creating, modifying, or leaving
tables as required, based on your desired schema.
### Custom write queries
If you have customised the generated database, then you probably want to change
the way that writes happen. This isused by `[api:Versioned]` to get an entry
written in ClassName_versions whenever an insert/update happens.
To do this, define the **augmentWrite(&$manipulation)** method. This method is
passed a manipulation array representing the write about to happen, and is able
to amend this as desired, since it is passed by reference.
### Custom relation queries
The other queries that you will want to customise are the selection queries,
called by get & get_one. For example, the Versioned object has code to redirect
every request to ClassName_live, if you are browsing the live site.
To do this, define the **augmentSQL(SQLSelect $query)** method. Again, the $query object is passed by reference and can
be modified as needed by your method. Instead of a manipulation array, we have a `[api:SQLSelect]` object.
### Additional methods
The other thing you may want to do with a extension is provide a method that can
be called on the `[api:DataObject]` that is being extended. For instance, you
may add a publish() method to every `[api:DataObject]` that is extended with
`[api:Versioned]`.
This is as simple as defining a method called publish() on your extension. Bear
in mind, however, that instead of $this, you should be referring to
`$this->owner`.
* $this = The `[api:DataExtension]` object.
* $this->owner = The related `[api:DataObject]` object.
If you want to add your own internal properties, you can add this to the
`[api:DataExtension]`, and these will be referred to as `$this->propertyName`.
Every `[api:DataObject]` has an associated `[api:DataExtension]` instance for
each class that it is extended by.
:::php
'Account'
);
private static $extensions = array(
'CustomerWorkflow'
);
}
class Account extends DataObject {
private static $db = array(
'IsMarkedForDeletion'=>'Boolean'
);
private static $has_many = array(
'Customers' => 'Customer'
);
}
class CustomerWorkflow extends DataExtension {
public function IsMarkedForDeletion() {
return (bool) $this->owner->Account()->IsMarkedForDeletion;
}
}
## API Documentation
* `[api:Extension]`
* `[api:DataExtension]`
## See Also
* [Injector](injector/)
* `[api:Object::useCustomClass]`