Skip to main content
Mar 08, 2018

It is really great relief if you have many blog posts and don't need to take care of related articles for each of them in SilverStripe CMS.

Here is how to get relevant or similar articles / posts in SilverStripe automatically, based on meta keywords or tags. And not only that the logic returns related articles but it also returns them by relevance! For example, if you visit funny article about jobs I dream of, you can notice that the first result has in common more than one tags and all below have only one.

Note: The code is not tested in v3.x.

How to get similar posts in SilverStripe

Let's assume that we have a class for blog post page BlogPostPage.

We need to do two things:

1) Crate a function in page's controller

2) Call the method in a template

Controller

Copy and paste the following method/function into your page's controller

/**
* Function returns articles similar to the current one based on MetaKeywords.
* Articles are got by relevance.
* 
*/
function RelatedArticles() {
  $array_tags = explode(",", $this->MetaKeywords); // Split tags into array
  $union_query = "";
   
  // Pass through all tags and create union part of the sql query
  for($i = 0; $i < count($array_tags); $i++ ) {
     
    if($i > 0) {
      $union_query .= " UNION ALL ";
    }
     
    $union_query .= " SELECT ID FROM `SiteTree_Live` ";
    $union_query .= " where ID <> $this->ID ";
    $union_query .= " and ClassName = '".$this->dataRecord->ClassName."' "; // e.g. BlogPostPage
    $union_query .= " and Locale = '".$this->dataRecord->Locale."' "; // e.g. en_US, sr_RS
    $union_query .= " and MetaKeywords like '%".trim($array_tags[$i])."%' ";
  }
   
  // Create sql query
  $query = " SELECT ID, count(ID) ";
  $query .= " FROM ( ";
  $query .=   $union_query;
  $query .= " ) as results ";
  $query .= "group by ID order by count(ID) desc, ID LIMIT 5 ";
   
  // Execute sql query
  $records = DB::query($query);
   
  // Get just IDs
  $justIds = array();
  foreach($records as $record) {
    $justIds[] = $record["ID"];
  }
   
  // SQL filter to get only published and where IDs are in results from the previous query
  $SQL_filter = "Status = 'Published' AND `BlogPostPage`.ID in (".implode(",", $justIds).")";
   
  // Get related blog pages
  return DataObject::get(
    $callerClass = "BlogPostPage",
    $filter = $SQL_filter
  );
   
}

Template

Add the following code into your template file:

<!-- Related articles -->
<% if RelatedArticles %>
  <div>
    <h3><% _t('RELATED_ARTICLES', 'Related articles') %></h3>
    <% control RelatedArticles %>
      <p><a href="$Link" title="$Title"><b>$Title</b></a></p>
    <% end_control %>
  </div>
<% end_if %>

I hope that you'll find this logic useful and if you create or know for better solution please let me know.