# DataExtension ## Introduction Extensions allow for adding additional functionality to a `[api:DataObject]`. In some cases, it can be easier to completely replace the used class throughout the core with your custom implementation. Have a look at `[api:Object->useCustomClass()]`. ## Usage Your extension will need to be a subclass of `[api:DataExtension]` or the `[api:Extension]` class. :::php 'Varchar', ); private static $has_one = array( 'RelatedMember' => 'Member', ); } ### 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 public function getCMSFields() { // ... $this->extend('updateCMSFields', $fields); return $fields; } The `$`fields parameter is passed by reference, as it is an object. :::php public function updateCMSFields(FieldList $fields) { $fields->push(new TextField('Position', 'Position Title')); $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 initialisation. 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() uses ($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 data object. For example, `[api:Versioned]` adds version tracking and staging to any data object that it is applied to. To do this, you need to be able to create additional database tables and fields to keep your state stored in. To do this, define an **augmentDatabase()** method on your extension. This will be called when db/build is visited. * 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 is used 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(SQLQuery &$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:SQLQuery]` 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 class Customer extends DataObject { private static $has_one = array('Account'=>'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 ($this->owner->Account()->IsMarkedForDeletion == 1) ? true : false; } } ## API Documentation `[api:DataExtension]`