1. Config::modify()->set(
  2. Injector::class,
  3. Security::class,
  4. [
  5. 'properties' => [
  6. 'Authenticators' => []
  7. ]
  8. ]
  9. );

this doesn't work


because it needs to happen only in certain conditions that I can't define in YML

  1. SilverStripe\Core\Injector\Injector:
  2. SilverStripe\Security\Security:
  3. properties:
  4. Authenticators:
  5. default: %$MyVendor\MyProject\Authenticator\MyAuthenticator

does anyone knows how to do this with PHP in _config.php


The topic is suggesting I can make a perormance improvement but it doesn't tell me how


Show 1 attachment(s)

# Elemental - advanced setup

This documentation assumes that the reader is already familiar with basic concepts
of the [Elemental module](<>) and the [Fluent module](<>).
This document provides an advanced setup guide for enterprise scale projects using these modules.

## Elemental setup

### Page setup

It's a good idea to create a `BlockPage` class to represent a page with blocks (i.e. avoid adding blocks directly to the `Page`).
This allows more flexibility as other page types can subclass `Page` without inheriting any blocks related functionality.
This is useful for covering edge cases that may appear during projects (i.e. not all pages may need blocks).

class BlockPage extends Page { /**

  • @var array */ private static $extensions = [ ElementalPageExtension::class, ];

Block setup

It is possible to share blocks between pages, but this may be a little bit tricky when it comes to content editing. Block should represent a chunk of page content, so editing it should not effect other pages. This depends on the project, but in most cases the content authors will be working per page (top down), so sharing blocks is probably something to avoid. Shared blocks become even less useful when large number of block instances are present as it becomes almost impossible to find the right one.

The overall recommendation is to only allow a content block to be used on only one page. The main benefit of using blocks is to reuse patterns and functionality across pages, not necessarily content data. It's possible to add functionality which allows content authors to copy specific blocks to other pages for a quick transfer of content data.

Elemental editor GridField needs to be adjusted accordingly:

 * Apply strong inheritance relation config
 * no existing auto complete as reusing items is not allowed
 * no unlink button for the same reason
 * @param GridFieldConfig $config
 * @return GridFieldConfig
public static function strongInheritanceConfig(GridFieldConfig $config): GridFieldConfig
        // remove add existing button since this is a full ownership relation
        // remove archive action because nested objects are expected to be publish / un-publish via page

    /** @var GridFieldDeleteAction $deleteAction */
    $deleteAction = $config-&gt;getComponentByType(GridFieldDeleteAction::class);

    if ($deleteAction !== null) {
        // replace unlink relation button with delete button since this is a full ownership relation

    return $config;

Note that we also want to remove publish and archive actions from blocks. These actions will be done only on page level and will cascade down to blocks. Make sure to properly configure your objects with the owns and cascade_deletes.

Elemental with Fluent setup

Fluent module provides multiple options how to localise your content, but there is one option which is the best on average: indirect localisation. The only thing that will be localised directly is the ElementalArearelation.

class BlockPage extends Page

     * @var array
    private static $field_include = [

This configuration allows us to have different ElementalArea for different locales of the page. We also need to create a copy of the ElementalArea when content is being localised.

class BlockPage extends Page

    public function onBeforeWrite()

        if (!$this-&gt;isDraftedInLocale() &amp;&amp; $this-&gt;isInDB()) {
            $elementalArea = $this-&gt;ElementalArea();

            $elementalAreaNew = $elementalArea-&gt;duplicate();
            $this-&gt;ElementalAreaID = $elementalAreaNew-&gt;ID;


Note that it's important to have the cascade_duplicates setting present on all the relevant objects so they would copy as well.

Furthermore, we also need to disable the inheritance for blocks. The Fluent module provides multiple extension points, one of them being the updateLocaliseSelect. We need to create an Extension with the following code and apply it to the BlockPage:

class BlockPageFluentExtension extends Extension
     * Override default Fluent fallback
     * @param string $query
     * @param string $table
     * @param string $field
     * @param Locale $locale
    public function updateLocaliseSelect(&amp;$query, $table, $field, Locale $locale)
        // disallow elemental data inheritance in the case that published localised page instance already exists
        if ($field == 'ElementalAreaID' &amp;&amp; $this-&gt;owner-&gt;isPublishedInLocale()) {
            $query = '"' . $table . '_Localised_' . $locale-&gt;getLocale() . '"."' . $field . '"';
class BlockPage extends Page

     * @var array
    private static $extensions = [

Benefits of indirect localisation

  • different localisation of pages can have completely different set of blocks which allows greater flexibility
  • localisation is only on page level, so any functionality on block level does not need to care about localisation
  • this is especially useful when writing unit tests as it is significantly easier to set up tests without localisation

Downsides of indirect localisation

  • the blocks are unaware of their locales which makes bottom up relation lookup slower
  • this can be remedied by some extra data stored in blocks (see notes below)

Block performance increase

If only one page can own a block, we can store page reference directly on the block. This can be done when block is created (onBeforeWrite). This is helps significantly when traversing relation from block up to a page. Note that there may be a lot of objects sitting between block and a page. For example Content block -&gt; Elemental area -&gt; Layout block -&gt; Elemental area -&gt; Page.

Additional information can be stored like page locale (relevant for Fluent module) to specify the target data even further.

Unit tests

Writing unit tests for Elemental with Fluent can be rather tricky to figure out. Here are some guidelines to make that easier.

Make sure your fixture has some locales setup

It's important to include some locales because otherwise your test might be testing a very different situation. Example Locale setup in a fixture:

    Locale: en_NZ
    Title: 'English (New Zealand)'
    URLSegment: newzealand
    Locale: en_US
    Title: 'English (US)'
    URLSegment: usa
      - =&gt;TractorCow\Fluent\Model\<|>

Localised fixture data (automatic, single locale)

In the case your fixture needs to contain data for only a single locale you can specify your desired locale in your unit test like this:

protected function setUp()
    // Set locale for fixture creation
    FluentState::singleton()-&gt;withState(function (FluentState $state) {

This will localise all your data so you don't need to worry about that in your fixtures. The following fixture will produce a page localised in en_NZ:

    Title: ArticlePage1 NZ
    URLSegment: article-page1

Localised fixture data (manual, single or multiple locales)

In some cases you want to have multiple locales o…

Hide attachment content

like having title and intro editable inline + link to detail for other (gridfields for relations)


I just upgraded elemental from 3 -> 4... am I doing something wrong or is there no detail view anymore (only inline)?