If you will search for a counter of post or webpage views in WordPress, you definitely will find lots and lots of plug-ins. But I really didn’t want to browse through a pile of improvements, to have to find out which one is the most suitable for me and, as the case may be, count on the fact that I will not have to update it all the time.
So it struck me: what about to increment a value in a WordPress custom field? That could be it. Custom fields are a standard thing, I don’t have to install anything and I will simply list the posts arranged by traffic based on the value from the custom field using WP_Query.
Recording post traffic to a user field in WordPress
The whole concept of recording traffic to a post custom field is very simple. What we need:
- To find out whether the detail of a post is being viewed and doesn’t appear just somewhere in a listing
- To load a current value of the counter from a user field
- To increment the value by 1
- To save the new value
So we will prepare the edit by creating a new function in the file functions.php in the visual theme:
function counter() { if(is_single()) { global $post; $count = get_post_meta($post->ID, 'counter', true); $new = $count + 1; update_post_meta($post->ID, 'counter', $new); } }
Now we will just ask WordPress to activate the function when the page is being loaded. We will make that happen by adding another line to functions.php:
add_action('wp_head', 'counter');
Viewing of the post traffic counter
The counter has started to work. As soon as you open various posts multiple times, look at their editing. First, it will be necessary to turn on the viewing of user fields if you haven’t done so yet. Click on the button with three dots on the upper right and select Options. In a new bar tick Custom fields below and close the bar.
Move to the bottom of the editing page where you should be able to see user fields including our newly added counter.
Such tracking of the traffic isn’t very convenient. In fact, it is not convenient at all. I needed a view similar to the one I am used to in Drupal. That means the information about number of views of each post located somewhere near a publication date or a name of the author. How to do that in WordPress?
I use a visual theme built with the Underscores generator, so the corresponding file where the edit is needed is named template-parts/content.php. You basically need to find the file that controls the viewing of the post in the template. It is possible that you will find it directly in single.php.
With the function get_post_custom I can load information from user fields for a current post.
$get_meta = get_post_custom($post->ID);
In the place where I want the information from the traffic counter to be viewed I’ll just insert following:
if( current_user_can('administrator') ){ echo ' – <span class="hits-counter">Hits: '.$get_meta['counter'][0].'</span>'; }
With the condition I can confirm that at the moment a logged-in administrator is looking at the web. Because I don’t want the counter to be visible for common readers. With the echo function I will then write it to HTML tag span along with the x symbol written with HTML entity ×.
A task for you: now, when you know how to test whether the web administrator is looking at the post, try to add into the code increasing the number of views the condition that it will increase only when a common reader is looking, not the admin. You don’t want to distort the numbers by your own visits, do you?
Viewing of the counter in the post listing
Viewing of the counter near the post listing on a web is actually a nice thing but for an administrator or an operator of a website it would be better to have the information about the post traffic directly in their overview in the WordPress administration. Along with the option to arrange posts by a number of reads with a click into the heading of the overview, same as it is possible to do with an issue date.
To do this we have to add several other functions and filters into functions.php. First we use the filter manage_post_posts_columns, with which we will inform WordPress about the column added to the post listing in the administration.
function my_filter_posts_columns( $columns ) { $columns['counter'] = __( 'Hits' ); return $columns; } add_filter( 'manage_post_posts_columns', 'my_filter_posts_columns' );
With the following filter manage_post_posts_custom_column and the function called by it we will fill in the data for each post in the particular column. The function called by the filter uses loading of a value from a user field described above.
function my_post_column( $column, $post_id ) { if ( 'counter' === $column ) { echo get_post_meta($post_id, 'counter', true); } } add_action( 'manage_post_posts_custom_column', 'my_post_column', 10, 2);
The data from the counter should be being viewed at this moment and the option to arrange the post listing according to the new column remains to be added. WordPress for this purpose offers the filter manage_edit-post_sortable_columns.
function my_post_sortable_columns( $columns ) { $columns['counter'] = 'counter'; return $columns; } add_filter( 'manage_edit-post_sortable_columns', 'my_post_sortable_columns');
And finally the last part. The order is set but we still have to instruct WordPress how exactly should be the arranging done when setting the demand to arrange by a new column. In other words that we want to arrange by the number value in a user field for the counter.
function my_posts_orderby( $query ) { if( ! is_admin() || ! $query->is_main_query() ) { return; } if ( 'counter' === $query->get( 'orderby') ) { $query->set( 'orderby', 'meta_value' ); $query->set( 'meta_key', 'counter' ); $query->set( 'meta_type', 'numeric' ); } } add_action( 'pre_get_posts', 'my_posts_orderby' );
Now look at the WordPress administration and try out the arranging of a post by a value from the counter of reads.
Widget with a list of the most read posts
The last reason why I have created this simple traffic counter or rather counter of post views is a widget viewing a list of the most popular posts. A question occurred – how to set the data selection. Current month or year? What if it’s January 1st? Then there won’t be anything shown in the box.
So I used WP_Query which I call with the requirement of five posts arranged by user field in descending order but at the same time filtered out so that the final selection includes only max. one year old posts. Not from the last year but basically the ones posted 365 days ago at most.
I have built the widget with a class placed once again into the file functions.php. In the arguments for WP_Query which is essential here you can see date_query for one year backwards. You can use other filter string that PHP understands. For example by entering -1 month you will ensure viewing of the most popular posts issued during the last month.
class my_popular_posts_widget extends WP_Widget { function __construct() { parent::__construct( // Base ID of your widget 'websitename_popular_posts_widget', // Widget name will appear in UI __('Most popular posts'), // Widget description array( 'description' => __( 'Most popular posts for the last year'), ) ); } // Creating widget front-end public function widget( $args, $instance ) { $title = apply_filters( 'widget_title', $instance['title'] ); // before and after widget arguments are defined by themes echo $args['before_widget']; if ( ! empty( $title ) ) echo $args['before_title'] . $title . $args['after_title']; // This is where you run the code and display the output $args = array( 'post_status' => 'publish', 'posts_per_page' => 5, 'post_type' => 'post', 'meta_key' => 'counter', 'orderby' => 'meta_value_num', 'order' => 'DESC', 'date_query' => array( array( 'after' => '-1 year', 'column' => 'post_date', ), ), ); $the_query = new WP_Query( $args ); print '<div class="recent">'; while ($the_query -> have_posts()) : $the_query -> the_post(); ?> <div bp="grid"> <div bp="4" class="thumb"><a href="<?php print get_permalink(); ?>"><?php print get_the_post_thumbnail(null,'thumbnail'); ?></a></div> <div bp="8"><a href="<?php print get_permalink(); ?>"><?php print get_the_title(); ?></a></div> </div> <?php endwhile; wp_reset_postdata(); print '</div>'; echo $args['after_widget']; } // Widget Backend public function form( $instance ) { if ( isset( $instance[ 'title' ] ) ) { $title = $instance[ 'title' ]; } else { $title = __( 'Popular posts' ); } // Widget admin form ?> <p> <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label> <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" /> </p> <?php } // Updating widget replacing old instances with new public function update( $new_instance, $old_instance ) { $instance = array(); $instance['title'] = ( ! empty( $new_instance['title'] ) ) ? strip_tags( $new_instance['title'] ) : ''; return $instance; } } // Class wpb_widget ends here
For the widget to be viewed in the administration section Appearance > Widgets and to start to run in WordPress, you also have to call the filter with which you will let know WordPress about a new filter:
function my_load_widget() { register_widget( 'my_popular_posts_widget' ); } add_action( 'widgets_init', 'my_load_widget' );
And that’s all. With no need for a plug-in we have built a simple traffic counter which uses only the standard functions accessible right in the basic WordPress installation. Of course, it is not sophisticated at all, it just increases the number by “one” with every reading. But for this purpose it is completely sufficient, I think. We don’t need to substitute advanced statistics when there are Google Analytics or logs from the server available to us.
Sources: