Tag: wordpress

The Basics of a WordPress Theme

In the first part of my article on child themes I explained a little about the history of WordPress themes and child themes, what they are and why you should use them. After sharing the post in a couple of online groups I am a part of, I received a fairly good positive response to the topic and my thoughts on it. One thing that also stood out from some of the comments was that a lot of Divi users don’t necessarily know how a WordPress theme works in the first place.

Everything I am writing about is (pretty much) included in the WordPress Theme Handbook, specifically the articles on the Template Files, Template Hierarchy and Child Themes. If you can afford the time to read through the handbook I highly recommend it. However it can sometimes be a little technical, so it’s often easier to get a ‘primer’ on the topic, to give you the relevant information you need in a manageable size. So, based on the responses to my first article I’ve chosen to first write this article to explain a little about how WordPress themes work.

Understanding the basics

To be able to understand child themes, first we need to understand WordPress themes. This means we need to talk a little about the Template Files and the Template Hierarchy. I’m not going to go into too much detail, but in short it’s the way in which WordPress looks for and loads the various template files in your theme to render content.

Template Files

Any WordPress theme has a base set of template files that is uses to render content for different sections of your WordPress site. If you take a look at the files inside a ‘standard’ WordPress themes (like those found on WordPress.org) you will see that, at their base level, all themes are made up of of two things, a style sheet and a bunch of php files, also known as templates. Theses templates include, but are not limited to, files like the header.php, footer.php, archive.php, single.php page.php and sidebar.php. Each of these templates performs a different type of task within your WordPress theme. The templates themselves are also divided into different template types, depending on what they do.

Template partials are template files that will be included inside another template file. For example the header and footer template files are partials. They will be included (and reused) inside other template files to render specific sections of a page, in this case the header and the footer of the site.


  • header.php – contains all code to render the header
  • footer.php – contains all code to render the footer
  • sidebar.php – contains all code to render the sidebar

The common WordPress template files are the templates that will render the specific content of your site they related to. They can be very specific (rendering a specific category of posts, or a specific page) or very generic (for example the index.php file which will be used whenever a specific template file doesnt exist for the content being rendered)


  • page.php – contains the code to render a page
  • archive.php – contains the code to render any blog post archive (or list)
  • single.php – contains the code to render any individual post

If you read up in the Template Files section of the theme developer handbook you can learn what each one does.

Template Hierarchy.

The next thing to understand is the template hierarchy. This is the way in which WordPress will look for a series of template files to render the specific piece of content. If the first template in the list doesn’t exist it will keep going down the list until it finds a template it can use. The easiest way to explain this is to take a look at a WordPress blog.

On a standard WordPress blog, you usually have a ‘list view’ (list of blog items, a thumbnail image, an excerpt and some form of link to the full article) and then an ‘article view’ (the actual article itself, including any content for that article). So, that means that you would need at least two templates, one to render the list view and one to render the article view. This means that the WordPress engine will look for a template for each view and use it to render the content. For the list view, WordPress will look for a ‘archive.php’ file in your template folder to render the list view and a ‘single.php’ file in your template folder for the article view.
So your archive.php would contain some HTML to display the list view as well as a WordPress Loop to render the list of blog posts, and the single.php would contain HTML specific to the article view, as well as some PHP to get the relevant article content for display.

Now, if you are a theme developer you are probably swearing at me right now, and that’s fine. That last statement is not 100% correct but I am simplifying it a bit, bear with me.

So great, there is a template for list views and a template for article views, what now? Well, lets say that you want to have links to a list of blog posts per category and you want a different display for each category list view. Whenever a user views blogs under a certain category you want to include a description of that category above the list view. Well you could do this in two ways. Either you would add some code to your archive.php to check if the user is browsing a list of blog posts per category and show that description or you can use the category specific templates in the template hierarchy and create a different template per category. For example, if you had a category with the slug ‘products’ and you created a template in your theme called category-products.php, whenever the user browses to the products blog category, WordPress would use that template file to render the products category blog items. This is useful because it means you don’t have to add extra processing to your archive.php template file and you can just service products category specific content to the user, just by adding a template file.

The other important thing to understand is that for each level of WordPress, from pages to posts, categories to tags, there is a hierarchy in place as to what gets loaded. So for example, when WordPress renders a list of blog posts, it first looks for a category specific template (either by slug or by id) then a general category template, then the archive template and then finally the index template. So in our products category example above (lets say the product category id was 5) WordPress would look for the following template files, in this order, to use to render the list:

  • category-products.php
  • category-5.php
  • category.php
  • archive.php
  • index.php

That’s 6 levels of template hierarchy that you could use to render things differently for different points of just your blog!

The same goes for blog posts, you have the following set of templates at your disposal when developing a theme. Lets say I was rendering a page with the slug (or name) ‘my-page’ which has an id of 7. WordPress would look for the following templates, in this order, to render the page content:

  • a custom template file – (The page template assigned to the page in WP admin).
  • page-my-page.php
  • page-7.php
  • page.php
  • singular.php
  • index.php

Each template is the fallback to the previous template, with index.php being the final template for pretty much all sections of a WordPress site. You can probably see now why any theme developers reading this article were a little annoyed at my oversimplification of the theme hierarchy earlier ;-).

To get a better understanding of the different levels of template types available to you, as well the order in which WordPress loads them, I highly suggest you read Hierarchy in Detail section of the Theme developers handbook.

So what does this mean in terms of Divi. Well the base of the Divi theme is a small subset of the template partials and common template files. Divi doesnt have (for example) category based templates or even an archive template in its template file list. Because Divi has modules which render the blog lists and pages. Divi tends to use the templates towards the end of the hierarchy (single.php page.php and index.php) to render all content, because the modules are handling all the grunt work. However we need to have an understanding of what these template files do and how they can be ‘overridden’ using the hierarchy to be able to build our child theme.

Filed under: Development, Divi, WordPressTagged with: , , ,

5 SEO Mistakes to Avoid


A little background…

Recently I was in the interesting position where plugin I had always used started giving me some hassles.

For basic SEO functionality my go to install is the Yoast SEO plugin as it gives me the basic features I need and comes highly rated on WordPress.org. For my first client of the year I installed the plugin and found that it gave me an issue when attempting to use the theme’s built in page builder, namely I couldn’t access it. A quick Chrome inspector session revealed a JavaScript error/conflict of some sort. Disabling the plugin caused the issue to go away.

Now normally I would simply contact either the theme developer or the plugin developer and see who was at fault. In this case I asked around and it seems there was some issue with the plugin and the theme I was using. Not prepared to change themes at this point in development I looked into an alternate SEO plugin and came across the All in One SEO Pack from Semper Plugins. A quick install later and all was well.

Now to the point of this introduction. As part of the free version of the plugin I was able to subscribe to their newsletter to receive updates and download a free SEO Tips ebook. As someone who is always keen to improve my knowledge I downloaded the book for future reading. While the books contents are valuable, my favourite part were a list of ‘5 SEO Mistakes to Avoid’.

Whether you’ve been optimizing your web pages for quite some time or are new to the strategy, everyone makes mistakes. While the bad news is that these mistakes can have a significant effect on your page ranking, the good news is, they’re reversible. Here are five SEO mistakes to avoid (And if you’ve already made them, not to worry, you can make changes and move forward with your SEO strategy).

SEO Mistake #1:

Not using keywords correctly. Many webmasters are concerned about being banned from the search engines for keyword spamming or stuffing so they limit the use of keywords on their web pages. As long as your content sounds natural and reads easily, the chances are you have not overused your keywords. Make sure your keywords are included in the first and last paragraphs, in your headings and in your title and meta tags.

SEO Mistake #2:

Trying to fool search engine spiders. Search engines are a lot more sophisticated than most of us realize. They recognize – and penalize – hidden text, keyword spamming, and cloaking, which is showing different content to the search engine spiders than to your visitors. All of these practices only serve to hurt your page ranking and can in fact cause your website to be banned by the search engines, which means no one will find you – and no traffic means no profits.

SEO Mistake #3:

Using Flash Flash is a great presentation tool and can be dramatic and effective if used sparingly. It’s particularly appropriate if you have a media related website and want to demonstrate your industry savvy. However for most website owners it’s just not necessary and can harm your page ranking. Search engine spiders cannot read content embedded in Flash files, which means they’re not recognized or indexed.

SEO Mistake #4:

Using Your Company Name (and Only Your Company Name) As a Title Tag Unless you’re branding your company name, your company name shouldn’t be the only element in your title tag. Feel free to include it, however it’s also important to use your primary keyword for each webpage title tag. This is more useful for your customers and helps the search engines identify the various pages on your site.

SEO Mistake #5:

Using A Splash Page A splash page is a web page with a large graphic or company logo, and a link to enter the site. This is an ineffective strategy for a number of reasons:

  • No keyword rich text on the page, nothing for the spiders to index.
  • Only one internal link on the page
  • These pages often have a redirect which often causes spiders to ignore them

If search engine optimization is important to your business, you may need to forgo the splash page. Your home page should be easy to navigate, content rich, and link visitors and spiders to other main web pages. In general, unless you’re trying to outsmart the search engines and using nefarious tactics, the majority of search engine mistakes are reversible. If you’ve committed a few of these mistakes, simply correcting them can increase your page ranking almost immediately. Take some time to evaluate your SEO strategy and eliminate these SEO mistakes.

Often I come across sites that break these rules and quite a few of them are also not recommended web practices but until now I didn’t realise their impact on SEO. Really valuable information for web owners and developers.

I liked Semper Plugins SEO plugin so much, as well as the information in the ebook, that I signed up as an affiliate for the pro version of their plugin. If you are in the market for a premium SEO plugin, I highly recommend All in One SEO Pro.

Filed under: Development, WordPressTagged with: , ,

Getting ready for 2016

Digital Resolutions.

2016 sees a new direction for me. For the first time since I started programming in 2004, I will be 100% self employed.

This doesn’t mean that I have always been employed by a boss for the last 12 years. There were some attempts at working for myself in the past, but each time it was at the request of someone else. This time it is a decision I have come to after much consideration and planning.

I am quite looking forward to this new direction. It will no doubt come with its struggles, but I get to focus on one of the things I like the most about working in the digital space, namely working with clients and solving their problems via technology.

To prepare myself for this new journey I have made a few digital ‘resolutions’ for 2016.

1. I will not reinvent the wheel.

Open source content management sytems have come a long way since I started custom coding CMS’s in 2004. With the worldwide adoption of WordPress as the CMS of choice and the multitude of top quality free and premium themes and plugins available, there really is no reason to develop a website from scratch any more. More often that not, when discussing various user requirements for a website, a quick Google search will reveal that (to coin the Apple catchphrase) ‘there’s a plugin for that’.

So my plan is simple. Instead of writing all the code myself, I will build on top of the shoulders of giants. Similar to what my colleague Ross has done over at Shopcreatify, I will be offering my services as a website ‘fitter’. A digital foreman if you will. I’ll get my hands dirty every now and then, but first and foremost I will use tried and tested themes, plugins and services, developed by experts in their respective fields, to deliver amazing web and mobile solutions to my clients.

2. Back to school

One of my secondary goals for 2016 is to spend as much of my free time as possible learning new skills. Not specifically development skills (e.g. new languages and/or technologies) but skills that will allow me to bring a more rounded service to my clients. A large part of this will be a focus on digital marketing. This isn’t just to be able to provide basic digital marketing services to my clients, but also to grow my own marketing experience, both for my digital business and the jiu-jitsu school I run.

4. Personal projects

There are a couple of personal software projects that I am keen to build on and grow.

A few years ago I wrote a piece of web based software to help a client (and my jiu-jitsu instructor) manage his students. This has huge potential for all the other martial arts gyms in South Africa and I just haven’t had the time to develop the idea to a point where I can offer it to multiple clients.

I am also part of a family run business with my wife. There is a lot of scope for converting manual processes into digitally driven ones and I am keen to get my hands dirty making our lives easier by improving our processes through automation.

3. Family man

The primary reason I am taking all of the above steps is to be able to spend more time with my family. Over the past four years I have been working on site at a great local development company but time with my family suffered, due to the hours I was busy with the various things I was involved in. Starting off on my own will hopefully give me some flexibility to spend a little bit more quality time with my family.

I’ve never been one for new year resolutions, based purely on the fact that most people never see them through. So I don’t really want to call these resolutions, the are life decisions I have made (for good or for ill) that I plan to live on a day to day basis.

I can’t wait to see how 2016 turns out.



Filed under: ExperiencesTagged with: , ,

Word Camp Cape Town 2015

So this year I was fortunate enough to attend my first Word Camp.

I’ve been meddling (at best) with WordPress for the better part of the last decade, I’ve set up a few blogs and sites and even completed some custom development using WordPress as the base but I’ve never been someone who was ‘focused’ on WordPress or the WordPress community. This year I decided to take a deeper look into what makes the local WordPress community tick. Boy, what a ride it was.

Day one dawned a typically early spring Cape Town day, namely rain. After getting out to the awesome venue that is the River Club, we got to mingle with some of the attendees and pick and choose our selection of ‘swag’. I wasn’t sure what the protocol was, so I just grabbed a few interesting items, my favourite of which was the USB power banks supplied by FNB/Paypal. A spread of coffee/teas and muffins awaited us while we milled around the entrance area, and then onto the workshops we went.

I chose to attend all the developer workshops and I wasn’t disappointed. From Brent’s talk on Varying Vagrant Vagrants to Pippin’s ‘Commitment to Backwards Compatibility’, each speaker was interesting, knowledgeable and insightful. I especially enjoyed Justin’s talk on the WordPress API, mainly because how interesting and funny he was at the same time, even after a 16 odd hour flight.

Day two was more of a typical conference day, with everyone seating in the auditorium, cinema style, listening to the talks of the day. Our’s MC’s were the always funny Derick Watts and the Sunday Blues who had the crowd in stitches in between each talk.

The talks on day two were just as interesting as day one, but the two that stood out for me the most were Drew’s ‘It takes a Village to make Wordpress’ and Bruce’s ‘The Age of the Digital Superhero’. Not that all the other speakers weren’t great (they were) but these two resonated with me on a personal level.

The last talk ended with the words ‘f*cking awesome’ which was apt, as this was the feeling I had when I left WordCamp. I met some amazing people and was inspired as a developer, both and a personal and a technical level. WordPress has come a long way in the past few years and it was really great to see and meet so many people who are developing, using and growing WordPress as a platform.

Special thanks go to Hugh Lashbrooke, who put all this together and was super friendly every time you chatted to him, even though I am sure he was buzzing from the nerves of running such a huge event.

And finally, thanks to all the amazing WordPress users and developers I meet, who made me realised that there is something special about belonging to an open source community.

I’ll definitely be back next year.

Filed under: Experiences, WordPressTagged with: ,

Migrating from Drupal to WordPress

Well, it finally happened. After months of frustration with Drupal as a blog tool, I gave up and decided it was time to move over to WordPress.

I’m sure as a CMS system Drupal can be quite wonderful, but for running and managing a simple blog site, I have yet to come across a better platform than WordPress.

However the migration of the database content from one system to another had it’s ups and downs. Fortunately for me, not many people read this blog, so I only had to migrate the posts data from one database to the other.

Below is the script I wrote to do so, in the hopes that this might save someone else the hassle of writing it themselves.

P.S. This was for a Drupal 6 to WordPress 2.8.2 migration. If it doesn’t work for you because you are working with different versions of either Drupal or WordPress, drop me a line and I will try to modify it for you.

	* Setup database constants
	define('DRUPAL', 'phpdevec_drpl1');
	define('WORDPRESS', 'phpdevec_wordpress');
	define('URL', 'http://www.php-developer.co.za/');
	* Setup server constants
	define("SERVER", "localhost");
	define('USER', 'username');
	define('PASSWORD', 'password');

	* debug function outputs data
	function debug($data){
		echo '<pre>';
		echo '</pre>';

	* Connect to server and database
	function connect($db) {
		// database connection
		$conn = mysql_connect(SERVER, USER, PASSWORD);
		if (!$conn) {
			//connection to server failed
			die("Cannot connect to server");
			return false;

		$dbSelected = mysql_select_db($db, $conn);
		if(!$dbSelected) {
			// database connection failed
			die("Cannot connect to database");
			return false;
		return $conn;

	* Close database connection
	function disconnect($conn){
		if ($conn){
			// connection exists to close
			if (!mysql_close($conn)){
				// database connection failed
				die("Cannot close database connection");
				return false;
			return true;
		// default return in case the original connection failed
		return true;

	* Generic SQL SELECT, checks type
	* @param string $sql query
	* @param string $type object or assoc
	* @return mixed $return array of rows if select or true/false if insert/update/delete

	function sql($sql, $database, $type = 'assoc'){
		$return = '';

		// check database connection
		$connection = connect($database);
		if (!$connection){
			$return = false;

		$sql = ltrim($sql);
		$query_type = substr($sql, 0, 6);

		// run query
		$rst = mysql_query($sql);

		if (!$rst){
			// query failed for some reason
			die("Error in MySQL query: " . $sql);
			$return = false;
		}else {

			if (strtoupper($query_type) == "SELECT"){
				// query was SELECT
				$return = array();
				$rows = mysql_num_rows($rst);

				switch($type) {
					case 'assoc' : // return as assoc array
						while($row = mysql_fetch_array($rst)) {
							// gather rows
							$return[] = $row;
					default : //return as object
					while($row = mysql_fetch_object($rst)) {
						// gather rows
						$return[] = $row;
			}else {
				// query was INSERT, UPDATE OR DELETE
				$queryType = 'UPDATE';
				if (mysql_insert_id()){
					// query was INSERT
					$id = mysql_insert_id();
					$queryType = 'INSERT';
					$return = $id;
				}else {
					$rows = mysql_affected_rows();
					$return = $rows;

		// disconnect from database

		//return rows / whether insert/update/delete successful
		return $return;

	$sql = "SELECT n.*, nv.body as content FROM node as n LEFT JOIN node_revisions as nv on n.nid = nv.nid WHERE n.type = 'blog'";

	$nodes = sql($sql, DRUPAL);


	$Nodes = array();

	foreach ($nodes as $node){
		$nId = $node['nid'];
		$sql = "SELECT * FROM comments WHERE nid = '$nId'";
		$comments = sql($sql, DRUPAL);
		$node['comments'] = $comments;
		$Nodes[] = $node;


	foreach ($Nodes as $node){
		//publish draft

		$two_hours = 2*3600;
		$date = $node['created'];
		$gmt_date = $date - $two_hours;
		$modified = $node['changed'];
		$gmt_modified = $modified - $two_hours;

		$post_date = date('Y-m-d H:i:s', $date);
		$post_date_gmt = date('Y-m-d H:i:s', $gmt_date);
		$post_modified = date('Y-m-d H:i:s', $modified);
		$post_modified_gmt = date('Y-m-d H:i:s', $gmt_modified);

		$post_title = mysql_escape_string($node['title']);
		$post_content = mysql_escape_string($node['content']);
		$post_name = strtolower(str_replace(array(' ', '.'), array('-', ''), $post_title));

		$status = $node['status'] == '1' ? 'publish' : 'draft';

		$sql = "INSERT INTO phpdevec_posts SET
			post_author = 1,
			post_date = '$post_date',
			post_date_gmt = '$post_date_gmt',
			post_content = '$post_content',
			post_title = '$post_title',
			post_status = '$status',
			comment_status = 'open',
			ping_status = 'open',
			post_name = '$post_name',
			post_modified = '$post_modified',
			post_modified_gmt = '$post_modified_gmt',
			post_parent = '0',
			menu_order = '0',
			post_type = 'post',
			comment_count = 0";

		$id = sql($sql, WORDPRESS);

		if (!$id){
			die('An error occured adding the data to the database');


		if (!empty($comments)){
			foreach ($comments as $comment){

		$guid = URL . "?p=$id";

		$sql = "UPDATE phpdevec_posts SET guid = '$guid' WHERE ID = '$id'";

		$updated_rows = sql($sql, WORDPRESS);

		if (!$updated_rows){
			die('An error occured updating record no '. $id);

Filed under: DevelopmentTagged with: , ,