Home cover development

Most of the home code is located in the index.php. The index file (with the extensions php, html, etc depending on the platform) is usually used as the entry point of a site or web application. From a technical point of view, inserting a lot of code in the index isn’t exactly the neatest thing. However, in this case I chose to develop the feature as fast as could. So some code I could have separated is in there.

Featured posts or cover

The objective is to implement a dynamic section that can contain from one to eight posts. These posts will be called featured posts/articles within the code. Also, I want to be able to highlight an article even more than the others (this is what I’ll call Main Featured Post).
To develop this I can think of two alternative approaches:

  1. Use meta values to save custom options during post edition.
  2. Implement this section as a widget area and place posts as widgets in it.

Truthfully I don’t seriously consider option two. It seems to me that I would have to implement several classes, and I want to speed up the development as much as possible to deploy a version of the page quickly. My idea is to simply implement a few checkboxes on the posts edition page. These controls will save a value (1 or 0) in the WordPress posts_meta table.

Adding options on the posts page.

An option to enable the regular featured posts.

Another one to enable a main featured post.

The code to implement both features should be added in the functions.php file. First, we need to declare the container box of each option using the add_meta_box function:

<?php
function register_post_assets(){
    add_meta_box('featured-post', __('Featured Post'), 'add_featured_meta_box', 'post', 'advanced', 'high');
    add_meta_box('main-post', __('Main Featured Post (Only one at a Time)'), 'add_main_meta_box', 'post', 'advanced', 'high');
}
add_action('admin_init', 'register_post_assets', 1);

add_meta_box key parameters

  1. Option ID.
  2. Box name.
  3. Callback function (where the box contents are set).
  4. Admin Screen (where it’ll be shown)

Admin_init es un hook que se ejecuta al principio cuando el usuario entra en el admin de WordPress.

Ten we set the boxex internal contents:

<?php
function add_featured_meta_box($post){
    $featured = get_post_meta($post->ID, '_featured-post', true);
    echo "<label for='_featured-post'>".__('Featured Post&nbsp;&nbsp;&nbsp;', 'foobar')."</label>";
    echo "<input type='checkbox' name='_featured-post' id='featured-post' value='1' ".($featured == 1 ? 'checked': '')."  />";
}
       
function add_main_meta_box($post){
    $main_featured = get_post_meta($post->ID, '_main-post', true);
    echo "<label for='_main-post'>".__('Main Featured Post (Only one at a Time)&nbsp;&nbsp;&nbsp;', 'foobar')."</label>";
    echo "<input type='checkbox' name='_main-post' id='main-post' value='1' ".($main_featured == 1 ? 'checked': '')."  />";
}

Finally, we must implement the code to save the meta option value of in the table. For that we can use the save_post hook that’s triggered when a post is saved.

<?php
function save_all_meta($post_id){
    // Do validation here for post_type, nonces, autosave, etc...
    if (isset($_REQUEST['_featured-post']))
        update_post_meta(esc_attr($post_id), '_featured-post', esc_attr($_REQUEST['_featured-post']));
    else
        update_post_meta(esc_attr($post_id), '_featured-post', esc_attr(0));
    if (isset($_REQUEST['_main-post']))
    {
        $args = array(
            'meta_key' => '_main-post',
            'meta_value' => 1
        );

        $main_featured = new WP_Query($args);

        if ($main_featured->have_posts()):
            while($main_featured->have_posts()):
                $main_featured->the_post();
                update_post_meta(esc_attr(get_the_ID()), '_main-post', esc_attr(0));
            endwhile;
        endif;
               
        update_post_meta(esc_attr($post_id), '_main-post', esc_attr($_REQUEST['_main-post']));
    }
    else
    {
        update_post_meta(esc_attr($post_id), '_main-post', esc_attr(0));
    }
           
}
add_action('save_post', 'save_all_meta');

The first part of the code, which deals with the regular featured posts, is pretty straightforward: if the checkbox is selected, the value 1 will be saved if not 0 will be saved. However, the main featured post code is a little bit more complex. If the checkbox isn’t selected it’s like a regular featured post. However, since there can only be one featured post at the same time, we need to make sure that only the post that we’re saving will have that option with the value 1. Therefore, first we look into the database, if any other record has that option we have to update the value in that post to 0.

Implement meta options on the cover of home.
Let’s start with the index.php. After establishing that we are in the front page, the featured posts are obtained from the database.

<?php
if ( is_front_page() ) :
               
    $args = array(
        'meta_key' => '_main-post',
        'meta_value' => 1
    );

    $main_featured = new WP_Query($args);
               
    $there_is_main = false;
    if ($main_featured->have_posts()):
        $there_is_main = true;
        $main_featured->the_post();
        $main_post = get_post();
        $main_post_id = $main_post->ID;
    endif;
               
    if ($there_is_main)
    {
        $args = array(
            'meta_key' => '_featured-post',
            'meta_value' => 1,
            'post__not_in' => array($main_post_id)
        );
    }
    else
    {
        $args = array(
            'meta_key' => '_featured-post',
            'meta_value' => 1,
        );
    }

    $featured = new WP_Query($args);
    $featured_post_count = 0;
    if ($featured->have_posts()):
        $featured_post_count = $featured->post_count;
    endif;
    if ($there_is_main || $featured_post_count > 0)
    {
        ?>
            <div class="row border-between mt-4 bottom-line justify-content-center">

In this “if”, we are asking if there is any kind of featured post. If there is one, we create the cover section.

<?php
                   
$featured_post_weight_count = 0;
$two_row_policy = false;
if ($there_is_main || (!$there_is_main && $featured_post_count == 1) )
{
    $featured_post_weight_count = 2;
    if (!$there_is_main)
    {
        $featured->the_post();
        $main_post = get_post();
        $featured_post_count--;
    }
                       
    $article = new MainFeaturedArticle();
    $article->setPost($main_post);
    $article->printHTML();
    $two_row_policy = true;
}

If there is a single featured post then we’ll treat it as if it were a main featured post, even though it doesn’t have to formally be a main featured post. The class is defined in inc/featured-article.php. The class name is main featured article instead of post because I didn’t realize it at the time. Anyone can make a mistake or two while speed coding.

<?php
class MainFeaturedArticle extends FeaturedArticle{
    public function __construct()
    {
    }
   
    public function printHTML()
    {
        ?>
        <div class="col-md-4 mb-4">
       
        <div class="card border-0 mr-2 text-center">
       
            <div class="pic card border-0">
            <?php
            echo '<a href="'.get_permalink($this->wp_post).'">';
            ?><img class="card-img-top img-fluid img-overlayed" src="<?php echo get_the_post_thumbnail_url($this->wp_post, 'medium_large')?>">
            <div class="overlay"></div>
            </a>
            </div>
       
            <div class="card-block px-0">
               
                <p class="card-category-text"><a href="<?php $category_name = get_the_category( $this->wp_post->ID )[0]->name; $category_id = get_cat_ID( $category_name ); echo esc_url( get_category_link( $category_id ) ); ?>"><?php echo $category_name; ?></a></p>
                <h3 class="card-title card-title-text big-card-title-text"><a href="<?php echo get_permalink($this->wp_post); ?>"><?php echo apply_filters( 'post_title', $this->wp_post->post_title ); ?></a></h3>
                <p class="card-category-author"><a href="/contact-page/"><?php echo get_the_author_meta('user_firstname', $this->wp_post->post_author)." ".get_the_author_meta('user_lastname', $this->wp_post->post_author); ?></a></p>
            </div>
        </div>
        </div>
        <?php
    }
}

Being a child class of FeaturedArticle, the code is quite limited. It only has a printHTML function. This is the function that’s responsible for printing HTML.

<?php
if ($featured_post_count > (8 - $featured_post_weight_count))
{
    $featured_post_count = (8 - $featured_post_weight_count);
}
                       
if ($featured_post_count > 4)
{
    $two_row_policy = true;
}

The number of rows and columns are going to dynamically depend on the items quantity the cover had. I decided the following rules:

  1. If there is a main featured post, I’ll only allow a maximum of 6 regular featured posts on the cover.
  2. The main featured post size will double the rest.
  3. Usually, we tend to pay more attention to what we see in the upper-left quadrant, so the main one will be located in that position.
  4. The rest of the posts will be distributed in two rows. The number of columns will depend on the number of featured posts. There may be from 1 column to 3.
  5. In case there is an odd number of regular featured posts. One of them will double the height of the rest (like the main featured one), but it’ll have the same width. The extra space will be used to show a post extract.
  6. For covers without main featured post we’ll allow a maximum of 8 posts.
  7. If there are from 1 to 4 featured posts, they’ll be displayed in a single row.
  8. If there are more than 4, it has the same policy as if there was a main featured post (they will be distributed in 2 rows).


<?php
$featured_post_count_even = ($featured_post_count % 2) == 0;
for ($i = 0; $i < $featured_post_count; $i++)
{
    $article = new FeaturedArticle();
    $featured->the_post();
    $current_post = get_post();

Beginning of the featured posts loop.

  1. We check if there is an odd or even number of posts . That changes how each post’s properties are configured.
  2. An object FeaturedArticle is created.
  3. We get the actual WordPress post.

FeaturedArticle class code:

<?php
class FeaturedArticle {
    protected $width = 12;
    protected $bottom_line = false;
    protected $margin_top = 0;
    protected $multi_line = false;
    protected $mini_summary = false;
    protected $wp_post;
    protected $float_right = false;
    protected $float_left = false;
   
    public function __construct()
    {
    }
   
    public function setMultiline($multi_line)
    {
        $this->multi_line = $multi_line;
    }
   
    public function getMiniSummary()
    {
        return $this->mini_summary;
    }
   
    public function setMiniSummary($mini_summary)
    {
        $this->mini_summary = $mini_summary;
    }
   
    public function getBottomLine()
    {
        return $this->bottom_line;
    }
   
    public function setBottomLine($bottom_line)
    {
        $this->bottom_line = $bottom_line;
    }
   
    public function setMarginTop($margin_top)
    {
        $this->margin_top = $margin_top;
    }
   
   
    public function setPost($post)
    {
        $this->wp_post = $post;
    }
   
    public function setRight($float)
    {
        $this->float_right = $float;
    }
   
    public function setLeft($float)
    {
        $this->float_left = $float;
    }
   
    public function printHTML()
    {
        if ($this->multi_line)
        {
        ?>
            <div class="row<?php if ($this->bottom_line){ echo ' bottom-line';} if ($this->margin_top > 0) { echo ' mt-'.$this->margin_top;}?>">
            <div class="col-md-12">
        <?php
        }
       
        echo '<div class="card border-0'.($this->float_right ? " float-right" : "").($this->float_left ? " float-left" : "").'">';
        ?>
            <div class="pic card border-0">
            <?php
            echo '<a href="'.get_permalink($this->wp_post).'">';
            ?><img class="card-img-top img-fluid img-overlayed" src="<?php echo get_the_post_thumbnail_url($this->wp_post, 'medium')?>">
            <div class="overlay"></div>
            </a>
            </div>
       
            <div class="card-block px-0">
                <h3 class="card-title card-title-text"><a href="<?php echo get_permalink($this->wp_post); ?>"><?php echo apply_filters( 'post_title', $this->wp_post->post_title ); ?></a></h3>
                <?php
                if ($this->mini_summary)
                {
                ?>
                    <p class="card-category-excerpt"><a href="<?php echo get_permalink($this->wp_post); ?>"><?php echo get_the_excerpt($this->wp_post); ?></a></p>
                <?php
                }
                ?>
                <p class="card-category-author"><a href="/contact-page/"><?php echo get_the_author_meta('user_firstname', $this->wp_post->post_author)." ".get_the_author_meta('user_lastname', $this->wp_post->post_author); ?></a></p>
            </div>
        </div>
        <?php
        if ($this->multi_line)
        {
        ?>
            </div>
            </div>
        <?php
        }
    }
}

Most of the methods are property getters or setters. However, compared to the one in MainFeaturedArticle, this printHTML function has several differences:

  1. The multi_line property, enables the definition of an extra row before printing the Bootstrap card.
  2. The mini_summary property, defines if an extract of the article is going to be printed.

Continuing with the featured post loop code:

<?php
if ($two_row_policy)
{
    $article->setMultiline(true);
    $index_even = ($i % 2) == 0;
    if ($index_even == $featured_post_count_even)
    {
        $article->setBottomLine(true);
    }
    else
    {
        if ($i > 0)
        {
            $article->setMarginTop(4);
        }
        else
        {
            $article->setMultiline(false);
            $article->setMiniSummary(true);
        }
    }
}
                       
$article->setPost($current_post);
if (!$two_row_policy || $article->getBottomLine() || $article->getMiniSummary())
{                          
    echo '<div class="col-md-2 mb-4">';
}
                       
$article->printHTML();
if (!$two_row_policy || !$article->getBottomLine() || $article->getMiniSummary())
{
    echo '</div>';
}
  1. By the rules four or eight, when the featured posts distribution policy is of two rows, the Multiline property is set to all regular featured posts, excepting when there is an odd number of posts. In that case, the first one occupies a height of two rows and has a post extract (set with $article->setMiniSummary(true);).
  2. In case the post is the on the top row, a lower horizontal separator row is created.
  3. If the post is the on the bottom row, a top margin is added.
  4. The WordPress post is assigned to the properties.
  5. If there is no two-row policy each item has its own assigned column.
  6. HTML is printed.
  7. The loop restarts or ends.