• Hi there,

    yesterday i started working with the plugin and discovered that sorting posts by rating is not simply possible.

    but i found a way to it by storing the average rating in the post_meta table.

    for reference here is the sourcecode to what ive done:

    disclaimer: the comment actions need some more investigation

    first i copied the shortcode function “wpcr_avg_rating” to my template or plugin, trimmed it (as there is a lot unnecessary code) and seperated the calculation of the average rating from the rest:

    
    // calculates average rating based on comment_meta 'rating'
    function myPlugin_calculate_avg_rating($post_id = false){
      if ( !function_exists('wpcr_avg_rating') ) {
        return 0;
      }
      if ( !$post_id ) {
        $post_id = get_the_ID();
      }
      $comments = get_approved_comments($post_id);
      $sum = 0;
      $avg = 0;
      $count_rated = 0;
      
      foreach ( $comments as $comment ) {
        $rating = get_comment_meta($comment->comment_ID, 'rating', true);
        if ( $rating ) {
          $sum = $sum + (int)$rating;
          $count_rated++;
        }
      }
      if ( $count_rated > 0 ) { 
        $avg = $sum/$count_rated;
      }
      return $avg;
    }
    // add own rating avg function for better displaying
    function myPlugin_avg_rating($atts) {
      if ( !function_exists('wpcr_avg_rating') ) {
        return '';
      }
      $a = shortcode_atts(array('title' => 'Rating',), $atts); // what is this good for?
      $output = '';
      
      $post_id = get_the_ID();
      $avg = myPlugin_calculate_avg_rating($post_id); // now stored to post
      $comment_count = wp_count_comments($post_id)->approved;
      
      $wpcr_options = get_option('wpcr_settings');
      $tooltip_inline = $wpcr_options['tooltip_inline'];
      $avgrating_text = $wpcr_options['wpcravg_text'];
      $avg_text = $avgrating_text == '' ? __( 'Average', 'wp-post-comment-rating' ) : $avgrating_text;
      $avgText = __('average', 'wp-post-comment-rating');
      $outOf   = __('out of 5. Total', 'wp-post-comment-rating');
    
      if ( $avg > 0 ) {
        if ( $tooltip_inline == 1 ) {
          $output = '<div class="wpcr_aggregate"><a class="wpcr_tooltip" title="'.$avgText.': '.round($avg,2).' '.$outOf.': '.$comment_count.'"><span class="wpcr_stars" title="">'.$avg_text.':</span>';
          $output .= '<span class="wpcr_averageStars" id="'.$avg.'"></span></a></div>';
        }
        if ( $tooltip_inline == 0 ) {
          $output = '<div class="wpcr_aggregate"><a class="wpcr_inline" title=""><span class="wpcr_stars" title="">'.$avg_text.':</span>';
          $output .= '<span class="wpcr_averageStars" id="'.$avg.'"></span></a><span class="avg-inline">('.$avgText.': <strong> '.round($avg, 2).'</strong> '.$outOf.': '.$comment_count.')</span></div>';
        }
      }
      return $output;
    }

    after having the calculation seperated i add action hooks to everytime a comment gets posted/updated/approved/trashed or deleted

    
    // add avg rating to post meta after a comment was written
    
    // sets post_meta '_wpcr_rating' when new approved comment is posted
    function myPlugin_comment_post($id, $approved){
      myPlugin_post_meta_avg_rating(get_comment($id)->comment_post_ID);
    }
    add_action('comment_post', 'myPlugin_comment_post', 10, 2);
    
    // sets post_meta '_wpcr_rating' when wp_set_comment_status is called
    function myPlugin_wp_set_comment_status($id, $status){
      myPlugin_post_meta_avg_rating(get_comment($id)->comment_post_ID);
    }
    add_action('wp_set_comment_status', 'myPlugin_wp_set_comment_status', 10, 2);
    
    // sets post_meta '_wpcr_rating' when approved or deleted
    function myPlugin_transition_comment_status($new_status, $old_status, $comment){
      myPlugin_post_meta_avg_rating($comment->comment_post_ID);
    }
    add_action('transition_comment_status', 'myPlugin_transition_comment_status', 10, 3);

    all three actions call the “myPlugin_post_meta_avg_rating” function which updates the post_meta field “_wpcr_rating”

    // sets post_meta '_wpcr_rating'
    function myPlugin_post_meta_avg_rating($post_id){
      $avg = myPlugin_calculate_avg_rating($post_id);
      if ( $avg > 0 ) {
        update_post_meta($post_id, '_wpcr_rating', $avg);
      }
    }

    now every time a comment action happens the meta value of “_wpcr_rating” is updated.

    its not neccesarry any more to calculate it on every page load so i made a final change to the shortcode function by reading the post_meta:

    
    
    // add own rating avg function for better displaying
    function myPlugin_avg_rating($atts) {
      if ( !function_exists('wpcr_avg_rating') ) {
        return '';
      }
      $a = shortcode_atts(array('title' => 'Rating',), $atts); // what is this good for?
      $output = '';
      
      $post_id = get_the_ID();
      $avg = get_post_meta($post_id, '_wpcr_rating', true);
      // $avg = myPlugin_calculate_avg_rating($post_id); // now stored to post
      $comment_count = wp_count_comments($post_id)->approved;
      
      $wpcr_options = get_option('wpcr_settings');
      $tooltip_inline = $wpcr_options['tooltip_inline'];
      $avgrating_text = $wpcr_options['wpcravg_text'];
      $avg_text = $avgrating_text == '' ? __( 'Average', 'wp-post-comment-rating' ) : $avgrating_text;
      $avgText = __('average', 'wp-post-comment-rating');
      $outOf   = __('out of 5. Total', 'wp-post-comment-rating');
    
      if ( $avg > 0 ) {
        if ( $tooltip_inline == 1 ) {
          $output = '<div class="wpcr_aggregate"><a class="wpcr_tooltip" title="'.$avgText.': '.round($avg,2).' '.$outOf.': '.$comment_count.'"><span class="wpcr_stars" title="">'.$avg_text.':</span>';
          $output .= '<span class="wpcr_averageStars" id="'.$avg.'"></span></a></div>';
        }
        if ( $tooltip_inline == 0 ) {
          $output = '<div class="wpcr_aggregate"><a class="wpcr_inline" title=""><span class="wpcr_stars" title="">'.$avg_text.':</span>';
          $output .= '<span class="wpcr_averageStars" id="'.$avg.'"></span></a><span class="avg-inline">('.$avgText.': <strong> '.round($avg, 2).'</strong> '.$outOf.': '.$comment_count.')</span></div>';
        }
      }
      return $output;
    }

    also its now possible to manipulate an archive query with the ‘pre_get_posts’ action:

    
    function myPlugin_pre_get_posts($query) {
      
      if ( !is_admin() && $query->is_main_query() && is_archive() && $query->get('post_type') === 'my_post_type' ) {
        $query->set('meta_key','_wpcr_rating');
        $query->set('orderby','meta_value_num');
      }
    }
    add_action('pre_get_posts', 'myPlugin_pre_get_posts', 10, 1);

    of course one also can add it now to any WP_Query call.

    maybe some of the enhancements also will make it into an update of the plugin?

Viewing 10 replies - 1 through 10 (of 10 total)
  • Do you place this all in functions.php?

    Thread Starter netzgestaltung

    (@netzgestaltung)

    i did place it in my themes function.php but you can add it to any file that gets included inside a theme or customizations plugin as well.

    @netzgestaltung Hello, I see your code. It will help me for sorting. But I need 3 sort method, like below
    1. sort by most rating(max total rating count)
    2. sort by highest avg rating
    3. sort by lowest avg rating.

    I am adding some query var for check condition. like if query var (mrr) then sort post by 1, then (har) for 2, then (lar) for 3.

    can you please do something for it?
    thanks

    Update :

    I am now using your function to set post meta , like avg, total and rating count
    when a new comment is posted post meta value is updated but. when I delete the comment it is again updated post meta but the value increases not downgrade. can you please tell me where is my problem? Below is my modified code. I have put all into function PHP. I display post meta value in single.php for show result.


    //new rating data save
    // calculates average rating based on comment_meta 'rating'
    function myPlugin_calculate_avg_rating($post_id = false){
    if ( !function_exists('wpcr_avg_rating') ) {
    return 0;
    }
    if ( !$post_id ) {
    $post_id = get_the_ID();
    }
    $comments = get_approved_comments($post_id);
    $sum = 0;
    $avg = 0;
    $count_rated = 0;
    $oldavg = get_post_meta($post_id, '_kksr_avg',true);
    $countold = get_post_meta($post_id, '_kksr_casts',true);
    $oldtotal = get_post_meta($post_id, '_kksr_ratings',true);
    foreach ( $comments as $comment ) {
    $rating = get_comment_meta($comment->comment_ID, 'rating', true);
    //var_dump($checkc);
    if ( $rating ) {
    $sum = $oldtotal + (int)$rating;
    $count_rated++;
    }

    }
    if ($countold && $oldavg && $oldtotal ){
    $count_rated = $count_rated;
    $sum = $sum;
    $avg =round(($sum/$count_rated),2);
    }
    if ( $count_rated > 0 || $oldtotal ) {
    $count_rated = $count_rated;
    $sum = $sum;
    $avg = $sum/$count_rated;

    }

    return array($avg,$count_rated,$sum);
    }

    // add own rating avg function for better displaying
    function myPlugin_avg_rating($atts) {
    if ( !function_exists('wpcr_avg_rating') ) {
    return '';
    }
    $a = shortcode_atts(array('title' => 'Rating',), $atts); // what is this good for?
    $output = '';

    $post_id = get_the_ID();
    $avg = myPlugin_calculate_avg_rating($post_id); // now stored to post
    $comment_count = wp_count_comments($post_id)->approved;

    $wpcr_options = get_option('wpcr_settings');
    $tooltip_inline = $wpcr_options['tooltip_inline'];
    $avgrating_text = $wpcr_options['wpcravg_text'];
    $avg_text = $avgrating_text == '' ? __( 'Average', 'wp-post-comment-rating' ) : $avgrating_text;
    $avgText = __('average', 'wp-post-comment-rating');
    $outOf = __('out of 5. Total', 'wp-post-comment-rating');

    if ( $avg > 0 ) {
    if ( $tooltip_inline == 1 ) {
    $output = '<div class="wpcr_aggregate t"><span class="wpcr_stars" title="">'.$avg_text.':</span>';
    $output .= '<span class="wpcr_averageStars" id="'.$avg.'"></span>
    </div>';
    }
    if ( $tooltip_inline == 0 ) {
    $output = '<div class="wpcr_aggregate ta"><span class="wpcr_stars" title="">'.$avg_text.':</span>';
    $output .= '<span class="wpcr_averageStars" id="'.$avg.'"></span>
    <span class="avg-inline">('.$avgText.': '.round($avg, 2).' '.$outOf.': '.$comment_count.')</span></div>';
    }
    }
    return $output;
    }

    // add avg rating to post meta after a comment was written

    // sets post_meta '_wpcr_rating' when new approved comment is posted
    function myPlugin_comment_post($id, $approved){
    myPlugin_post_meta_avg_rating(get_comment($id)->comment_post_ID);
    update_rrating_count(get_comment($id)->comment_post_ID);
    update_rrating_total(get_comment($id)->comment_post_ID);
    }
    add_action('comment_post', 'myPlugin_comment_post', 10, 2);

    // sets post_meta '_wpcr_rating' when wp_set_comment_status is called
    function myPlugin_wp_set_comment_status($id, $status){
    myPlugin_post_meta_avg_rating(get_comment($id)->comment_post_ID);
    update_rrating_count(get_comment($id)->comment_post_ID);
    update_rrating_total(get_comment($id)->comment_post_ID);
    }
    add_action('wp_set_comment_status', 'myPlugin_wp_set_comment_status', 10, 2);

    // sets post_meta '_wpcr_rating' when approved or deleted
    function myPlugin_transition_comment_status($new_status, $old_status, $comment){

    myPlugin_post_meta_avg_rating($comment->comment_post_ID);
    update_rrating_count($comment->comment_post_ID);
    update_rrating_total($comment->comment_post_ID);
    }
    add_action('transition_comment_status', 'myPlugin_transition_comment_status', 10, 3);

    // sets post_meta '_wpcr_rating'
    function myPlugin_post_meta_avg_rating($post_id){
    $avg = myPlugin_calculate_avg_rating($post_id);
    //if ( $action == 'insert') {
    update_post_meta($post_id, '_kksr_avg', $avg[0]);
    // }

    }
    function update_rrating_count ($post_id) {
    $rcount = myPlugin_calculate_avg_rating($post_id);
    update_post_meta($post_id, '_kksr_casts', $rcount[1]);
    }
    function update_rrating_total ($post_id) {
    $rtt = myPlugin_calculate_avg_rating($post_id);
    update_post_meta($post_id, '_kksr_ratings', $rtt[2]);
    }

    @netzgestaltung

    • This reply was modified 5 years, 4 months ago by Faisal Ahmed.
    Thread Starter netzgestaltung

    (@netzgestaltung)

    hi, i will look into it tomorrow and post an update.

    i took the calculation from the original shortcode, but maybe i put a bug into it.

    thanks for the reply. I am fixed my abode code, it is now working for insert and delete a comment, but working avg and reviews count. not working for total star count. SO I have to commit total star count and do star count manually where I display the rating data. Here is my updated working code for avg and review count.


    //new rating data save
    // calculates average rating based on comment_meta 'rating'
    function myPlugin_calculate_avg_rating($post_id = false){
    if ( !function_exists('wpcr_avg_rating') ) {
    return 0;
    }
    if ( !$post_id ) {
    $post_id = get_the_ID();
    }
    $comments = get_approved_comments($post_id);
    $sum = 0;
    $avg = 0;
    $count_rated = 0;
    $oldavg = get_post_meta($post_id, '_kksr_avg',true);
    $countold = get_post_meta($post_id, '_kksr_casts',true);
    //$oldtotal = get_post_meta($post_id, '_kksr_ratings',true);
    foreach ( $comments as $comment ) {
    $rating = get_comment_meta($comment->comment_ID, 'rating', true);
    if ( $rating ) {
    $sum = $sum + (int)$rating;
    $count_rated++;
    }
    }
    if ($countold && $oldavg ){
    $count_rated = $count_rated;
    $sum = $sum;
    $avg =round(($sum/$count_rated),2);
    }
    if ( $count_rated > 0 ) {
    $count_rated = $count_rated;
    $sum = $sum;
    $avg = round(($sum/$count_rated),2);
    }
    return array($avg,$count_rated);
    }
    // add own rating avg function for better displaying
    function myPlugin_avg_rating($atts) {
    if ( !function_exists('wpcr_avg_rating') ) {
    return '';
    }
    $a = shortcode_atts(array('title' => 'Rating',), $atts); // what is this good for?
    $output = '';
    $post_id = get_the_ID();
    $avg = myPlugin_calculate_avg_rating($post_id); // now stored to post
    $comment_count = wp_count_comments($post_id)->approved;
    $wpcr_options = get_option('wpcr_settings');
    $tooltip_inline = $wpcr_options['tooltip_inline'];
    $avgrating_text = $wpcr_options['wpcravg_text'];
    $avg_text = $avgrating_text == '' ? __( 'Average', 'wp-post-comment-rating' ) : $avgrating_text;
    $avgText = __('average', 'wp-post-comment-rating');
    $outOf = __('out of 5. Total', 'wp-post-comment-rating');
    if ( $avg > 0 ) {
    if ( $tooltip_inline == 1 ) {
    $output = '<div class="wpcr_aggregate t"><span class="wpcr_stars" title="">'.$avg_text.':</span>';
    $output .= '<span class="wpcr_averageStars" id="'.$avg.'"></span>
    </div>';
    }
    if ( $tooltip_inline == 0 ) {
    $output = '<div class="wpcr_aggregate ta"><span class="wpcr_stars" title="">'.$avg_text.':</span>';
    $output .= '<span class="wpcr_averageStars" id="'.$avg.'"></span>
    <span class="avg-inline">('.$avgText.': '.round($avg, 2).' '.$outOf.': '.$comment_count.')</span></div>';
    }
    }
    return $output;
    }
    // add avg rating to post meta after a comment was written
    // sets post_meta '_wpcr_rating' when new approved comment is posted
    function myPlugin_comment_post($id, $approved){
    if ($approved == 1){
    myPlugin_post_meta_avg_rating(get_comment($id)->comment_post_ID);
    //update_rrating_count(get_comment($id)->comment_post_ID);
    //update_rrating_total(get_comment($id)->comment_post_ID);
    }
    }
    add_action('comment_post', 'myPlugin_comment_post', 10, 2);
    // sets post_meta '_wpcr_rating' when wp_set_comment_status is called
    function myPlugin_wp_set_comment_status($id, $status){
    //if($status =='approve'){
    myPlugin_post_meta_avg_rating(get_comment($id)->comment_post_ID);
    //update_rrating_count(get_comment($id)->comment_post_ID);
    //update_rrating_total(get_comment($id)->comment_post_ID);
    //}
    }
    add_action('wp_set_comment_status', 'myPlugin_wp_set_comment_status', 10, 2);
    // sets post_meta '_wpcr_rating' when approved or deleted
    function myPlugin_transition_comment_status($new_status, $old_status, $comment){
    //if($new_status != $old_status){
    myPlugin_post_meta_avg_rating($comment->comment_post_ID);
    //update_rrating_count($comment->comment_post_ID);
    //update_rrating_total($comment->comment_post_ID);
    //}
    }
    add_action('transition_comment_status', 'myPlugin_transition_comment_status', 10, 3);
    // sets post_meta '_wpcr_rating'
    function myPlugin_post_meta_avg_rating($post_id){
    $avg = myPlugin_calculate_avg_rating($post_id);
    //if ( $action == 'insert') {
    update_post_meta($post_id, '_kksr_avg', $avg[0]);
    //update_post_meta($post_id, '_kksr_ratings', $avg[2]);
    update_post_meta($post_id, '_kksr_casts', $avg[1]);
    // }
    }
    // function update_rrating_count ($post_id) {
    // $rcount = myPlugin_calculate_avg_rating($post_id);
    // update_post_meta($post_id, '_kksr_casts', $rcount[1]);
    // }
    // function update_rrating_total ($post_id) {
    // $rtt = myPlugin_calculate_avg_rating($post_id);
    // update_post_meta($post_id, '_kksr_ratings', $rtt[2]);
    // }

    Thread Starter netzgestaltung

    (@netzgestaltung)

    like avg, total and rating count
    when a new comment is posted post meta value is updated but. when I delete the comment it is again updated post meta but the value increases not downgrade

    i think that the behaviour you are describing ist OK when the comment you have deleted had low stars.

    the new values are then being calculated on the rest of the approved comments because it only gets data from them.

    maybe its questionable if all ever given stars should be kept but i thought it should only represent whats in the database right now.

    if you like to keep the number of all ever posted comments (why deletion?) then you need another post_meta field that gets counted up whenever a comment gets postet

    for more sort options one also will need more fields to store the data in

    Thread Starter netzgestaltung

    (@netzgestaltung)

    i added the code to my snippets repository here:
    https://github.com/netzgestaltung/wordpress-snippets/blob/master/wp-post-rating_order-posts.php

    i added documentation to it

    i dont know if i will have time until friday to check the other orderby suggestions

    Thread Starter netzgestaltung

    (@netzgestaltung)

    i now added 2 functions + 1 action within

    the hooked function at ‘save_post’ now saves the zero value of ‘_wpcr_rating’ to every post that gets saved.

    it was neccesarry because WP_Query also filters posts when using orderby on meta_key/meta_value/meta_query arguments and sorting was not possible when having two clauses with EXISTS and NOT EXISTS compare operators

    
    // sets post_meta '_wpcr_rating' to zero when not having comments
    function myPlugin_save_post($post_id, $post, $update){
      if ( !$post->_wpcr_rating ) {
        update_post_meta($post_id, '_wpcr_rating', 0);
      }
    }
    add_action( 'save_post', 'myPlugin_save_post', 10, 3);

    for an initial handling i added a function that saves the zero value once to all posts that does not have it.

    
    // sets all existing posts '_wpcr_rating' meta field to zero, 
    // customize the query args, only use once
    function myPlugin_set_rating(){
      $args = array(
        'post_type' => 'my_post_type',
        'posts_per_page' => -1,
        'meta_query' => array(
          'rating_all' => array(
            'key' => '_wpcr_rating',
            'compare' => 'NOT EXISTS',
          ),
        ),
      );
      $query = new WP_Query($args);
      if ( $query->have_posts() ) {
        while ( $query->have_posts() ) {
          $query->the_post();
          update_post_meta(get_the_ID(), '_wpcr_rating', 0);
        }
        wp_reset_postdata();
      }
    }

    putted it together here: https://github.com/netzgestaltung/wordpress-snippets/blob/master/wp-post-rating_order-posts.php

    Thanks for the reply. I already fix it for my project. For sorting I am using below codes.


    // the query
    //$paged = get_query_var('page') ? get_query_var('page') : 1;
    $paged = get_query_var('paged') ? get_query_var('paged') : 1;
    $sortby = get_query_var('sortby') ? get_query_var('sortby') : 'DESC';
    if($sortby == "ASC"):
    $orderby = "title";
    else:
    $orderby = "date";
    endif;

    $gb_bundle_args = array (
    'category_name' => $value, // pass category slug name
    'post_type' => 'post',
    'post_status' => 'publish',
    'posts_per_page' => 6,
    'paged' => $paged,
    'meta_query' => array(
    'relation' => 'AND',
    'rating' => array(
    'key' => '_kksr_casts',
    'type' => 'NUMERIC',
    'compare' => 'EXISTS',
    ),
    '_kksr_avg' => array(
    'key' => '_kksr_avg',
    'compare' => 'EXISTS',
    ),
    )
    );
    // Sort Results
    $current_sort = isset( $_GET['sortby'] ) ? esc_attr( $_GET['sortby'] ) : 'DESC'; //default date sorting
    switch( $current_sort ) {
    case 'a2z': // title accending and date decending
    $gb_bundle_args['orderby'] = array(
    'title' => 'ASC',
    'post_date' => 'DESC',
    );
    break;
    case 'mrv': //most reviews
    $gb_bundle_args['orderby'] = array(
    '_kksr_casts' => 'DESC',
    );
    break;
    case 'hrr': //highest rated
    $gb_bundle_args['orderby'] = array(
    '_kksr_avg' => 'DESC', // most avg
    '_kksr_casts' => 'DESC', // most avg with highest review (can remove)
    'post_date' => 'DESC', // date latest (can remove)
    'title' => 'ASC', // title a-z (can remove)
    );
    break;
    case 'lrr': // lowest rated
    $gb_bundle_args['orderby'] = array(
    '_kksr_avg' => 'ASC', // low avg
    '_kksr_casts' => 'DESC', // low avg with highest review (can remove)
    'post_date' => 'DESC', // date latest (can remove)
    'title' => 'ASC', // title a-z (can remove)
    );
    break;
    }
    $gb_bundle_qry = new WP_Query($gb_bundle_args);
    // echo "

    ";
    // 					var_dump($gb_bundle_qry->request);
    // 				  echo "

    ";
    $sortarr = array ('ASC','mrv','hrr','lrr');

Viewing 10 replies - 1 through 10 (of 10 total)
  • The topic ‘Howto sort posts by rating’ is closed to new replies.