Compose your SilverStripe content of Elements / Widgets
PHP Scheme CSS
Latest commit 9b4aae8 Jan 30, 2017 @wilr wilr committed on GitHub Merge pull request #66 from SilbinaryWolf/feat-anchor
feat(BaseElement): Add 'getAnchor' function
Failed to load latest commit information.

SilverStripe Elemental

Build Status Version License


This module extends a page type to swap the content area for a GridField and manageable elements (widgets) to compose a page out of rather than a single text field. Features supported:

  • Versioning of elements
  • Ability to add, remove supported elements per page.

The module provides basic markup for each of the widgets but you will likely need to provide your own styles. Replace the $Content variable with $ElementArea and rely on the markup of the individual widgets.


composer require "dnadesign/silverstripe-elemental" "dev-master"

Extend any page type with the ElementPageExtension and define allowed elements. This can be done via the SilverStripe YAML config API.


    - ElementPageExtension

In your page type template use $ElementArea to render the elements to the page.


Customize HTML and Markup

The basic element area is rendered into the standard WidgetArea template. This loops over each of the widget controller instances. Each controller instance will render $WidgetHolder which represented the widget contained within a holder div. The wrapper div is the template.

Limit Allowed Elements

You may wish to only enable certain elements for the CMS authors to choose from rather than the full set.

        - 'ElementContent'

Likewise, you can exclude certain elements from being used.

        - 'ElementContact'

By default, an Element List can contain nested Elements. To set allowed elements in list use the allowed_elements flag. The disallowed_elements configuration flag works here too.

        'ElementFile' : 'File'

Extra CSS classes can be configure in the YAML config file. By default, the Image element comes with 3 optional classes:

        - 'image_large' : 'Large'
        - 'image_medium' : 'Normal'
        - 'image_small' : 'Small'

Defining your own elements.

An element is as simple as a class which extends BaseElement. After you add the class, ensure you have rebuilt your database and reload the CMS.


class MyElement extends BaseElement {

    private static $title = "My Element";

    private static $description = "My Custom Element";

    public function getCMSFields() {
        $fields = parent::getCMSFields();

        // ...

        return $fields;

MyElement will be rendered into a template with the wrapper.

Implementing search

Composing your page of elements means that searching the page for content will require you to add some additional logic as SilverStripe would normally expect content to live in a single Content field. By default, elemental will copy all elements into the $Content field on save so search works as designed however this has one limitation, if you share elements between pages, publishing on page A will not update the searched content on page B.

To get around this, we can use the ElementalSolrIndexer class (given you're using the FulltextSearchable module). This class can help add some smarts to the Solr indexing so that it understands our content.

First step is to define a custom Solr index


class CustomSolrSearchIndex extends SolrSearchIndex {

    public function getFieldDefinitions() {
        $xml = parent::getFieldDefinitions();

        // adds any required XML configuration
        $indexer = new ElementalSolrIndexer();
        $xml = $indexer->updateFieldDefinition($xml);

        return $xml;

    protected function _addAs($object, $base, $options)
        // boiler plate from parent::_addAd since we can't
        // call the parent function to modify this document.

        $includeSubs = $options['include_children'];

        $doc = new Apache_Solr_Document();
        $doc->setField('_documentid', $this->getDocumentID($object, $base, $includeSubs));
        $doc->setField('ID', $object->ID);
        $doc->setField('ClassName', $object->ClassName);

        foreach (SearchIntrospection::hierarchy(get_class($object), false) as $class) {
            $doc->addField('ClassHierarchy', $class);

        foreach ($this->getFieldsIterator() as $name => $field) {
            if ($field['base'] == $base) {
                $this->_addField($doc, $object, $field);

        // custom code for adding in the Elemental smarts
        $indexer = new ElementalSolrIndexer();
        $indexer->elementPageChanged($object, $doc);

        try {
        } catch (Exception $e) {
            SS_Log::log($e, SS_Log::WARN);
            return false;

        return $doc;

After setting up your SolrSearchIndex, run `sake dev/tasks/Solr_Configure`.





CMS Icon blocks by Creative Stall from the Noun Project