profile picture
16 Jun 2014
WordPress beginner's guide

Some best practices to approach WordPress development in a reasoned way. The post is meant for beginners and aims to collect in one place the minimal knowledge to start developing a WordPress theme.

AngularJS logo

Crafting a WordPress theme from scratch is not as simple as it seems. The reason is that WordPress is not just a blogging platform anymore, but it has gradually become a refined publishing ecosystem with thousands of features. In this post I will propose some best practices to approach WordPress development in a reasoned way. The article is meant for beginners and aims to collect in one place the necessary knowledge to start developing a WordPress theme.

1. Tools & Docs

It could sound pretty obvious, but the first thing to do is to gather information about the technologies we’re going to use. To code a WordPress theme you need to know basics of PHP, HTML5, CSS and JavaScript. Being comfortable with jQuery is not necessary, but will help.

Concerning the platform itself, you can rely on the official WordPress Codex Pages, a very active community, a Stack Exchange channel and a big amount of blogs and forums. However, It’s better considering at first the official documentation. WordPress is still in development, and the web is plenty of old deprecated snippets of code.

2. Fundamentals

Before coding, you should take some time to understand how WordPress works. The Hierarchy, the Query, the Loops and Actions (Hooks and Filters) are the fundamental concepts you should familiarize with.

The Hierarchy

Basically WordPress is a PHP engine that retrieves (on the server or in a MySQL database) the content requested by the reader, and then serves it with the corresponding template. The so-called hierarchy is the combination of the logic and priority rules that WordPress follows each time a request is done. Understanding when and how requests are satisfied is crucial to keep the design of your theme clean. Here you can find a helpful and interactive diagram that links the different queries to the respective documentation.

The Query

The Query is the hearth of WordPress. It is the core process that fetches the request content. Technically is a PHP Class which instantiates a PHP object with all the information concerning the requested content.  All encapsulated data are then made available to the Loop.

The Loop

The easiest way to understand the Loop is to figure it as a printer. Firstly the Query retrieves the requested content according to the Hierarchy rules, then the Query shares its results with the WordPress Loop, and finally the Loop makes them available for the output via a set of specific PHP functions. While in plain PHP you’re used to call printf or echo to display something on the screen, in the WordPress environment you should always recur to the built-in specific Loop functions. A typical expression of WordPress jargon is: “This function must be used within the Loop”. In simple words, that means the function must be called in the front-end part of your theme, where you define how the content is displayed.

Hooks

WordPress is both hi-standardized and customizable. To achieve this equilibrium, its designers have deployed a mechanism of hooks and filters that allows developers to manipulate hi-level behaviors and appearance without modifying the core. Every customizable process is identified with a hook. Via the hooks developers can inject their custom code and apply filters.

/*
** Action exemple: Inject code after the "switch theme" event hook.
** This code sets the "recent posts", "categories", "archives" as default widgets.
*/

function your_theme_set_default_widgets ()
{
  $your_theme_default_widgets =  array (
    'wp_inactive_widgets' => array (),
    'your-theme-main-sidebar' => array (
      0 => 'recent-posts-2',
      1 => 'archives-2',
      2 => 'categories-2',
    ), 
    'array_version' => 3
  );
  update_option( 'sidebars_widgets', $your_theme_default_widgets );
}
add_action('after_switch_theme', 'your_theme_set_default_widgets');
Filters

A filter is a PHP function that modifies a WordPress process at run time. Filters are the best way to make WordPress do everything you need for your project.

/*
**A filter to exclude Home Code Snippets Post from Homepage.
*/

function your_theme_exclude_category_from_home( $query )
{
  if ( $query->is_home() && $query->is_main_query() )
  {
    $query->set( 'cat', -get_cat_ID( 'snippets' ) );
  }
  return $query;
}
add_filter( 'pre_get_posts', 'your_theme_category_from_home' );
3. Architecture

We can divide a WordPress theme in two functional parts: the back-end and the front-end.

Back-end

The back-end part of a theme is the PHP code that provides all the data and logic that our theme requires (features, dedicated dashboard areas, specific data or properties, etc.). For example, if we would like to display a reading time at the top of each post, then we must code a PHP function that calculates that value and makes it available to the front-end components.

The right place where to put our “reading time feature” is in the function.php file. This file is the core of the back-end. We can both use it to implement custom features and override WordPress standards behaviors.

Front-end

The front-end part is actually made up by all the different PHP/HTML templates we use in our theme. Usually we have a template for the home page (index.php), another for the post (single.php), for the archives (archives.php), and so on… Every PHP/HTML template will be filled with the content provided by the Loop functions or the specific data generated by our custom back-end.

4. Frameworks

I strongly recommend using a starter theme to guide you trough your first WordPress theme. It’s easier to build on the top of a well-designed existing structure, instead of coding everything from scratch. Using a framework created by WordPress experts is also a good opportunity to discover and learn best practices and great tricks. There are several starters out there. IMHO the best ones are Underscores and Bone.

Underscores

Underscores (also written literally as _s) is a starter theme maintained by the Automattic team. It is really complete, clean and 100% compliant with all the most recent WordPress standards and guidelines. It provides you with a quite complete file structure, a CSS style sheet with all the minimum-required WordPress classes and styles, and it implements all the basic features you need to build a functional WordPress theme. Its lightness and minimalism allow you to transform it in what you want: from a neat writer blog to a beautiful designer portfolio until an extensive news website.

Bones

Bones, as his motto says, is a more sophisticated WordPress starter. It adds several advanced features to the toolbox of a WordPress developer. It comes with a mobile-first approach, responsive layouts, SASS, custom post types and custom dashboard out of the box. It’s an awesome developing companion, but maybe it suits better to those who have already experienced WordPress developing and now want to take their projects to the next level.

4. Debugging

Debug quite often your project, even if not complete. The community shares a lot of useful debugging tools. Here is a shortlist of a strictly minimal starter toolkit:

themeCheck passed
5. The code

It’s now time to dive deeper in code details.

File organisation
/*
**Display a clean navigation menu bar in all pages.
*/

if ( ! function_exists( 'your_navmenu' ) ) :
function your_navmenu()
{
  $navmenuParameters = array(
    'theme_location'  => 'your-navmenu',
    'container'     => 'nav',
    'container_class' => 'navmenu',
    'echo'        => false,
    'depth'       => 0,
  );
  echo strip_tags(wp_nav_menu( $navmenuParameters ) );
}
endif;
/*
**Create a custom menu in the dashboard appeareance section.
**It lets the user set his twitter account a profile image.
*/

function your_theme_settings_manager( $wp_customize )
{
  $wp_customize->add_section( 'your_theme_settings', array(
        'title'   => 'your_theme Theme Settings',
        'priority'  => 1,
  ) );

  // Twitter
  $wp_customize->add_setting( 'set_twitter', array(
        'default' => '',
        'capability'=> 'edit_theme_options',
        'transport' => 'refresh',
        'sanitize_callback' => 'sanitize_text_field',
  ) );

  $wp_customize->add_control( 'set_twitter', array(
        'label'   => 'Twitter Account (without the @)',
        'section' => 'your_theme_settings',
        'type'    => 'text',
  ) );

  // Profile Image
  $wp_customize->add_setting( 'set_profile_image', array(
        'default' => get_template_directory_uri() . '/images/profile.jpg',
        'capability'=> 'edit_theme_options',
        'transport' => 'refresh',
        'sanitize_callback' => 'sanitize_raw_url',
  ) );

  $wp_customize->add_control( new WP_Customize_Image_Control( $wp_customize, 'set_profile_image', array(
        'label'   => 'Profile image',
        'section' => 'your_theme_settings',
        'settings'  => 'set_profile_image',
  ) ) );
}
add_action( 'customize_register', 'your_theme_settings_manager' );
Implementation
/*
** No content template
*/

if ( is_search() )
{
    esc_html_e( 'The search returned no result. Please, try with different keywords. Otherwise you can enjoy a recent post or browse the archives below. ', 'your-theme' );
}
elseif ( is_category() )
{
    esc_html_e( 'Sorry, but there is no post for this category. ', 'your-theme' );
}
else
{
    esc_html_e( 'Nothing found.', 'your-theme' );
}
Style sheets and scripts WordPress installations usually host custom themes, custom styles, custom plugins and custom scripts. In order to make all these components work together flawlessly some importing rules must be respected.
/*
** Enqueue scripts and styles.
*/

function your_theme_scripts()
{
//Load Julien Shapiros's velocity.js library for smoother animations */
  wp_register_script( 'velocity', get_template_directory_uri() . '/inc/velocity/velocity.min.js', array('jquery'), '1.0', true);
  wp_enqueue_script( 'velocity' );
  wp_register_script( 'velocity-ui', get_template_directory_uri() . '/inc/velocity/velocity.ui.min.js', array('jquery', 'velocity'), '1.0', true);
  wp_enqueue_script( 'velocity-ui' );

  wp_register_script( 'home-script', get_template_directory_uri() . '/js/home.js', array('jquery'), '1.0', true);
  wp_register_script( 'single-script', get_template_directory_uri() . '/js/single.js', array('jquery', 'velocity', 'velocity-ui'), '1.0', true);
  wp_register_script( 'search-result-script', get_template_directory_uri() . '/js/search-result.js', array('jquery'), '1.0', true);
  wp_register_script( '404-script', get_template_directory_uri() . '/js/404.js', array('jquery'), '1.0', true);
}
add_action( 'wp_enqueue_scripts', 'your_theme_scripts' );

function your_theme_css()
{
  wp_register_style( 'swipebox-css', get_template_directory_uri() . '/inc/swipebox/src/css/swipebox.min.css', array(), '1.0', 'screen' );
  wp_enqueue_style( 'swipebox-css' );
  wp_register_style( 'stylesheet', get_template_directory_uri() . '/style.css', array(), '1.0', 'screen' );
  wp_enqueue_style( 'stylesheet' );
  wp_register_style( 'prism', get_template_directory_uri() . '/inc/prism/prism.css', array(), '1.0', 'screen' );
  wp_enqueue_style( 'prism' );
}
add_action( 'wp_enqueue_scripts', 'your_theme_css' );
/*
** Enqueueing swipbox script ad prism script only in the single.php template 
** To enqueue a script you must register it before in you funciton.php
*/

get_header();
wp_enqueue_script( 'swipebox-script' ); 
wp_enqueue_script( 'prism-script' );
//Passing PHP data to JavaScript
wp_enqueue_script( 'single-script' );
wp_localize_script( 'single-script', 'phpData', array(
  'featuredImage' => $featuredImage, 
  'titleColor' => get_post_meta($post->ID, 'titlecolor', true),
  'twitter' => $twitter,
  'excerpt' => get_the_excerpt(),
));
/*
** No conflits jQuery
*/

(function($)
{
  'use strict';
  var $window;
  var $width;
  var $height;
  var $titleSingle;

  /*----------------------------------------------
  INITIALIZE
  -----------------------------------------------*/ 
  $window = $(window); 
  $titleSingle = $(".title-single");

  $window.resize(function()
  {
    $width = $window.width();
    $height = $window.height();
    $titleSingle.css("top", $height / 2);
  });

  $(document).ready(function()
  {
    //here I put all my code
    $window.trigger('resize');
  }
})(jQuery);
6. Security

Every time you put online a website you unavoidably expose it to all kind of threatens. That’s why security is such a critical aspect of web developing. WordPress comes with a bunch of well-experimented tools to help you handling the two main security issues: data validation and sanitization.

Validation

For validation we mean all the tests and checks that your code must run in order to ensure that inserted data correspond to what your back-end expects. For instance, that an email is an email address for real (with an @ and a domain), that a date is a real date (33/14/-9123 and similar are not allowed) and so on.

Sanitization (or escaping)

For sanitization we mean all the filters applied to data to make them safe and trustable in a specific context. For instance, to display HTML code in a text area it would be necessary to replace all the HTML tags by their entity equivalents. Or when displaying the content of a PHP variable on a webpage it would be necessary to make JavaScript injections impossible.

A validation routine must be applied to all inputs, while a sanitization one to all outputs. To do that, it’s always better to recur to the dedicated WordPress functions.

Outputs Inputs

This is NOT an exhaustive list of the WordPress functions available for validation and sanitization. I'd recommend reading further on WordPress official codex pages. Anyway a general good rule is: when in doubt trust WordPress. In fact all hi-level WordPress functions embed a robust sanitization/validation routine.

SHARE BACK