Tutorials - Big bits of code to help you do more
Add flexible MetaTags control to Pages
Tweet15 January 2011 | | | Supports v2.4
When you display DataObjects as if they are a single Page or use a Page Action to display a different view of the same Page, by default your MetaTags are the same as the tags you entered in the CMS.
One way to change the MetaTags dynamicly is to add a lot of template controls to display different MetaTags for each view, but you can get a lot more flexibility by overriding the SiteTree MetaTags function in your Page_Controller.
Usecases
One thing I always like is to show the SiteConfig SiteTitle on the HomePage first, but on other pages I want it to show at the end of the Title:
My Awesome Site >> Home
My Detail Page >> My Awesome Site
Or when you have an Product OrderPage you want to add the orderprocess steps in the BrowserTitle:
OrderPage >> ShoppingCart >> My Awesome Site
OrderPage >> My Details >> My Awesome Site
OrderPage >> Confirm Order >> My Awesome Site
And when you show a DataObject as if it is a Page you want to show:
My DataObjectTitle >> MyPageTitle >> My Awesome Site
Lets make this happen quite easily
Copy the complete MetaTags function from SiteTree.php (around r1234 (no joke in v2.4.4)) to your Page_Controller class :
public function MetaTags($includeTitle = true) {
$tags = "";
if($includeTitle === true || $includeTitle == 'true') {
$tags .= "<title>" . Convert::raw2xml(($this->MetaTitle)
? $this->MetaTitle
: $this->Title) . "</title>\n";
}
$tags .= "<meta name=\"generator\" content=\"SilverStripe - http://silverstripe.org\" />\n";
$charset = ContentNegotiator::get_encoding();
$tags .= "<meta http-equiv=\"Content-type\" content=\"text/html; charset=$charset\" />\n";
if($this->MetaKeywords) {
$tags .= "<meta name=\"keywords\" content=\"" . Convert::raw2att($this->MetaKeywords) . "\" />\n";
}
if($this->MetaDescription) {
$tags .= "<meta name=\"description\" content=\"" . Convert::raw2att($this->MetaDescription) . "\" />\n";
}
if($this->ExtraMeta) {
$tags .= $this->ExtraMeta . "\n";
}
$this->extend('MetaTags', $tags);
return $tags;
}
As you see the Metatags are returned from the current Page with $this->MetaTitle or $this->MetaDescription. Thats not very flexible, so lets change that.
Replace the $this->MetaDescription etc. values with function calls like $this->MetaDescription(); and add those functions to Page_Controller as well, so it looks like this:
public function MetaTags($includeTitle = true) {
$tags = "";
if($includeTitle === true || $includeTitle == 'true') {
$tags .= "<title>" . Convert::raw2xml($this->MetaTitle()) . "</title>\n";
}
$tags .= "<meta name=\"generator\" content=\"SilverStripe - http://silverstripe.org\" />\n";
$charset = ContentNegotiator::get_encoding();
$tags .= "<meta http-equiv=\"Content-type\" content=\"text/html; charset=$charset\" />\n";
if($this->MetaKeywords()) {
$tags .= "<meta name=\"keywords\" content=\"" . Convert::raw2att($this->MetaKeywords()) . "\" />\n";
}
if($this->MetaDescription()) {
$tags .= "<meta name=\"description\" content=\"" . Convert::raw2att($this->MetaDescription()) . "\" />\n";
}
if($this->ExtraMeta()) {
$tags .= $this->ExtraMeta() . "\n";
}
$this->extend('MetaTags', $tags);
return $tags;
}
function MetaTitle(){
return ($this->MetaTitle) ? $this->MetaTitle : $this->Title;
}
function MetaKeywords(){
return $this->MetaKeywords;
}
function MetaDescription(){
return $this->MetaDescription;
}
function ExtraMeta(){
return $this->ExtraMeta;
}
Now you have moved the MetaTag values to seperate functions, which you can customize on each PageType based on what you want to show on a certain action or DataObject you are showing.
Let say you want the SiteConfig Title to show up in the MetaTitle as well, you can do something like this :
function MetaTitle(){
$title = ($this->MetaTitle) ? $this->MetaTitle : $this->Title;
return $title . ' - ' . $this->SiteConfig()->Title;
}
In your template you would now use:
$MetaTags(false)
<title>$MetaTitle</title>
This is quite similar like it is already done Page.ss from the BlackCandy template, but we want to control the Tags in the Controller.
Now when you have a seperate HomePage PageType, like I always have, and you there want to display the the SiteConfig Title first and add the SiteConfig Tagline as well, you just have to add the function MetaTitle() to HomePage_Controller :
function MetaTitle(){
$title = ($this->MetaTitle) ? $this->MetaTitle : $this->Title;
return $this->SiteConfig()->Title . ' - ' . $this->SiteConfig()->Tagline . ' - ' . $title;
}
Now that was not that hard, lets change the MetaTitle so its shows something complety different.
Change MetaTags based on Controller Actions
Lets use the tutorial DataObjects as Pages for these examples. When we show a single StaffMember, we want to show the StaffMember Name in the Browser Title Bar instead of the StaffPage MetaTitle.
Just add the function MetaTitle() to the StaffPage_Controller and check for the current Action and getStaffMember() to decide if you want to show a different MetaTitle:
function MetaTitle(){
if($this->urlParams['Action'] == 'show' && $this->getStaffMember()) {
$title = $this->getStaffMember()->Name;
} else {
$title = ($this->MetaTitle) ? $this->MetaTitle : $this->Title;
}
return $title . ' - ' . $this->SiteConfig()->Title;
}
And ofcourse we want a different MetaDescription as well :
function MetaDescription(){
if($this->urlParams['Action'] == 'show' && $this->getStaffMember() && $this->getStaffMember()->Description) {
return $this->getStaffMember()->Description;
} else {
return $this->MetaDescription;
}
}
In this way you can control the MetaTags very easy, based uppon Controller Actions or if a certain DataObject is displayed. You don't have to copy the MetaTags() function to each PageType, just the MetaTitle(), MetaKeyWords() and MetaDescription() methods and apply a different set of rules for each PageType. You can concat several fields from a DataObject to use for the MetaDescription or add a MetaDescription and MetaKeyWords fields to your DataObject and use those.
If you easily want to add the KeyWords based uppon a Text Field. Copy this class to your code base:
http://www.sspaste.com/paste/show/4d2649a25d034
and call KeywordsGenerator::generateKeywords($string); to get an ordered list of keywords:
function MetaKeywords(){
if($this->MetaDescription()){
return KeywordsGenerator::generateKeywords($this->MetaDescription());
} else {
return Convert::raw2att($this->MetaKeywords);
}
}
Good luck!
2 Comments
RSS feed for comments on this page RSS feed for all comments
Darren-Lee
09/04/2011 7:18pm (2 years ago)
@Martijn van Nieuwenhoven - I like that technique - makes the SEO more relevant. I am going to try this on my current project where I am displaying DataObjects as pages. Thanks for sharing.
Sonet
29/01/2012 9:16pm (1 year ago)
@Martijn - I think it is not necessary to override the MetaTags function from SiteTree.php. The controller methods get priority over the model.
class Page extends SiteTree {
public static $db = array(
"Foo" => "Text",
);
}
class Page_Controller extends ContentController {
public function getFoo() {
return 'controller value';
}
}
Calling $Foo from template will result in 'controller value'. I remember using this approach to set the $MetaTitle for DataObject as pages.
Post a comment ...
You cannot post comments until you have logged in. Login Here.