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.

 

Superglue Your Pages

Do you ever find yourself writing increasingly complex code, just to show interesting content on your home page? We've got the solution for you: Superglue module.

Read post

Do you ever find yourself writing increasingly complex code, just to show interesting content on your home page? I'm talking about the kind of code that's hard to test and even harder to figure out?

class HomePageController extends Controller
{
    public function getProducts()
    {
        $stores = StorePage::get();
        $departments = new ArrayList();
        
        foreach ($stores as $store) {
            $departments->merge(
                $store->Departments()->column("ID")
            );
        }
        
        $departments = Department::get()->byIDs($departments);
        $products = new ArrayList();
        
        foreach ($departments as $department) {
            $products->merge(
                $department->Products()->column("ID")
            );
        }
        
        return Product::get()->byIDs($products)
            ->sort("Price")
            ->limit(5);
    }
}

Unless the pages you're looking to display are children of the page on which you're trying to display them, things quickly fall apart. The NZ Transport Agency recently needed to do something similar to this for their website, and a team at SilverStripe decided to tackle this problem, and produce a reusable solution.

You can find the code for this module on Github.

Getting Things Ready

The team developed a module you can install via:

$ composer require silverstripe/superglue

The first step to using it is to define an interface for things we want to glue to landing pages:

use SilverStripe\SuperGlue\Connector;
  
class ProductsConnector implements Connector
{
    public function getTitle()
    {
        return "Home Page → Product Pages";
    }
  
    public function getTemplate(SiteTree $page)
    {
        return "HomePageProductPages";
    }
  
    public function getDataList(SiteTree $page)
    {
        return ProductPage::get()
	    ->filter("Department.ParentID", $page->ID);
    }
}

You can define any number of these connectors, defining relationships between different kinds of landing pages and the sub-pages they display.

For each, you should enable extensions on the pages. Pages that display data get one extension, while pages that are displayed get another:

HomePage:
  extensions:
    - SilverStripe\SuperGlue\PageExtension
HomePage_Controller:
  extensions:
    - SilverStripe\SuperGlue\PageControllerExtension
ProductPage:
  extensions:
    - SilverStripe\SuperGlue\SubPageExtension

Finally, you need to define the template for the included pages and add a special template variable to the home page template:

In HomePage.ss

<% if $SuperGlueConnector %>
    $SuperGlueView
<% end_if %>

In HomePageProductPages.ss

<% with $Parent %>
    <% if $SuperGlueViewSubPages %>
        <% loop $SuperGlueViewSubPages.Limit(5) %>
            <div class="product">$Title → $Price</div>
        <% end_loop %>
    <% end_if %>
<% end_with %>

These files create the framework for displaying sub-pages on your special landing page types. They're only half the equation though...

Switching Connectors

Once you add the PageExtension and SubpageExtension classes to the pages you're trying to connect, you will see a few new fields:

superglue settings

Those "connected items" fields were added by the Superglue module. The new Connector class we created should be visible in the dropdown, so we can select it.

If you don't see your Connector class, you may need to run dev/build?flush=1.

Once we select it, and save the page, a new CMS tab will appear:

superglue connected pages

With this list, we can decide the order of pages we want to display. As new pages are created, they are added to the top of the "Normal Pages" list. We can drag and reorder them. We can also pin pages to the top of our page. Pinned items are displayed first in the template we created.

We'll only be able to pin sub-pages until we reach the limit in settings. After that, the "pin" buttons will disappear.

We can also customise the columns displayed in the CMS grid field, by adding a method to the Connector:

public function getGridFieldDisplayFields(SiteTree $page)
{
    return [
        "Title" => "Product name",
        "Price" => "Product price",
    ];
}

Loading More

The Superglue module was designed to work well with ajax pagination, so you can create infinitely-scrolling landing pages easily. We can use a special link, in our controllers:

<% with $Parent %>
    <button 
        class="load-more"
        data-page-limit="$SuperGluePageLimit"
        data-first-page-limit="$SuperGlueFirstPageLimit"
        data-load-more-link="$LoadMoreLink"
    >
        Load more
    </button>
<% end_with %>

Ajax requests to this URL will return a JSON response resembling:

{
    "total": 123,
    "limit": 9,
    "start": 16,
    "next": "special-landing-page\/LoadMore?start=16",
    "items: [...]
}

We might also want to alter the sub-page data, before it gets to the browser. We can do this by adding another method to the Connector:

public function getPageArray(SiteTree $page)
{
    $date = DBField::create_field(
        "SS_Datetime", $page->Created
    );
    
    return array(
        "link" => $page->Link(),
        "title" => $page->Title,
        "price" => $page->Price,
        "created" => date->Format("j M Y"),
    );
}

The templates we created will always have access to the DataList returned by getDataList. getPageArray is just for the ajax load more endpoint.

Wrapping up

This module is useful in terms of the amount of code it saves us from creating, testing and maintaining. It goes really well with a library like Isotope, or a Masonry layout.

We'd like to thank NZ Transport Agency for letting us be creative and contribute to open source.

If you'd like to know more about this module, or want to suggest improvements, let us know in the comments below!

Header photo by Chris Isherwood

About the author
Christopher Pitt

Chris works in the bespoke team, at SilverStripe. He helps build and maintain many of the modules used in SilverStripe applications. He also teaches; speaking at local user groups and international conferences, and through technical blog posts and books.

He also enjoys building robots and connecting those to circuits inside Minecraft. When the machines take over, he hopes this will buy him some time to formulate a plan of escape.

Post your comment

Comments

No one has commented on this page yet.

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