Create WordPress theme from scratch : Part 7 – Index, Archive and Search

Welcome to Part 7 of the series Create WordPress theme from scratch. In this tutorial, we will assemble index.php, archive.php, search.php template files.

If you missed the Part 6 of this series, then check it out at the link below.

Create WordPress theme from scratch – Part 6 – The Loop

Getting started

Now that we have figured out the basics and the Loop, we are ready to bring our theme to life. We will be creating our theme based on Bootstrap. We can use Bootstrap directly from the CDN.

Modify the functions file and add the following lines of code.

function mytheme_scripts() {
    wp_enqueue_style( 'mytheme-bootstrap', 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css' );
    wp_enqueue_style( 'mytheme-google-fonts', 'https://fonts.googleapis.com/css?family=Slabo+13px' );
    wp_enqueue_style( 'mytheme-style', get_stylesheet_uri() );
}
add_action( 'wp_enqueue_scripts', 'mytheme_scripts' );

To begin with, we are adding the Bootstrap CSS library. While we are at it, we have also added Google fonts. Finally, we are adding our theme CSS file. Loading theme CSS file after all other CSS libraries makes overriding CSS styles easier.

Template index.php

We already have our header, sidebar, searchform and footer ready. We will start with the core template file index.php. Create a file called index.php in your theme directory.

Place the following lines of code in your index.php file.

<?php get_header(); ?>

<div class="container">
    <div class="row">

        <div class="col-sm-8 content-area">
            <main class="site-main">
                <?php if ( have_posts() ) : ?>
                    <?php while ( have_posts() ) : the_post(); ?>
                        <?php get_template_part( 'template-parts/content' ); ?>
                    <?php endwhile; ?>
                    <?php the_posts_navigation(); ?>
                <?php else : ?>
                    <p><?php _e( 'Sorry, no posts found!', 'mytheme' ); ?></p>
                <?php endif; ?>
            </main>
        </div><!-- .col-sm-8 -->

        <div class="col-sm-4">
            <?php get_sidebar(); ?>
        </div><!-- .col-sm-4 -->

    </div><!-- .row -->
</div><!-- .container -->

<?php get_footer(); ?>

We are using pretty simple markup. First, we need our header. We have already assembled our header template earlier in the series. We are retrieving it using a call to get_header function.

Template file header.php code

<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
    <meta charset="<?php bloginfo( 'charset' ); ?>">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="profile" href="http://gmpg.org/xfn/11">
    <link rel="pingback" href="<?php bloginfo( 'pingback_url' ); ?>">
    <?php wp_head(); ?>
</head>

<body <?php body_class(); ?>>
    <div class="site">

        <header class="site-header">
            <nav class="site-navigation">
                <?php wp_nav_menu( array( 'theme_location' => 'primary', 'container_class' => 'container', 'menu_class' => 'primary-menu' ) ); ?>
            </nav>

            <div class="site-branding">
                <div class="container">
                    <p class="site-title"><a href="<?php echo esc_url( home_url( '/' ) ); ?>" rel="home"><?php bloginfo( 'name' ); ?></a></p>
                    <?php if ( $description = get_bloginfo( 'description' ) ) : ?>
                        <p class="site-description"><?php echo $description; ?></p>
                    <?php endif; ?>
                </div>
            </div>

            <div class="container">
                <?php get_search_form(); ?>
            </div>
        </header><!-- .site-header -->

        <div class="site-content">

Back to index.php template file, we are using the Loop to display our content.

<?php while ( have_posts() ) : the_post(); ?>
    <?php get_template_part( 'template-parts/content' ); ?>
<?php endwhile; ?>

Our original post content is inside a partial template file located in a folder named template-parts. We are displaying our partial template file using the function get_template_part.

Create a new directory named template-parts in your theme directory. Now create a file named content.php in the newly created directory and paste the following lines of code in it.

<article id="entry-<?php the_id(); ?>" <?php post_class( 'entry' ); ?>>
    <header class="entry-header">
        <?php if ( is_sticky() && is_home() ) : ?>
            <span class="sticky-tag"><?php _e( 'Sticky', 'mytheme' ); ?></span>
        <?php endif; ?>
        <h2 class="entry-title">
            <a href="<?php echo esc_attr( get_the_permalink() ); ?>" rel="bookmark"><?php the_title(); ?></a>
        </h2>
    </header>
    <footer class="entry-meta">
        <?php entry_meta(); ?>
    </footer>
    <div class="entry-summary">
        <?php the_excerpt(); ?>
    </div>
    <?php the_tags( '<div class="entry-tags">' . __( 'Tags: ', 'mytheme' ), __( ', ', 'mytheme' ), '</div>' ); ?>
</article>

To start with, we are giving each entry an unique id with the help of the_id template tag. We are also using post_class template tag so that other plugins can add additional CSS class names they need. We are using the post_class tag to add our own class called entry.

The post_class tag adds a special class called hentry. When I was a newbie, it felt like junk but It turns out I was wrong – how obvious! The hentry CSS class indicates the presence of microformat data.  Read on…

What is a Microformat?

Simply put, a microformat is an enhancement of semantic markup (meaningful markup) that uses HTML/XHTML tags to provide extra information about the content of a weblog such as a news article.

This means the content is easier to identify. This is certainly useful for search engines and just to let you know, Google understands microformat data.

What is hAtom?

hAtom is a microformat. Any existing blog can be easily modified to add support for hAtom by using hentry – a structure of organizing hAtom data. All we need to do is add some class names to our markup.

The root of an hAtom entry is identified by the class hentry.  Listed below are the CSS class names that are used to organize data.

  • entry-title – This is required and represents the title of an entry.
  • author – This is required and must be in the hCard format. hCard itself is based on vCard and is often used to represent people or organizations. Check the hCard page for usage examples.
  • published – This is optional and represents the time when the entry was created or first made available.
  • updated – This is required and represents the time when the entry was modified. When updated element is not available then the published element is used.
  • entry-content – This is optional and represents the content of an entry. An entry may have zero or more entry-content elements.
  • entry-summary – This is optional and represents the summary of an entry. An entry may have zero or more entry-summary elements.
  • bookmark- This is optional and represents the permalink to the entry. This is indicated using rel-bookmark.
  • tags – This is optional and represents keywords or phrases. This is indicated using rel-tag. WordPress will automatically take care of this if you use built-in template tags to display tags and categories.

Take caution

I am not sure whether using microformat will increase your SEO or not. What I do know is that you will end up with a whole bunch of errors in your Google Search Console if you do it the wrong way. Either completely remove the microformat (hentry class) or add all the required markup to your code.

Back to content.php

Back to our partial template file content.php, we find ourselves working on the entry header. Sticky posts are a core feature of WordPress and every theme should include something that makes sticky posts stand out from the rest.

We are adding a small tag to mark the post as sticky. Using the conditional tag is_home we make sure that the tag is added only when the blog posts index page is being displayed.

<?php if ( is_sticky() && is_home() ) : ?>
    <span class="sticky-tag"><?php _e( 'Sticky', 'mytheme' ); ?></span>
<?php endif; ?>

Next, we are adding the title with a permalink. By using esc_attr function we make sure that we escape all characters that may break our markup.

<h2 class="entry-title">
    <a href="<?php echo esc_attr( get_the_permalink() ); ?>" rel="bookmark"><?php the_title(); ?></a>
</h2>

By setting the rel attribute of the link, we are specifying the permalink to the entry in hentry recognizable syntax.

After that, we are displaying the entry meta information using our own template tag.

<footer class="entry-meta">
    <?php entry_meta(); ?>
</footer>

All custom template tags are best placed in a separate file, which is normally called template-tags.php and placed in a directory named inc.

Go ahead, create a directory named inc in the theme directory and create a file named template-tags.php in the newly created directory. Paste the following lines of code containing our custom template tag in the newly created file.

<?php

if ( ! function_exists( 'entry_meta' ) ) {

    function entry_meta() {
        $author_string = sprintf(
            esc_html_x( 'by %s', 'post author', 'mytheme' ),
            '<span class="author vcard"><a class="url fn" href="' . esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ) . '">' . esc_html( get_the_author() ) . '</a></span>'
        );
        ?>
        <time class="entry-date published updated" datetime="<?php the_time( 'c' ) ?>"><?php printf( esc_html__( '%s', 'mytheme' ), 'Posted on ' . get_the_date() ); ?></time>
        <span class="entry-author"><?php echo $author_string; ?></span>
        <span class="entry-category"><?php _e( ' in ', 'mytheme' ); the_category( __( ', ', 'mytheme' ) ) ?></span>
        <?php
        edit_post_link(
            sprintf(
                esc_html__( 'Edit %s', 'mytheme' ),
                the_title( '<span class="screen-reader-text">"', '"</span>', false )
            ),
            '<span class="edit-link">',
            '</span>'
        );
    }

}

I have intentionally simplified the twentysixteen custom meta information template tag above. Let’s break down our code.

We are creating a pluggable template tag so that a child theme can easily override it. Inside the real function itself, we are grabbing the author information. The author information is displayed in a valid hCard markup.

$author_string = sprintf(
    esc_html_x( 'by %s', 'post author', 'mytheme' ),
    '<span class="author vcard"><a class="url fn" href="' . esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ) . '">' . esc_html( get_the_author() ) . '</a></span>'
);

All text on the front end should translatable. To make sure that author name remains translatable we are using the following translation function

esc_html_x( $text, $context, $domain )

Translates a text and offers a hint for the translator.

  • $text – The text to translate.
  • $context – Comment describing what $text means to help out the friendly translator.
  • $domain – Translation domain.

All translation functions accept raw text only. If you have a variable or the text is getting retrieved from a function, then depending on what you are trying to do, you have the following options:

  • Use sprintf to substitute variables if you want to use the text in PHP.
  • Use printf to substitute variables if you want to display the text.

In either case, you must use a non-echo version of translation function. A great article on WordPress translation functions is available here.

To display the author information, the have this line of code

<span class="entry-author"><?php echo $author_string; ?></span>

We display the date of the entry with the following piece of code

<time class="entry-date published updated" datetime="<?php the_time( 'c' ) ?>"><?php printf( esc_html__( '%s', 'mytheme' ), 'Posted on ' . get_the_date() ); ?></time>

get_the_date( $format, $post_id )

Returns date when the post was first published. It takes the following arguments:

  • $format – A PHP date format string which defaults to the date format set in the Dashboard > Settings > General.
  • $post_id – ID of the post to use. Defaults to the current post ID.

We are also using esc_html__ to make our time information translation ready and safe for HTML display.

I have over-simplified the above date code. If you want to display the date/time when the post was updated, then you will need to alter the code. Here is some code from _s template tags.

$time_string = '<time class="entry-date published updated" datetime="%1$s">%2$s</time>';
if ( get_the_time( 'U' ) !== get_the_modified_time( 'U' ) ) {
    $time_string = '<time class="entry-date published" datetime="%1$s">%2$s</time><time class="updated" datetime="%3$s">%4$s</time>';
}

$time_string = sprintf( $time_string,
    esc_attr( get_the_date( 'c' ) ),
    esc_html( get_the_date() ),
    esc_attr( get_the_modified_date( 'c' ) ),
    esc_html( get_the_modified_date() )
);

$posted_on = sprintf(
    esc_html_x( 'Posted on %s', 'post date', 'mytheme' ),
    '<a href="' . esc_url( get_permalink() ) . '" rel="bookmark">' . $time_string . '</a>'
);

The above code compares the post publish date with the post update date. If they don’t match, then the entry was updated. In such case, it displays both the publish and the update time. Hide any one you like using CSS styles.

You may be wondering why the time string is in an anchor tag. Well, that’s because it’s the safest way to make sure that you always have a permalink available to the entry. Some posts may not contain a title or a featured image or content. But every post has a publish time.

After that, we are displaying the category a post belongs to. This is easily done with the help of built-in template tag the_category.

<span class="entry-category"><?php _e( ' in ', 'mytheme' ); the_category( __( ', ', 'mytheme' ) ); ?></span>

Finally, we are displaying a link to edit the post when a user with the ability to edit the post is logged in. Here is the code we are using (taken from _s template)

edit_post_link(
    sprintf(
        esc_html__( 'Edit %s', 'mytheme' ),
        the_title( '<span class="screen-reader-text">"', '"</span>', false )
    ),
    '<span class="edit-link">',
    '</span>'
);

That sums up our little template tag. We are almost done with our content.php file too. Given below is the final part of content.php

<div class="entry-summary">
    <?php the_excerpt(); ?>
</div>
<?php the_tags( '<div class="entry-tags">' . __( 'Tags: ', 'mytheme' ), __( ', ', 'mytheme' ), '</div>' ); ?>

We are showing the excerpt using  the_excerpt template tag and displaying the tags using the_tags template tag.

Now, back to our index.php file. At the end of the Loop, we are displaying navigation links using the_posts_navigation.

<?php the_posts_navigation(); ?>

If you have lots of posts in your site, you may want to display a pagination instead using template tag the_posts_pagination.

<?php the_posts_pagination(); ?>

If there are no posts to display, we are printing out a notification using translation ready function.

<p><?php _e( 'Sorry, no posts found!', 'mytheme' ); ?></p>

That takes care of our main content. Now, we need to display our sidebar. Remember, earlier in the series, we created our sidebar template file. We are just grabbing it right now.

<?php get_sidebar(); ?>

Template file sidebar.php code

<?php
if ( ! is_active_sidebar( 'sidebar-1' ) ) {
    return;
}
?>

<aside class="widget-area">
    <?php dynamic_sidebar( 'sidebar-1' ); ?>
</aside><!-- .widget-area -->

Finally, we only have the footer left to display. Earlier in the series, we prepared that one too. We are just grabbing the template now.

get_footer();

Template file footer.php code

</div><!-- .site-content -->

<footer class="site-footer">
    <div class="container">
        <div class="site-info">
            <p><?php printf( __( '%1$s &copy; %2$s', 'mytheme' ), get_bloginfo( 'title' ), date( 'Y' ) ); ?></p>
        </div>
    </div>
</footer><!-- .site-footer -->

</div><!-- .site -->

<?php wp_footer(); ?>

</body>
</html>

We have already seen how the footer template works earlier in the series. So, that would sum up our index.php.

Template archive.php

The archive template file is almost similar to the index template file. Here is the code.

<?php get_header(); ?>

<div class="container">

    <header class="page-header">
        <?php
        the_archive_title( '<h1 class="page-title">', '</h1>' );
        the_archive_description( '<div class="taxonomy-description">', '</div>' );
        ?>
    </header>

    <div class="row">

        <div class="col-sm-8 content-area">
            <main class="site-main">
                <?php if ( have_posts() ) : ?>
                    <?php while ( have_posts() ) : the_post(); ?>
                        <?php get_template_part( 'template-parts/content' ); ?>
                    <?php endwhile; ?>
                    <?php the_posts_navigation(); ?>
                <?php else : ?>
                    <p><?php _e( 'Sorry, no posts found!', 'mytheme' ); ?></p>
                <?php endif; ?>
            </main>
        </div><!-- .col-sm-8 -->

        <div class="col-sm-4">
            <?php get_sidebar(); ?>
        </div><!-- .col-sm-4 -->

    </div><!-- .row -->
</div><!-- .container -->

<?php get_footer();

That looks almost identical to our index template file. The only difference is that we have added a page header.

<header class="page-header">
    <?php
    the_archive_title( '<h1 class="page-title">', '</h1>' );
    the_archive_description( '<div class="taxonomy-description">', '</div>' );
    ?>
</header>

Let’s look at the template tags we have added to assemble the page header.

the_archive_title( $before, $after )

the_archive_description( $before, $after )

Both are relatively new template tags added in WordPress version 4.1.  Both accept the following arguments

  • $before – Content to display before the title
  • $after – Content to display after the title

The tag the_archive_title displays the archive title based on the query. This single tag works for all author, category, tag, custom taxonomy and date archives.

The tag the_archive_description displays category, tag or term description.

Template search.php

The search template is almost similar to the archive template. Here is the code

<?php get_header(); ?>

<div class="container">

    <header class="page-header">
        <h1 class="page-title"><?php printf( __( 'Search Results for: %s', 'mytheme' ), '<span>' . esc_html( get_search_query() ) . '</span>' ); ?></h1>
    </header>

    <div class="row">

        <div class="col-sm-8 content-area">
            <main class="site-main">
                <?php if ( have_posts() ) : ?>
                    <?php while ( have_posts() ) : the_post(); ?>
                        <?php get_template_part( 'template-parts/content' ); ?>
                    <?php endwhile; ?>
                    <?php the_posts_navigation(); ?>
                <?php else : ?>
                    <p><?php _e( 'Sorry, no posts found!', 'mytheme' ); ?></p>
                <?php endif; ?>
            </main>
        </div><!-- .col-sm-8 -->

        <div class="col-sm-4">
            <?php get_sidebar(); ?>
        </div><!-- .col-sm-4 -->

    </div><!-- .row -->
</div><!-- .container -->

<?php get_footer();

The only difference is that we have changed our page header.

<header class="page-header">
    <h1 class="page-title"><?php printf( __( 'Search Results for: %s', 'mytheme' ), '<span>' . esc_html( get_search_query() ) . '</span>' ); ?></h1>
</header>

We are using yet another new template tag

get_search_query()

Returns the query string when a user performs a search. This function returns the query string after passing it through esc_attr function. This means that the value is safe to use in an attribute.

Conclusion

This concludes the seventh part of the series Create WordPress theme from scratch. In the next part of the series, we will create a single post and single page templates.

Multiple Gmail Accounts

Next Article

How to use Multiple Gmail Accounts with Gmail Delegaion