I m trying make a code that list the popular posts by week. Until now i was able to creat a veiws counter and display the 4 popular posts. So what i still need is to make this query more accurate. I would like get the thumbnails and links and get the most popular of the week.
Functions.php
remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0);
function wpb_track_post_views ($post_id) {
if ( !is_single() ) return;
if ( empty ( $post_id) ) {
global $post;
$post_id = $post->ID;
}
wpb_set_post_views($post_id);
}
add_action( 'wp_head', 'wpb_track_post_views');
function wpb_get_post_views($postID){
$count_key = 'wpb_post_views_count';
$count = get_post_meta($postID, $count_key, true);
if($count==''){
delete_post_meta($postID, $count_key);
add_post_meta($postID, $count_key, '0');
return "0 View";
}
return $count.' Views';
}
Single.php
wpb_set_post_views(get_the_ID());
<?php echo wpb_get_post_views(get_the_ID()); ?>
// show 4 most popular posts
<?php
$popularpost = new WP_Query( array(
'posts_per_page' => 4,
'meta_key' => 'wpb_post_views_count',
'orderby' => 'meta_value_num',
'order' => 'DESC'
) );
while ( $popularpost->have_posts() ) : $popularpost->the_post();
the_title();
endwhile;
?>
I m trying make a code that list the popular posts by week. Until now i was able to creat a veiws counter and display the 4 popular posts. So what i still need is to make this query more accurate. I would like get the thumbnails and links and get the most popular of the week.
Functions.php
remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0);
function wpb_track_post_views ($post_id) {
if ( !is_single() ) return;
if ( empty ( $post_id) ) {
global $post;
$post_id = $post->ID;
}
wpb_set_post_views($post_id);
}
add_action( 'wp_head', 'wpb_track_post_views');
function wpb_get_post_views($postID){
$count_key = 'wpb_post_views_count';
$count = get_post_meta($postID, $count_key, true);
if($count==''){
delete_post_meta($postID, $count_key);
add_post_meta($postID, $count_key, '0');
return "0 View";
}
return $count.' Views';
}
Single.php
wpb_set_post_views(get_the_ID());
<?php echo wpb_get_post_views(get_the_ID()); ?>
// show 4 most popular posts
<?php
$popularpost = new WP_Query( array(
'posts_per_page' => 4,
'meta_key' => 'wpb_post_views_count',
'orderby' => 'meta_value_num',
'order' => 'DESC'
) );
while ( $popularpost->have_posts() ) : $popularpost->the_post();
the_title();
endwhile;
?>
Share
Improve this question
edited Jan 27, 2015 at 22:35
Howdy_McGee♦
20.9k24 gold badges91 silver badges177 bronze badges
asked Jan 27, 2015 at 21:49
DolomatsDolomats
1772 gold badges3 silver badges9 bronze badges
2
- Just so it's clear, you're currently getting the 4 most popular posts ever. What you would like to do now is get the 4 most popular for the current week, get the thumbnails and links of these 4 posts. Does that sound right? – Howdy_McGee ♦ Commented Jan 27, 2015 at 22:37
- @Dolomats Link to the source of the code please. – Brad Dalton Commented Jan 28, 2015 at 10:53
2 Answers
Reset to default 2Currently, I see no way that you can do that as you have no good reference to pull date based statistics.
Notice: You are storing post views as follows:
add_post_meta($postID, $count_key, '0');
To be able to grab "Most viewed in period", you would have to do something more complex, probably with an extra database table. Right now, you have no relation between the number of views and any time period.
For example, if you created the following table in your database:
{$prefix} should use the wordpress database table prefix (e.g. wp_ )
CREATE TABLE {$prefix}post_views (
post_id bigint(20) NOT NULL,
entry_day datetime NOT NULL,
num_views int NOT NULL,
PRIMARY KEY (post_id,entry_day),
KEY idx_pv_ed (entry_day)
);
With that table, you can now track highest posts in a period.
Here is a revised version written as a class with logging, error handling, and an example usage script to show what is going on.
The Handler Class
The below code is a small class that includes debugging information so that you can see where any problems are.
I've sprinkled it with notes to explain what is going on
class My_Most_Viewed_Posts {
protected $errors = null;
protected $notices = null;
protected $debug = false;
# This just sets up the errors, notices, and debug variables able
function __construct($debug = false) {
$this->clear_all();
if ( $debug ) {
$this->debug = true;
}
}
# This lets you add notices about what is going on so you can display them later
protected function add_notice($msg = '') {
if ( is_array($msg) || is_object($msg) ) {
$this->notices[] = print_r($msg, true);
} else {
$this->notices[] = $msg;
}
}
# Get an array of the most viewed posts as an array of the form ( post_id => number_of_views )
# - $oldest_date : should be of the form YYYY-MM-DD
# - $newest_date : should be of the form YYYY-MM-DD ( if null is provided, it will default to the current day )
# - $num_to_get : the number of results to return (e.g. 4 highest by default)
function get_highest_posts_in_range( $oldest_date, $newest_date = null, $num_to_get = 4 ) {
# Set up our return value
$found = array();
# Debug notice noting what was passed in
if ( $this->debug ) {
$this->add_notice( "Starting get_highest_posts_in_range( '{$oldest_date}', '{$newest_date}', {$num_to_get} )" );
}
# Do all real processing in a try / catch block to ensure can log problems instead of just throwing errors
try {
# Get a handle to the global database connection
global $wpdb;
# Verify that the provided $oldest_date is 10 characters long
if ( 10 !== strlen($oldest_date) ) {
throw new Exception("Parameter 1: Must be of the form YYYY-MM-DD", 10001);
} else {
# Appends time to the oldest date to make any between query include everything on the date
$oldest_date .= ' 00:00:00';
}
# Initialize the $newest_date variable OR validate that any non null value could be in the right format
if ( is_null( $newest_date ) ) {
$newest_date = date('Y-m-d') . ' 23:59:59';
} else if ( 10 !== strlen( $newest_date ) ) {
# Error
throw new Exception("Parameter 2: Must be null or in the form YYYY-MM-DD", 10002);
} else {
# Appends time to make any between query contain everything entered that day
$newest_date .= ' 23:59:59';
}
# Make sure that the number of records to get is sane
$num_to_get = (int) $num_to_get;
if ( $num_to_get <= 0 ) {
throw new Exception("Parameter 3: Must be a positive integer", 10003);
}
# Debug message to note the final values of provided variables after validation
if ( $this->debug ) {
$this->add_notice( "After Validation - Oldest: {$oldest_date}, Newest: {$newest_date}, Number to get: {$num_to_get}" );
}
# Build our query
# It will return the post_id and number of views (as views_in_period) for the most viewed items in the given date range
$query = <<<SQL
SELECT post_id, SUM(num_views) as views_in_period
FROM {$wpdb->prefix}post_views
WHERE entry_day BETWEEN %s AND %s
GROUP BY post_id
ORDER BY views_in_period DESC
LIMIT %d
SQL;
# Add our variables to the mysql query above safely
$query = $wpdb->prepare( $query, $oldest_date, $newest_date, $num_to_get );
# Debug message to note what the final prepared query is
if ( $this->debug ) {
$this->add_notice( "Prepared Query:<br />{$query}" );
}
# Run the query and get the results
$results = $wpdb->query( $query );
if ( false === $results ) {
$error = $wpdb->last_error;
throw new Exception("Bad Database query: {$query}, DB Error: {$error}", 10004);
} else if ( 0 < count($results) ) {
# There is at least one result. Add a debug message to show what the results are
$this->add_notice("Results detected:");
$this->add_notice( $results );
# Cycle through each result and add it to our return value
foreach ( $results as $row ) {
$found["{$row['post_id']}"] = $row['views'];
}
} else if ( $this->debug ) {
# No results returned, add a notice if in debug mode
$this->add_notice("Found no results for query");
}
} catch ( Exception $e ) {
# Exception detected, add it to the array of errors
$this->errors[] = $e;
}
return $found;
}
# This adds a new row to the post views table OR updates an existing row's num_views by 1
# The record is automatically added as viewed on the current day
function add_post_view( $post_id ) {
# Log how we were called
if ( $this->debug ) {
$this->add_notice("Called add_post_view( {$post_id} )");
}
# Initialize our return value
$added = 0;
try {
# Get hold of the database
global $wpdb;
# Add a new record. If there is a key violation, update the number of views instead.
# ( the unique key on the table is on the post_id and entry_day )
$query = <<<SQL
INSERT INTO {$wpdb->prefix}post_views (
post_id, entry_day, num_views
) VALUES (
%d, UTC_DATE(), 1
) ON DUPLICATE KEY UPDATE num_views = num_views + 1
SQL;
# Add our variables to the query in a safe manner
$query = $wpdb->prepare( $query, $post_id );
# Log the query to be ran so we can look at it if needed
if ( $this->debug ) {
$this->add_notice("Prepared Query: {$query}");
}
# Determine our results
$result = $wpdb->query( $query );
if ( false === $result ) {
# Error
$error = $wpdb->last_error;
throw new Exception("Bad Query: {$query}, Database Claims: {$error}", 10001);
} else if ( 0 === $result ) {
# Should never happen - would be an error as result is numer affected and there should be at least 1 row affected
throw new Exception("Bad Query: {$query}, Database claims no records updated!", 10002);
} else {
# Note how many changes were made (anything over 1 is fine)
if ( $this->debug ) {
$this->add_notice("Query completed with {$result} results");
}
$added = $added + $result;
}
} catch ( Exception $e ) {
# Make note of the exception
$this->errors[] = "Exception Ahoy!";
$this->errors[] = $e;
}
if ( $this->debug ) {
$this->add_notice("Leaving add_post_view");
}
return $added;
}
# Get the list of all errors as an array
function get_errors() {
if ( is_null( $this->errors ) ) {
$errors = array();
} else {
$errors = $this->errors;
}
return $errors;
}
# Get the list of all notices as an array
function get_notices() {
if ( is_null( $this->notices ) ) {
$notices = array();
} else {
$notices = $this->notices;
}
return $notices;
}
# Clear all errors and notices
# Used on initialization and between calls
function clear_all() {
$this->notices = array();
$this->errors = array();
}
}
To make use of this class, you will need to do the following:
- Option 1: Turn it into a plugin
- Option 2: Turn it into a library file (see below)
- Option 3: Add it to the bottom of your themes functions.php file.
- Option 4: Add it to the top of the template you want to use it in.
Turning a class into a simple library file
If you want to use this as needed, you could create a new file ( say class-my-most-viewed-posts.php ) and copy the code into it with <?php
on the first line of the file
Drop it into your theme directory and you can then call it as follows from within your template:
# Load the class file so that the class is made available
$found = locate_template('class-my-most-viewed-posts.php', true);
if ( empty($found) ) {
echo "<p>Failed to locate template class-my-most-viewed-posts.php</p>";
} else {
# NOTE - If you copy this into functions.php or into the template (instead of creating its own file):
# - copy from the line below to the line further down labeled INLINE DECLARATION METHOD END
if ( ! class_exists('My_Most_Viewed_Posts') ) {
echo "<p>Failed to find class My_Most_Viewed_Posts</p>";
} else {
# Create an instance of the class in debug mode
$Popularity = new My_Most_Viewed_Posts( true );
if ( !is_object( $Popularity ) ) {
echo "<p>Failed to create My_Most_Viewed_Posts object</p>";
} else {
# NOTE - the $post_id here should be the post to add it for!
$entries_added = $Popularity->add_post_view( $post_id );
if ( 0 === $entries_added ) {
# Failed, show problems.
echo "<p>Notices:</p><pre>" . print_r( $Popularity->get_notices(), true ) . "</pre>";
echo "<p>Errors:</p><pre>" . print_r( $Popularity->get_errors(), true ) . "</pre>";
} else {
# Clear notices so far
$Popularity->clear_all();
# Tell user we are making progress - comment out when done debugging
echo "<p>It seems to have worked. Added {$entries_added} record(s).</p>";
echo "<p>Checking counts</p>";
# Get the highest counts now that we have added a record (from the first of the year to tonight at midnight)
$highest_counts = $Popularity->get_highest_posts_in_range( '2015-01-01', null, $num_to_get = 4 );
if ( ! is_array($highest_counts) ) {
echo "<p>Bad return value for highest posts in range</p>";
echo "<p>Notices:</p><pre>" . print_r( $Popularity->get_notices(), true ) . "</pre>";
echo "<p>Errors:</p><pre>" . print_r( $Popularity->get_errors(), true ) . "</pre>";
} else {
# We have some highest counts in the form post_id => views
# Get the ids of the posts (see note 2 below)
# see: http://php/manual/en/function.array-keys.php
$wanted_ids = array_keys( $highest_counts );
# Build arguments for [WP_Query] (see note 3 below)
# - posts_per_page : -1 means to show them all
# - post_type : any will show all but those flagged exclude from search
# - post_parent__in will require that the results have keys in the provided array
# see http://codex.wordpress/Class_Reference/WP_Query
$wanted_args = array(
'posts_per_page' => -1,
'post_type' => 'any',
'post_parent__in' => $wanted_ids
);
$wanted = new WP_Query( $wanted_args );
if ( $wanted->have_posts() ) {
while ( $wanted->have_posts() ) {
$most_viewed = $wanted->next_post();
echo "<p>Post Found:</p><pre>" . print_r( $most_viewed, true ) . "</pre>";
}
unset( $wanted );
} else {
echo "<p>No posts found for wp_query using args:</p><pre>" . print_r( $wanted, true ) . "</pre>";
}
}
}
}
}
# INLINE DECLARATION METHOD END
}
Obviously, the code could be a lot shorter without all of the debug messages, but they should be helpful in tracking down the problem.
If you choose to place the class structure in your themes functions.php or in your template, copy the section just between INLINE DECLARATION METHOD for running the code. (e.g. you do not use locate_template )
Privateer, thanks a lot for the sample and explanations.
In my case it works only if I change a bit in get_highest_posts_in_range()
function of class file:
$results=$wpdb->query($query);
to
$results=$wpdb->get_results($query,ARRAY_A);
$found["{$row['post_id']}"]=$row['views'];
to
$found["{$row['post_id']}"]=$row['views_in_period'];
There are also a few changes required in the theme code too - args for getting the most popular posts:
$wanted_args = array(
'posts_per_page' => 4,
'post_type' => 'post',
'post_status' => 'publish',
'post__in' => $wanted_ids
);
due to if I have an error all records of db were captured, so I limited number by 4
. I chose post only (in my case) and not parents records but simple ids of posts.