PHPackages                             fromholdio/silverstripe-cms-fields-scaffolder - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. fromholdio/silverstripe-cms-fields-scaffolder

ActiveSilverstripe-vendormodule[Utility &amp; Helpers](/categories/utility)

fromholdio/silverstripe-cms-fields-scaffolder
=============================================

2.0.0(8mo ago)0439BSD-3-ClausePHP

Since May 2Pushed 8mo ago1 watchersCompare

[ Source](https://github.com/fromholdio/silverstripe-cms-fields-scaffolder)[ Packagist](https://packagist.org/packages/fromholdio/silverstripe-cms-fields-scaffolder)[ Docs](https://github.com/fromholdio/silverstripe-cms-fields-scaffolder)[ RSS](/packages/fromholdio-silverstripe-cms-fields-scaffolder/feed)WikiDiscussions master Synced 3w ago

READMEChangelog (5)Dependencies (1)Versions (7)Used By (0)

SilverStripe CMS Fields Scaffolder
==================================

[](#silverstripe-cms-fields-scaffolder)

A SilverStripe module for scaffolding CMS field tab structures and placement via YAML configuration. This module simplifies the process of organizing CMS fields into custom tab structures across different contexts (CMS edit forms, Settings forms, custom forms).

Features
--------

[](#features)

- **Section-based configuration**: Define different tab structures for different contexts (cms, settings, or custom sections)
- **Declarative tab structure**: Define your entire tab hierarchy in YAML
- **Automatic tab creation**: TabSets and Tabs are created automatically based on configuration
- **Field label integration**: Tab titles automatically use your DataObject's `field_labels` configuration
- **Content field relocation**: Move the default Content field to a custom tab location
- **Root.Main tab relocation**: Move fields from the default Root.Main tab to a custom location
- **Empty tab cleanup**: Automatically remove empty tabs and tabsets
- **Field removal**: Remove unwanted fields by name

Installation
------------

[](#installation)

```
composer require fromholdio/silverstripe-cms-fields-scaffolder
```

Requirements
------------

[](#requirements)

- SilverStripe ^4.1 || ^5.0 || ^6.0

Usage
-----

[](#usage)

### Basic Setup

[](#basic-setup)

1. **Apply the extension to your DataObject:**

```
Page:
  extensions:
    - Fromholdio\CMSFieldsScaffolder\CMSFieldsScaffolder
```

2. **Configure your tab structure:**

```
Page:
  scaffolder:
    cms:
      tabs:
        MainTabSet:
          - MainTab
          - DetailsTab
        ContentTabSet:
          - ContentMainTab
          - ContentBlocksTab
  field_labels:
    MainTabSet: Main
    MainTab: Main
    DetailsTab: Details
    ContentTabSet: Content
    ContentMainTab: Main
    ContentBlocksTab: Blocks
```

3. **Integrate into your getCMSFields() method:**

```
public function getCMSFields()
{
    $this->beforeUpdateCMSFields(function (FieldList $fields) {
        return $this->runCMSFieldsScaffolderBeforeUpdate($fields);
    });

    $this->afterUpdateCMSFields(function (FieldList $fields) {
        return $this->runCMSFieldsScaffolderAfterUpdate($fields, 'cms', false);
    });

    $fields = parent::getCMSFields();

    // Your custom field additions here

    $fields = $this->clearCMSFieldsScaffolderEmptyTabs($fields, 'cms');
    $fields = $this->removeCMSFieldsScaffolderFields($fields, 'cms');

    return $fields;
}
```

### Section Keys

[](#section-keys)

The module supports **section-based configuration**, allowing you to define different tab structures for different contexts. Common section keys include:

- `cms` - For the main CMS edit form (getCMSFields)
- `settings` - For the Settings tab (getSettingsFields)
- Custom keys - For any other form context you define

Each section can have its own independent configuration.

### Configuration Options

[](#configuration-options)

Each section supports the following configuration options:

#### `tabs`

[](#tabs)

Defines the tab structure. Can be a simple array of tab names, or a nested structure for TabSets:

```
Page:
  scaffolder:
    cms:
      tabs:
        # Simple TabSet with child Tabs
        MainTabSet:
          - MainTab
          - DetailsTab
        # Another TabSet
        ContentTabSet:
          - ContentMainTab
```

#### `do_clear_empty_tabs`

[](#do_clear_empty_tabs)

Boolean. When `true`, automatically removes empty tabs and tabsets after all fields have been added. Default: `true`

```
Page:
  scaffolder:
    cms:
      do_clear_empty_tabs: true
```

#### `content_field_tab_path`

[](#content_field_tab_path)

Specifies where to move the default `Content` field. Set to `false` to remove it entirely, or provide a tab path:

```
Page:
  scaffolder:
    cms:
      content_field_tab_path: 'Root.ContentTabSet.ContentMainTab'
      # Or to remove it:
      # content_field_tab_path: false
```

#### `root_main_tab_path`

[](#root_main_tab_path)

Specifies where to move fields from the default `Root.Main` tab:

```
Page:
  scaffolder:
    cms:
      root_main_tab_path: 'Root.MainTabSet.MainTab'
```

#### `fields_to_remove`

[](#fields_to_remove)

Array of field names to remove from the form:

```
Page:
  scaffolder:
    cms:
      fields_to_remove:
        - UnwantedField
        - AnotherField
```

### Complete Example

[](#complete-example)

Here's a complete example showing multiple sections:

```
Page:
  extensions:
    - Fromholdio\CMSFieldsScaffolder\CMSFieldsScaffolder
  scaffolder:
    cms:
      do_clear_empty_tabs: true
      content_field_tab_path: 'Root.ContentTabSet.ContentMainTab'
      root_main_tab_path: 'Root.MainTabSet.MainTab'
      tabs:
        MainTabSet:
          - MainTab
          - DetailsTab
        ContentTabSet:
          - ContentMainTab
          - ContentBlocksTab
        SEOTabSet:
          - SEOMainTab
    settings:
      do_clear_empty_tabs: true
      root_main_tab_path: 'Root.MainTabSet.MainTab'
      tabs:
        MainTabSet:
          - MainTab
        AccessTabSet:
          - AccessMainTab
  field_labels:
    MainTabSet: Main
    MainTab: Main
    DetailsTab: Details
    ContentTabSet: Content
    ContentMainTab: Main
    ContentBlocksTab: Blocks
    SEOTabSet: SEO
    SEOMainTab: SEO
    AccessTabSet: Access
    AccessMainTab: Main
```

### Integration Patterns

[](#integration-patterns)

#### For getCMSFields()

[](#for-getcmsfields)

```
public function getCMSFields()
{
    $this->beforeUpdateCMSFields(function (FieldList $fields) {
        return $this->runCMSFieldsScaffolderBeforeUpdate($fields);
        // Or with explicit section: $this->runCMSFieldsScaffolderBeforeUpdate($fields, 'cms');
    });

    $this->afterUpdateCMSFields(function (FieldList $fields) {
        return $this->runCMSFieldsScaffolderAfterUpdate($fields, 'cms', false);
    });

    $fields = parent::getCMSFields();

    // Add your custom fields here

    $fields = $this->clearCMSFieldsScaffolderEmptyTabs($fields, 'cms');
    $fields = $this->removeCMSFieldsScaffolderFields($fields, 'cms');

    return $fields;
}
```

#### For getSettingsFields()

[](#for-getsettingsfields)

```
public function getSettingsFields()
{
    $this->beforeExtending('updateSettingsFields', function (FieldList $fields) {
        return $this->runCMSFieldsScaffolderBeforeUpdate($fields, 'settings');
    });

    $this->afterExtending('updateSettingsFields', function (FieldList $fields) {
        return $this->runCMSFieldsScaffolderAfterUpdate($fields, 'settings', false);
    });

    $fields = parent::getSettingsFields();

    // Add your custom fields here

    $fields = $this->clearCMSFieldsScaffolderEmptyTabs($fields, 'settings');
    $fields = $this->removeCMSFieldsScaffolderFields($fields, 'settings');

    return $fields;
}
```

API Methods
-----------

[](#api-methods)

### `runCMSFieldsScaffolderBeforeUpdate(FieldList $fields, string $key = 'cms'): FieldList`

[](#runcmsfieldsscaffolderbeforeupdatefieldlist-fields-string-key--cms-fieldlist)

Runs before extensions update fields. Creates tabs and relocates the Content field.

### `runCMSFieldsScaffolderAfterUpdate(FieldList $fields, string $key = 'cms', bool $doClearTabs = true): FieldList`

[](#runcmsfieldsscaffolderafterupdatefieldlist-fields-string-key--cms-bool-docleartabs--true-fieldlist)

Runs after extensions update fields. Relocates Root.Main tab fields and optionally clears empty tabs.

### `clearCMSFieldsScaffolderEmptyTabs(FieldList $fields, string $key = 'cms'): FieldList`

[](#clearcmsfieldsscaffolderemptytabsfieldlist-fields-string-key--cms-fieldlist)

Removes empty tabs and tabsets based on the section's `do_clear_empty_tabs` setting.

### `removeCMSFieldsScaffolderFields(FieldList $fields, string $key = 'cms'): FieldList`

[](#removecmsfieldsscaffolderfieldsfieldlist-fields-string-key--cms-fieldlist)

Removes fields specified in the section's `fields_to_remove` configuration.

### `getCMSFieldsScaffolderSetting(string $name, string $key = 'cms')`

[](#getcmsfieldsscaffoldersettingstring-name-string-key--cms)

Retrieves a specific configuration setting for a section.

### `applyCMSFieldsScaffolderTabs(FieldList $fields, string $key = 'cms'): FieldList`

[](#applycmsfieldsscaffoldertabsfieldlist-fields-string-key--cms-fieldlist)

Creates the tab structure defined in the section's `tabs` configuration.

### `applyCMSFieldsScaffolderContentFieldTabPath(FieldList $fields, string $key = 'cms'): FieldList`

[](#applycmsfieldsscaffoldercontentfieldtabpathfieldlist-fields-string-key--cms-fieldlist)

Relocates or removes the Content field based on the section's `content_field_tab_path` configuration.

### `applyCMSFieldsScaffolderRootMainTabPath(FieldList $fields, string $key = 'cms'): FieldList`

[](#applycmsfieldsscaffolderrootmaintabpathfieldlist-fields-string-key--cms-fieldlist)

Relocates fields from Root.Main (or Root.Settings) to the path specified in `root_main_tab_path`.

Version History &amp; Breaking Changes
--------------------------------------

[](#version-history--breaking-changes)

### Version 2.x (Current - With Section Keys)

[](#version-2x-current---with-section-keys)

The current version uses **section-based configuration** where each context (cms, settings, etc.) has its own configuration block.

**Configuration structure:**

```
Page:
  scaffolder:
    cms:                    # Section key
      tabs: ...
      do_clear_empty_tabs: true
    settings:               # Another section key
      tabs: ...
```

**Method signatures:**

```
runCMSFieldsScaffolderBeforeUpdate(FieldList $fields, string $key = 'cms'): FieldList
runCMSFieldsScaffolderAfterUpdate(FieldList $fields, string $key = 'cms', bool $doClearTabs = true): FieldList
clearCMSFieldsScaffolderEmptyTabs(FieldList $fields, string $key = 'cms'): FieldList
removeCMSFieldsScaffolderFields(FieldList $fields, string $key = 'cms'): FieldList
getCMSFieldsScaffolderSetting(string $name, string $key = 'cms')
```

**Default section configurations:**

```
private static $scaffolder = [
    'cms' => [
        'tabs' => null,
        'do_clear_empty_tabs' => true,
        'content_field_tab_path' => 'Root.ContentTabSet.ContentMainTab',
        'root_main_tab_path' => 'Root.MainTabSet.MainTab',
        'fields_to_remove' => null,
    ],
    'settings' => [
        'tabs' => null,
        'do_clear_empty_tabs' => true,
        'content_field_tab_path' => 'Root.ContentTabSet.ContentMainTab',
        'root_main_tab_path' => 'Root.MainTabSet.MainTab',
        'fields_to_remove' => null,
    ],
];
```

### Version 1.x (Legacy - Without Section Keys)

[](#version-1x-legacy---without-section-keys)

The legacy version used a **flat configuration structure** without section keys.

**Configuration structure:**

```
Page:
  scaffolder:
    tabs: ...              # Direct configuration, no section key
    do_clear_empty_tabs: true
```

**Method signatures:**

```
runCMSFieldsScaffolderBeforeUpdate(FieldList $fields): FieldList
runCMSFieldsScaffolderAfterUpdate(FieldList $fields): FieldList
clearCMSFieldsScaffolderEmptyTabs(FieldList $fields): FieldList
removeCMSFieldsScaffolderFields(FieldList $fields): FieldList
getCMSFieldsScaffolderSetting(string $key)
```

**Default configuration:**

```
private static $scaffolder = [
    'tabs' => null,
    'do_clear_empty_tabs' => true,
    'content_field_tab_path' => 'Root.ContentTabSet.ContentMainTab',
    'root_main_tab_path' => 'Root.MainTabSet.MainTab',
    'fields_to_remove' => null
];
```

### Migrating from 1.x to 2.x

[](#migrating-from-1x-to-2x)

If you're upgrading from version 1.x, you'll need to:

1. **Update your YAML configuration** to nest settings under section keys:

```
# Old (1.x)
Page:
  scaffolder:
    tabs:
      MainTabSet:
        - MainTab

# New (2.x)
Page:
  scaffolder:
    cms:                    # Add section key
      tabs:
        MainTabSet:
          - MainTab
```

2. **Update method calls** to include the section parameter:

```
// Old (1.x)
$this->runCMSFieldsScaffolderBeforeUpdate($fields);
$this->runCMSFieldsScaffolderAfterUpdate($fields);
$this->clearCMSFieldsScaffolderEmptyTabs($fields);

// New (2.x)
$this->runCMSFieldsScaffolderBeforeUpdate($fields, 'cms');
$this->runCMSFieldsScaffolderAfterUpdate($fields, 'cms', false);
$this->clearCMSFieldsScaffolderEmptyTabs($fields, 'cms');
```

Note: The section parameter defaults to `'cms'`, so you can omit it for CMS forms if desired.

License
-------

[](#license)

BSD-3-Clause

Maintainer
----------

[](#maintainer)

Luke Fromhold -

###  Health Score

36

—

LowBetter than 79% of packages

Maintenance59

Moderate activity, may be stable

Popularity15

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~251 days

Recently: every ~219 days

Total

6

Last Release

261d ago

Major Versions

1.x-dev → 2.0.02025-10-08

### Community

Maintainers

![](https://www.gravatar.com/avatar/40e135ad117686bee39707c1d9286cc5e915e219c26a10d13858ca44d14f1eb0?d=identicon)[dizzystuff](/maintainers/dizzystuff)

---

Top Contributors

[![dizzystuff](https://avatars.githubusercontent.com/u/576903?v=4)](https://github.com/dizzystuff "dizzystuff (7 commits)")

---

Tags

silverstripe

### Embed Badge

![Health badge](/badges/fromholdio-silverstripe-cms-fields-scaffolder/health.svg)

```
[![Health](https://phpackages.com/badges/fromholdio-silverstripe-cms-fields-scaffolder/health.svg)](https://phpackages.com/packages/fromholdio-silverstripe-cms-fields-scaffolder)
```

###  Alternatives

[silverstripe/userforms

UserForms enables CMS users to create dynamic forms via a drag and drop interface and without getting involved in any PHP code

1321.1M83](/packages/silverstripe-userforms)[symbiote/silverstripe-gridfieldextensions

A collection of useful grid field components

951.9M264](/packages/symbiote-silverstripe-gridfieldextensions)[silverstripe/tagfield

Tag field for SilverStripe

561.3M50](/packages/silverstripe-tagfield)[jonom/silverstripe-betternavigator

Front-end utility menu for Silverstripe websites featuring administration and development tools

59429.0k12](/packages/jonom-silverstripe-betternavigator)[symbiote/silverstripe-advancedworkflow

Adds configurable workflow support to the CMS, with a GUI for creating custom workflow definitions.

46299.9k9](/packages/symbiote-silverstripe-advancedworkflow)[lekoala/silverstripe-cms-actions

Add actions to your models in SilverStripe

39317.0k29](/packages/lekoala-silverstripe-cms-actions)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
