2
\$\begingroup\$

I am using WP with the Avada theme and the built in Star Rating element as well as ACF Pro. Unfortunately I found out that the Star-Rating element adds a rating scheme to the footer of the website without asking. There is also no way to disable this. This is annoying

So I created my own shortcode based on the CSS code of the Star-Rating element.

It also works perfectly. Nevertheless, I have the feeling that there might still be potential for optimization.

The CSS code works as follows:

Example rating: 2.0 stars out of max 5.0

2x stars (filled)
3x stars (empty)

Example rating: 2.5 stars from max 5.0

2x stars (filled)
1x star (percentage filled: 50%)
2 stars (empty)

Example rating: 1.2 stars from max 5.0

1x star (filled)
1x star (percentage filled: 20%)
3x stars (empty)

Example rating: 4.6 stars from max 5.0

4x stars (filled)
1x star (percentage filled: 60%)

I have the following code now:

function show_star_rating_review()
{
    ob_start();
    global $post;

    $rating   = get_field('rating', $post);
    $rate_arr = explode('.',$rating);

    if ($rate_arr[1]) {
    $rate_emp = 4 - $rate_arr[0];
    } else {
    $rate_emp = 5 - $rate_arr[0];
    }

  $x = 1;
  $y = 1;

    echo '<div class="awb-stars-rating awb-stars-rating-1 awb-stars-rating-no-text" aria-label="Rating: ' .  $rating . ' out of 5">';
  echo '<style>.awb-stars-rating-1 .awb-stars-rating-filled-icon{margin-right:2px;color:#ffd041;}.awb-stars-rating-1 .awb-stars-rating-empty-icon,.awb-stars-rating-1 .awb-stars-rating-partial-icon-wrapper{margin-right:2px;}.awb-stars-rating-1 .awb-stars-rating-partial-icon{color:#ffd041;}.awb-stars-rating-1 .awb-stars-rating-icons-wrapper{color:#dbdbdb;font-size:23px;}</style>';
    echo '<div class="awb-stars-rating-icons-wrapper">';
  while ($x <= $rate_arr[0]) {
    $x++;
    echo '<i class="fa-star fas awb-stars-rating-filled-icon"></i>';
  }
    if ($rate_arr[1]) {
    echo '<i class="fa-star fas awb-stars-rating-partial-icon-wrapper"><i class="fa-star fas awb-stars-rating-partial-icon" style="width:' . $rate_arr[1] . '0%;"></i></i>';
    }
  if ($rate_emp > 0) {
    while ($y <= $rate_emp) {
      $y++;
      echo '<i class="fa-star fas awb-stars-rating-empty-icon"></i>';
        }
    }
    echo '</div>';
    echo '</div>';

    return ob_get_clean();
}
add_shortcode('ShowStarRatingReview', 'show_star_rating_review');

$rating is the ACF with the assigned rating. $rate_arr decomposes the rating into number before and number after the point.

If number after the point exists, so x.x rating then empty stars = 4 - number before the point. If number after the point exists, so x.0 rating then empty stars = 5 - number before the point.

As I mentioned before, the code works without problems so far. I'm just wondering if maybe I've made the whole thing too complicated.

\$\endgroup\$
5
  • \$\begingroup\$ Food for thought: my answer to Laravel - Star rating - Optimization? ... I guess you'd just need an extra step to round stars down to the nearest .5 of a point. \$\endgroup\$ Jan 26 at 0:02
  • \$\begingroup\$ Good morning. Thank you for your answer. But I don't want to have half stars. If something is rated with 2.7 stars, then 2.7 stars should be filled. That's how it all works. \$\endgroup\$
    – Torsten
    Jan 26 at 0:40
  • \$\begingroup\$ Oh, like this: codepen.io/filcp/pen/QvZVOg \$\endgroup\$ Jan 26 at 11:26
  • \$\begingroup\$ Have I made a mistake transferring to a jsfiddle? Do I have the wrong fontawesome version? I have advice, but I want to test my suggestion first. jsfiddle.net/jn1243g9 Can you provide a working, static demo link? \$\endgroup\$ Jan 26 at 11:32
  • \$\begingroup\$ The percentage refers only to the "started" star. Rating 3.7 means: 3 filled stars then the started star 70% filled and then 2 stars unfilled. To get to the percentage, I simply add a "0" to the 7 and have my 70%. A rating of 4.0 would then be 4 filled stars and 1 unfilled star. With a round sum there is no started star. Rating range is min 1 star, max 5 stars - steps each 0.1 - so a 1.75 rating is not possible. So in a hurry, unfortunately I can not set up a demo, because I have to set up wordpress for this and activate an Avada Theme Builder license, because this is a premium theme. \$\endgroup\$
    – Torsten
    Jan 26 at 14:19

2 Answers 2

1
\$\begingroup\$

I don't know if I am getting older and grumpier or older and wiser, but I find that I am increasingly in favor of writing code that is easier to read and easier to manage.

First, I found your css styling block very difficult to read. In general, I'd say move all of those style declarations to an external style sheet, but with Wordpress (I don't use Wordpress), you may prefer to jam all of that into your shortcode function.

Second, I generally prefer to avoid string concatenation -- especially when creating html markup. Since learning about printf(), I find myself using it (and its related native functions) to create "template strings" with placeholders, then adding the the variables at the end of the function call. This creates a clear separation from markup and variables which improves maintainability.

I kicked around the idea of making str_repeat() calls versus a for() loop with math. I thought about building a string of star markup versus an array of star markups. I am not completely sold on what is "best" and I could be convinced of several different "good" approaches. This is a rather lightweight task in terms of processing and memory, so there isn't any benefit in trying to optimize for performance. Striving for code brevity is only going to damage readability. I'll share one version that I mocked up which endeavors to make the code easier to read/maintain.

Code: (Demo)

function show_star_rating_review(): string
{
    global $post;
    $rating = get_field('rating', $post);
    [$whole, $decimal] = explode('.', $rating);    

    $css = <<<CSS
        <style>
        .awb-stars-rating-1 .awb-stars-rating-filled-icon{
            margin-right:2px;
            color:#ffd041;
        }
        .awb-stars-rating-1 .awb-stars-rating-empty-icon,
        .awb-stars-rating-1 .awb-stars-rating-partial-icon-wrapper{
            margin-right:2px;
        }
        .awb-stars-rating-1 .awb-stars-rating-partial-icon{
            color:#ffd041;
        }
        .awb-stars-rating-1 .awb-stars-rating-icons-wrapper{
            color:#dbdbdb;
            font-size:23px;
        }
        </style>
    CSS;

    $stars['whole'] = str_repeat("<i class=\"fa-star fas awb-stars-rating-filled-icon\"></i>\n                 ", $whole);
    if ($decimal) {
        $stars['piece'] = sprintf(
            "<i class=\"fa-star fas awb-stars-rating-partial-icon-wrapper\">
                     <i class=\"fa-star fas awb-stars-rating-partial-icon\" style=\"width:%d0%%;\"></i>
                 </i>\n                 ",
             $decimal
        );
    }
    $stars['empty'] = str_repeat("<i class=\"fa-star fas awb-stars-rating-empty-icon\"></i>\n                 ", 5 - ceil($rating));

    return sprintf(
        '%s
         <div class="awb-stars-rating awb-stars-rating-1 awb-stars-rating-no-text" aria-label="Rating: %s out of 5">
             <div class="awb-stars-rating-icons-wrapper">
                 %s
             </div>
         </div>',
        $css,
        $rating,
        implode($stars)
    );
}
\$\endgroup\$
4
  • \$\begingroup\$ Yes, you are right about the CSS. The ATB just packs everything in shortcodes with masses of parameters. This inflates the code - especially since everything is saved in every single post. I have copied out the star rating area with the dev tools, so that I have a basis. I was concerned with a way to display just those stars, but without including the rating scheme in the footer. I already generate a scheme for the posting in the header. After my solution works technically in the end, I became curious what could be improved. Even with almost 55 years I still like to learn. \$\endgroup\$
    – Torsten
    Jan 28 at 12:59
  • \$\begingroup\$ I like your solution quite well. But I have to admit that I don't really know how to use sprintf(). Unfortunately, I can not vote due to lack of reputation. \$\endgroup\$
    – Torsten
    Jan 28 at 13:00
  • \$\begingroup\$ Yes, you can only "accept" the answer that is most helpful. That can be mine or Sharky's so far. You may get more reviews as well. \$\endgroup\$ Jan 28 at 13:01
  • \$\begingroup\$ printf() (print from a formatted string), sprintf() ("silent" version of printf()), vprintf() (print from array of values), vsprintf() ("silent" version of vprintf()) -- these all allow you to create placeholders starting with % (then some characters with special meanings), then you deliver your variables as subsequent parameters. After a short while, it will make more sense and its beauty will become realized. \$\endgroup\$ Jan 28 at 13:05
1
\$\begingroup\$

Speaking strictly about the code and not the approach in general.

Use array destructuring to get nicer, more readable variables:

[$whole, $decimal] = explode('.', $rating);

Some syntactic sugar to replace if/else:

$rate_emp = ($decimal ? 4 : 5) - $whole;

Loops can be replaced by str_repeat(). Optionally, you can check if repeat count is less than 1 and avoid the function call if it is. If there's a chance that rating could be above 5.0, this is necessary to avoid errors.

echo str_repeat('<i class="fa-star fas awb-stars-rating-filled-icon"></i>', $whole);

Generating and catching the output seems unnecessary, especially since you're doing it using PHP syntax. Write to string directly:

function show_star_rating_review()
{
    global $post;

    $rating = get_field('rating', $post);
    [$whole, $decimal] = explode('.', $rating);

    $rate_emp = ($decimal ? 4 : 5) - $whole;

    $output = '<div class="awb-stars-rating awb-stars-rating-1 awb-stars-rating-no-text" aria-label="Rating: ' . $rating . ' out of 5">'
    . '<style>.awb-stars-rating-1 .awb-stars-rating-filled-icon{margin-right:2px;color:#ffd041;}.awb-stars-rating-1 .awb-stars-rating-empty-icon,.awb-stars-rating-1 .awb-stars-rating-partial-icon-wrapper{margin-right:2px;}.awb-stars-rating-1 .awb-stars-rating-partial-icon{color:#ffd041;}.awb-stars-rating-1 .awb-stars-rating-icons-wrapper{color:#dbdbdb;font-size:23px;}</style>'
    . '<div class="awb-stars-rating-icons-wrapper">'
    . str_repeat('<i class="fa-star fas awb-stars-rating-filled-icon"></i>', $whole);

    if ($decimal) {
      $output .= '<i class="fa-star fas awb-stars-rating-partial-icon-wrapper"><i class="fa-star fas awb-stars-rating-partial-icon" style="width:' . $decimal . '0%;"></i></i>';
    }

    $output .= str_repeat('<i class="fa-star fas awb-stars-rating-empty-icon"></i>', $rate_emp)
    . '</div>'
    . '</div>';

    return $output;
}
\$\endgroup\$
1
  • \$\begingroup\$ Thank you for your input. Your solution is also nice and clear. I also had the "$output .=" solution in the beginning. But was now somehow not 100% sure what is better ... I have therefore switched back to the direct "echo". Also for your solution I can unfortunately not yet vote due to lack of reputation. \$\endgroup\$
    – Torsten
    Jan 28 at 13:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.