Skip to main content

This site requires you to update your browser. Your browsing experience maybe affected by not having the most up to date version.

 

SilverStripe Strips

In this guest post, Nick Spiel of Digital360 shares how they created a method of building pages with modular 'strips'.

Read post

Introducing SilverStripe Strips: a fast and scalable method to building modular webpages

Popular front-end frameworks like React and Vue have placed more emphasis the value of breaking down projects into components for reuse across the build. This is in line with a general trend that favours modular components or ‘patterns’ in web development.

Recently, my team and I have developed a new approach to building websites in SilverStripe. Instead of building out entire page templates, we have created a method of building pages with modular 'strips'.

Others in the SilverStripe community have been moving in a similar direction. Shae Dawson’s SilverStripe blocks module is one great, mature example. In this article, I’ll share how my team and I came to develop a similar solution.

There are a number of benefits to this strip process. These include:

  • Fast page customisation: Strips can be added, removed, updated and even re-ordered on pages independently of other pages. Structural changes to a page no longer have to go through development and can be completed by any CMS user (and this can be restricted to admins only).
  • Less repetition (DRY): Each strip is self-contained, so there is no need to copy across code to multiple layouts.
  • Time savings: Strips allow teams to allocate the task of ‘building’ pages to non-technical team members. This will give developers more time to focus on technical tasks.
  • Modularity: Each 'strip' will have its own [.ss, .php, .css] and perhaps even a [.js] file. These files can be copied into any project to avoid re-building common patterns.
  • Single source: Layouts, models and controllers are independent and have a single source for making changes. This means that managing elements is far simpler.
  • Reusable: Strips can be included on multiple pages across the site, making the administration of common content simple.

This process speeds up development time and makes our lives easy when publishing content or developing updates.

The traditional approach to building Layouts

When we started building sites in SilverStripe we adopted a very traditional approach: building out page templates in the [templates/Layout directory]. These pages were made up of a combination of includes and inline markup.

Here is an example of a typical page style in one of our builds:

Example: templates/Layout/ServicePage.ss
<% include HeroBanner %>
<section class="main-content">
...
{$Content}
...
</section>
<% include CallToAction %>
<% include EnquiryForm %>

Your process and templates may look similar to this.

While this method is tried and true, it can be time-consuming and tedious. You end up building out as many layouts as there are unique page styles for the project.

How can we improve on this process?

As we developed our client’s websites, we found many projects used similar elements across separate pages. These elements often appeared in a different order, too.

Consider the following scenario:

wireframe

These two pages are almost identical. The only difference is that the ‘content block’ and ‘call to action’ appear at different points on the page. Traditionally, we would have needed to create an entirely new page template to facilitate this change (and come up with a new name, too).

This is where the strip method becomes so valuable.

When building using strips, you craft the strip directly inside the CMS, duplicate them, reuse them, and reorder them. You can even nest them within each other.

Here is an example of the strips at work on a current live site:

strips gridfield

The strips are created and managed through a GridField which provides flexibility and some valuable functionality.

We have included the GridFieldExtensions module to add additional functions to the GridField. This supports the strip system in two ways: 

  • Managing multiple classes: The MultiClass component allows us to add different strip types to the page. This way each strip can have its own unique set of fields and templates.
  • Drag and drop reordering: The OrderableRows component provides the ability to drag and drop reorder the strips. 

Implementation

Page object: site/code/Page.php

First we setup a many_many relationship from Page to our Strip object. It is important that we make this relationship a many_many so that global or repeated strips can be included on multiple pages. We also add an extra field to the many_many relationship so we can control the order of the strips in the context of the relationship.

private static $many_many = [
'Strips' => 'Strip'
];

// Add a SortOrder field to the relationship to allow for reordering
private static $many_many_extraFields = [
'Strips' => [
'SortOrder' => 'Int'
]
];

Next, we set up the GridField. The GridField uses the multiclass editor plugin from the GridFieldExtensions.

// Set the columns to be displayed in the gridfield
$dataColumns = new GridFieldDataColumns();
$dataColumns->setDisplayFields(
    [
        'Title'         => 'Title',
        'singular_name' => 'Type'
    ]
);

// Get available strips from 'strips' folder as an array
$multiClassConfig = new GridFieldAddNewMultiClass();
$multiClassConfig->setClasses($this->AvailableStrips());

// Set up autocompleter to add existing strips
$autocompleter = new GridFieldAddExistingAutocompleter('toolbar-header-right', ['Title']);
$autocompleter->setResultsFormat('$Title ($ClassName)');

// Create the gridfield
$stripsGridField = GridField::create('Strips', 'Strips', $this->Strips(),
    GridFieldConfig::create()->addComponents(
        new GridFieldToolbarHeader(),
        new GridFieldDetailForm(),
        new GridFieldEditButton(),
        new GridFieldDeleteAction('unlinkrelation'),
        new GridFieldDeleteAction(),
        new GridFieldTitleHeader(),
        new GridFieldOrderableRows('SortOrder'),
        $autocompleter,
        $multiClassConfig,
        $dataColumns
    )
);

$fields->addFieldsToTab(
    'Root.Strips',
    [
        $stripsGridField
    ]
);

We then create a method for automatically populating the dropdown for the MultiClass editor. All php files under /site/code/strips will be returned as an array.

private function AvailableStrips()
{
// TODO Is there a better method for getting this folder?
    $stripFolder = BASE_PATH . '/site/code/strips';
    $strips      = [];

    foreach (new DirectoryIterator($stripFolder) as $file) {
        if ($file->isFile()) {
            $fileName  = $file->getFilename();
            $fileParts = pathinfo($fileName);
            // Only add PHP files
            if ($fileParts['extension'] == 'php') {
                array_push($strips, $fileParts['filename']);
            }
        }
    }
    // Sort strips alphabetically
    asort($strips);

    return $strips;
}
Strip object: site/code/Strip.php

Next, we set up a base class for our strips. I have commented inline with the purpose of each section.

code5

site/templates/Layout/Page.ss

Then we tie this all together with the following loop in your default (and only) page template.

1 <% loop $Strips.Sort(SortOrder) %> 
2 {$Me}
3 <% end_loop %>

This loop runs over all of the strips on the current page and through the use of {$Me} , then renders them using the forTemplate method we set on the base Strip class.

Once you have completed those steps, you can start extending this class with your custom

A Practical Example:

CallToActionStrip object: site/code/CallToActionStrip.php

Let’s take the example I used earlier with the ‘call to action’ strip and build it out using this process. The strip has the following elements:

  • An small image
  • A line of text
  • A button

So nothing special here. Just be sure to extend Strip.

code7 

CallToActionStrip template: site/templates/Includes/CallToActionStrip.ss
<section class="call-to-action-strip"> 
{$Image}
<p class="text">{$Text}</p>
<a href="{$PageLink.Link}" class="button">{$ButtonText}</a>
</section>

The implementation is complete! Finally, you can rid yourself of Page layouts almost entirely.

Final words

We have tried this method on our own website and three of our client builds. In short: it has revolutionised our approach to development. Our team can't see ourselves going back to the traditional approach. In fact, the content publishers and account managers are now complaining that they don’t have this flexibility on all of our client builds.

Some additional observations:

  • Code is more transportable between projects.
  • A/B testing was difficult to manage. We found that we had to wrap the entire page in a conditional block or build out an entirely new B version.
  • Client requests are no longer a hassle. In the past, if the client wanted to reorder the content we would need to go through the development team to make these changes.
  • Our CMS is now less cluttered. Before using strips, if we needed a similar page style built out we would often need to create a similar page style and change the portions that required it.
  • This is really just the bare essentials to working in a componentised manner in SilverStripe. If you are looking for a ready-to-rock repo I highly recommend the SilverStripe Blocks module.

For more information, please leave a comment or email me at nick.spiel@digital360.com.au or contact Digital360.

About the author
Nick Spiel

Nick Spiel is a Senior Front End Developer at Digital360,  a digital agency in Melbourne, Australia.

Post your comment

Comments

  • After doing a recent site in Divi I had been looking for a similar approach for Silverstripe.
    Divi is this idea taken to the next level.

    Posted by Dave, 06/10/2016 2:36pm (8 years ago)

  • We build a similar approach using Elements. See Elemental (https://github.com/dnadesign/silverstripe-elemental/) has a bunch of support for versioning, search, linking and v3 of elemental will start to see more client friendly functionality (like visual preview) and all the default elements cleaned up.

    Posted by Will, 06/10/2016 2:33pm (8 years ago)

  • @Michael - I only found the blocks module after the fact. I wrote this article and posted it in a SilverStripe slack channel, the guys in there pointed me to the blocks module. If I had have found this module first it would have saved me a lot of time. I updated the article to mention the blocks module after the hearing about it.

    ---

    @Giancarlo It would work for both.

    Anna could use the 'link existing' function provided my the gridfield.

    Bob could also use strips. He could approach it 2 ways:
    1. He could build out a strip with a boolean (GalleryLayout => boolean) value that defines the layout. He could then apply a class to the strip to change the style to present the photos in either a gallery or a slider format and inject required javascript conditionally. Depending on the design of the two elements, he would likely need different markup.

    2. Bob could wrap his template in a conditional if block and serve up a different template based on the boolean value.

    3. He could create 2 seperate strips (one with a gallery and one with the slider) and link them up to a global photos strip (or even just a data object that manages many images).

    Note that this could probably be done with the blocks module as well.

    ---

    @Vitaliy It has not been open sourced. I recommend the blocks module for a solid open platform. This tutorial was meant to just demonstrate the concept and how to roll your own.

    ---

    @Paul Clarke Strips made a lot of sense to us as we tend to design in this manner and was easy to convey to clients and other non dev or design people in our agency.

    Posted by Nick Spiel, 14/09/2016 1:17pm (8 years ago)

  • Hello Nick,

    Out of curiosity and as a contributor to the Blocks module; what made you still decide to implement similar/custom code yourself (any comments/improvements on the existing Blocks module)?

    I'd like to plug our Block-Enhancements module as a nice Blocks companion: https://github.com/restruct/silverstripe-block_enhancements

    Posted by Michael, 07/09/2016 3:14pm (8 years ago)

  • Thank you for sharing your techniques and code, Nick! I couldn't easily find the differences between Stripes and Blocks, what do you suggest based on these two use cases?

    1. Anna has her site divided in parts, each part with the same content and structure. She wants to create a promo hero once in a single place, so that it gets updated in sync automatically in every part of her website.

    2. Bob has his site divided in parts, each part with the same structure, but different content. For example, On a page he needs a photo gallery, on another page he needs a photo slider, on a third page he needs both. Each page has unique content, he wants to be able to pick parts from a library and configure them individually.

    Do you think these Anna and Bob can do that with Blocks, Strips or both?

    Posted by Giancarlo, 06/09/2016 2:19am (8 years ago)

  • perfect but, I can not find the module strips on https://github.com/ and on http://addons.silverstripe.org/add-ons?search=strips
    Can you get link ?

    Posted by Vitaliy, 05/09/2016 7:17pm (8 years ago)

  • I like the way you've called them Strips, I've been thinking of a similar naming convention which are similar to the component structure of Atomic/PatternLab. Instead calling them Stripes, Dashes, and so on.

    Posted by Paul Clarke, 05/09/2016 5:19pm (8 years ago)

RSS feed for comments on this page | RSS feed for all comments