Prestbury Web Services

01625 820 339

WordPress Optimisation 101


google dev

Open Graph & ld+Json markup – without plug-ins

This is the first in a series of full disclosure guides to my techniques for optimising WordPress theme builds to give the fullest SEO viability with dynamic Open Graph and ld+Json markup for social share and Google rich content respectively.
This approach means no more heavy third party plug-ins that can seriously impact site speed, undermine code validity and even compromise security. Complex third party plug-ins can always pose a potential security risk as many can be exploited by hackers wishing to do naughty things with your database.

SEO Plug-ins – The Basics

So, before we delve a little deeper we need to recap and summarise the typical functions offered by many popular SEO plug-ins. I tend to split these functions into two categories, UA (Useful Additions) and total BS (Basic Shenanigans). The UA functions are things such as dynamic Open Graph tags added to the header, Json-ld markup added to the header and bespoke meta descriptions for pages and posts. These are all things that we are going to hard code into our theme. The total BS is keyword density checks and scores, reading ease scores and basically anything else connected with content metrics can now be ignored.
The reason why we no longer need to pay attention to key-wording metrics and other similar parameters is because it is 2020 and not 2002! If you are writing content with enthusiasm, about a subject in which you have a reasonable expertise and you write at least two paragraphs with citations and (mostly) good grammar, then you are creating quality content that should be meaningful, useful and compelling for the target audience. It really is as simple as that. The Google algorithm is now a very nuanced and subtle thing that just has ways of knowing that you know what you are talking about. Also, the main ‘algorithm’ of concern is the one that resides in the minds of our proposed readership. If we write from the ‘heart’ and convey authentic interest then we will create satisfying and informative content which people will find useful and want to share – hopefully! Let the title(s) do the keyword heavy lifting and you will be fine. Conversely, if we were to write to the plug-ins’ keyword algorithm so as to get the green light say, then we are more likely to produce bad, stilted, repetitive text and it is very unlikely to make any difference to Google; but it will really irritate readers!

The Code

Now we can move on to the heart of the issue, so lets dive right in to the coding – there is quite a lot of this.
First, we will create a custom field for meta descriptions. The following code is placed in functions.php, this will populate a meta description tag in the wp_header.php with what ever is entered into the custom ‘description’ fields for each post and page. Note that we only need to write the meta description for the blog page (index.php) into this snippet…This project is based on a WP theme with a static front page.

Set the meta description in functions.php


// Blogpage Meta Description
<?php
function add_custom_meta_des(){
if( is_home() ){
$meta_des = "Front page meta description here."; #Edit here
echo '<meta name="description" content="' . $meta_des . '" />';
}
// Single Page Meta Description
if( is_single() || is_page() ){
$des = get_post_meta( get_the_id(), 'description', true);
if( ! empty( $des ) ){
$meta_des = esc_html($des);
echo '<meta name="description" content="' . $meta_des . '" />';
}
}}
add_action( 'wp_head', 'add_custom_meta_des', 4 );
?>


Hop over to your WP dashboard and go to edit any page or post. You can now create a custom field called ‘description’ here. Once done anything entered into this field becomes the meta description of that page or post. Check this with a source view once completed.

Create Custom Field

Screen

Check Output in source View

screen

The Header File and OG Tags

Next we move on to the header.php file and populate this with the html / php markup that will dynamically create our Open Graph tags for social share goodness. You will see that the code is mostly based around standard WP functions. This block of code will also make use of the meta description tag that we previously enabled and you will see this implemented with a few conditional [if] statements as [meta property=”og:description”].

This is the full code block for Open Graph tagging


<meta property="og:locale" content="en-GB" />
<meta property="og:type" content="website" />
<meta property="og:title" content="<?php bloginfo('name')?>" />
<meta property="og:description" content="<?php
if( is_single() || is_page() ){
$des = get_post_meta( get_the_id(), ‘description’, true);
if( ! empty( $des ) ){
$meta_des = esc_html($des);
echo "$meta_des";
}} ?>"/>
<meta property="og:url" content="<?php the_permalink(); ?>" />
<meta property="og:site_name" content="<?php bloginfo('name')?>" />
<meta property="og:image" content="<?php the_post_thumbnail_url(); ?>" />
<meta property="og:image:secure_url" content="<?php the_post_thumbnail_url(); ?>" />
<?php $image_attributes = wp_get_attachment_image_src
( get_post_thumbnail_id( get_the_ID() ), "full" );
if ( $image_attributes ) : ?>
<meta property="og:image:width" content="<?php echo $image_attributes[1]; ?>" />
<meta property="og:image:height" content="<?php echo $image_attributes[2]; ?>" />
<?php endif; ?>
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:description" content="<?php
if( is_single() || is_page() ){
$des = get_post_meta( get_the_id(), ‘description’, true);
if( ! empty( $des ) ){
$meta_des = esc_html($des);
echo "$meta_des";
}} ?>"/>
<meta name="twitter:title" content="<?php the_permalink(); ?>" />
<meta name="twitter:site" content="@myHandle" />
<meta name="twitter:image" content="<?php the_post_thumbnail_url(); ?>" />
<meta name="twitter:creator" content="@myHandle" />

Remember to add your own Twitter handle into the Twitter site and creator tags.
The OG tags will cite an image for display in a Facebook, Twitter card or share item. This code pulls in the largest version of the attachment image or featured image for each post or page. There is also a bit of clever php that will read and display the image size attributes which are required in the OG convention.

The image tags


<meta property="og:image:secure_url" content="<?php the_post_thumbnail_url(); ?>" />
<?php $image_attributes = wp_get_attachment_image_src( get_post_thumbnail_id( get_the_ID() ), "full" );
if ( $image_attributes ) : ?>
<meta property="og:image:width" content="<?php echo $image_attributes[1]; ?>"/>
<meta property="og:image:height" content="<?php echo $image_attributes[2]; ?>" />
<?php endif; ?>


Once this code has been installed and thoroughly checked, load a page or post from your site and once again check source view in the browser to make sure that the tags are populating properly. Check at least two different pages. I will discuss Facebook and Twitter card debugging at the bottom of the article.

Now it Gets Esoteric – ld+Json

Now we are ready to move on to the most complex part of all, and possibly an item that some believe is no longer required or even worth bothering with? This is the ld+json markup that helps to populate Google search results with rich content from your pages — sometimes, but mostly almost never!
However, it is still worth incorporating this item. There are many complex variations for json-ld depending on the nature of your site or business. The example given here is for sites in the category defined as “organisation” and this is suitable for just about any commercial site associated with a brand identity.
Notice again how the standard WP functions and our new meta description field also form the bedrock of this code. From that its essential function should be almost self-explanatory for any skilled WP developer.
In the top block we have all the basic identity credentials of our site and a [sameAs:]
field that takes links to associated social media accounts. This is the markup that is supposed to be responsible for populating your company knowledge panel with social links at the bottom.

Here is the full markup for ld+json


<script type='application/ld+json'>
{"@context":"https://schema.org",
"@graph":
[{
"@type":"Organization",
"@id":"<?php bloginfo( 'url' )?>/#organization ",
"name":"<?php bloginfo( 'name')?>",
"url":"<?php bloginfo( 'url' )?>/",
"sameAs":[
"https://www.facebook.com/mypage",
"https://twitter.com/myhandle",
],
"logo":{"@type":"ImageObject","@id":"<?php bloginfo( 'url' )?>/#logo",
"url":"https://www.mysite.com/mylogo","width":xxxx,"height":xxx,"caption":"<?php bloginfo( 'name')?>"},
"image":{"@id":"<?php bloginfo( 'url' )?>/#logo"}},
{"@type":"WebSite","@id":"<?php bloginfo( 'url' )?>/#website",
"url":"<?php bloginfo( 'url' )?>/",
"name":"<?php bloginfo( 'name')?>",
"publisher":{"@id":"<?php bloginfo( 'url' )?>/#organization "},
"potentialAction":{"@type":"SearchAction","target":"
<?php bloginfo( 'url' )?>/?s={search_term_string}",
"query-input":"required name=search_term_string"}},
{"@type":"WebPage","@id":"<?php the_permalink(); ?>#webpage",
"url":"<?php the_permalink(); ?>",
"inLanguage":"en-GB",
"name":"<?php the_title(); ?>",
"isPartOf":{"@id":"<?php bloginfo( 'url' )?>/#website"},
<?php
if ( has_post_thumbnail() ) {?>
"image":{
"@type":"ImageObject",
"@id":"<?php the_permalink(); ?>#primaryimage",
"url":"<?php the_post_thumbnail_url(); ?> ",
<?php $image_attributes = wp_get_attachment_image_src
( get_post_thumbnail_id( get_the_id() ), "full" );
if ( $image_attributes ) : ?>
"width":<?php echo $image_attributes[1]; ?>,
"height":<?php echo $image_attributes[2]; ?>,
<?php endif; ?>
"caption":"<?php the_post_thumbnail_caption(); ?>"},
"primaryImageOfPage":{"@id":"<?php the_permalink(); ?>#primaryimage"},
<?php }?>
"datePublished":"<?php the_time( 'c' ) ?>",
"dateModified":"<?php the_modified_date( 'c' ); ?>",
"description":"<?php
if( is_single() || is_page() ){
$des = get_post_meta( get_the_id(), 'description', true);
if( ! empty( $des ) ){
$meta_des = esc_html($des);
echo "$meta_des";
}
}
?>"
}]}
</script>

Another interesting feature of the “organisation” protocol is the Logo field. This is where you tell google about your company logo and provide a link to it. This has to be manually entered along with the image dimensions and of course this will be a static element – the same on all pages of your site.
Once again, after installing this code, launch your site in a browser and check at least two random pages / posts in source view to make sure all of the tags are being populated correctly.
And that is it. Now your word press site is automatically doing everything Yoast and others implement but with much less hassle.
If you want to see exactly what the output of this all looks like then just view the source of this page and it is everything between the comment lines that say –Prestburyweb SEO and Schema module–

Quick Note on Debugging

It is a good idea to debug pages for share with the FB Debugger and Twitter Card checker below. Their function is pretty much self explanatory but both of these applications work with your OG tags.
Facebook debugger here
Twitter cards here

Important Caveat

Please note that all of this rich content markup should not be used in combination with SEO plug-ins as this will give rise to duplicate tags. It is possible to disable the ld+json and OGT output of Yoast with a few snippets in your functions file but generally it is better to decide whether you want to take the hard-coded or plug-in route and then stick to one method. Obviously, I prefer to hard-code as much as possible into my themes or to write my own plug-ins.

Coming Next

In my next blog I will discuss methods for cleaning up the header to enhance W3C validation of WP pages and posts. There is a lot of superfluous content that can be safely removed.
Also, I will show you how I created the highlighted markup in this post with the aid of the ‘sed’ command in Linux terminal and a hand coded bash script to replace code parts with span classes and html entities. I will make this script and the relevant CSS downloadable.


  |   »


Category: code, Technology   Tags: , ,

11-Dec-2020