> Silverstripe Attributable Module: Smarter Internal Linking for Better Engagement and SEO
- Publication
- Author
- Florian Thoma
- Categories
- Reading time
- 2 minutes
Modern websites often contain diverse content types—case studies, blog posts, product pages, services—and making meaningful connections between them isn’t just helpful for users, it’s critical for SEO and content discoverability.
Imagine this:
- A blog post promoting a service links directly to related case studies and products.
- A product page highlights relevant blog posts and industries.
- A case study references the exact services or products involved.
Enter Attributable
To simplify these relationships, we've developed the fromholdio/silverstripe-attributable module. It enables you to define any DataObject as an attribute, and any other (or the same) DataObject as attributable.
Yes, this means you can even relate objects to their own type. For instance, a product can have “related products” using this module. It's versatile, elegant, and a huge step forward for structured content relationships.
How it works
On the attribute side, you add the Attribute class to every class that can be used as an attribute:
use Fromholdio\Attributable\Extensions\Attribute;class Industry extends DataObject{...private static $extensions = [Attribute::class,];...}
And on the “attributable” side, you add the Attributable extension and define which attribute types it accepts. For example:
use Fromholdio\Attributable\Extensions\Attributable;use Fromholdio\Attributable\Extensions\Attribute;class ProductPage extends Page{...private static $extensions = [Attributable::class,];private static $allowed_attributes = [Industry::class,Market::class,Type::class,];...}
On the ProductPage, you can configure where the attribute fields will be inserted:
private static $attributes_tab_path = 'Root.Main'; And that adds tag fields for the allowed attributes:

Attribute fields on the receiving class
Now you can retrieve all Industries attributed to a ProductPage like this:
class ProductPage extends Page{...public function getIndustries(){return $this->getAttributes(Industry::class);}...}
Simple, yet powerful.
Smart Filtering
With these relationships in place, you can easily filter for related content. Want a list of all products tied to a particular industry? Easy:
public function getProductsForIndustry(int $industryID): ?DataList{return Attribution::get_related_objects(Industry::class,$industryID,ProductPage::class,);}
Looking for blog posts using the same attributes as the current product page? Done:
public function getRelatedBlogPosts(): ?DataList{// get attributions of current page// and get a list of blog post by the combined attribute keys$attributions = Attribution::get()->filter(['ObjectClass' => $this->getClassName(),'ObjectID' => $this->ID,])->columnUnique('AttributeKey');// get blog posts based on attributionsreturn BlogPost::get()->filterAny(['Attributions.AttributeKey' => $attributions]);}
SEO Benefits
These internal connections aren’t just great for user engagement, they’re gold for SEO. Automatically linking content across types ensures search engines understand your site’s architecture better, boosting crawlability and relevance.
For example, when a blog post is related to a product or project, the module can auto-generate internal links to those pages, reinforcing thematic connections and content authority.
Editorial and Structural Benefits
This module streamlines editorial workflows by allowing content creators to surface relevant material without manually inserting links. It also supports growing projects, making it easy to extend relationships as your content ecosystem grows.
Get Started
Ready to make your Silverstripe site smarter and more connected? Explore the module on GitHub, try it out, and start building richer, more discoverable content relationships.
If you’d like to discuss implementation, customisation, or how this could support your specific content strategy, contact us. We’d love to hear from you.