Вернуться на предыдущую страницу

В данной статье приведен пример системы рейтинга для любого сайта, которая работает на PHP (7.2+- [можно и меньше, но смотрите JSON encode русские символы])+MySQL (5.6+)+AJAX (jQuery 3.3.1+-).

Система устроена таким образом, что позволяет оценивать один товар/услугу/материал по пяти или одному критерию. Кроме того, формула учитывает количество проголосовавших и, в зависимости от этого количества, голос следующего человека имеет зависимое от общего числа проголосовавших значение и влияние на общий рейтинг материала.

Каким именно способом и образом Вы расположите все необходимые файлы у себя - роли не играет. Главное понять и правильно применить их все.

В базе данных должны быть следующие поля:

comm, product, user

CREATE TABLE `comm` (
 `id` int(11) NOT NULL,
 `text` mediumtext NOT NULL,
 `prid` int(11) NOT NULL,
 `ptyp` int(11) NOT NULL,
 `uid` text NOT NULL COMMENT 'имя комм.',
 `rait` text NOT NULL,
 `date` text NOT NULL COMMENT 'дата сообщ.',
 `ip` text NOT NULL,
 `pub` int(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `product` (
 `id` int(10) UNSIGNED NOT NULL,
 `category_id` mediumtext,
 `brand_id` tinyint(3) UNSIGNED NOT NULL,
 `title` varchar(255) NOT NULL,
 `data` mediumtext NOT NULL,
 `copy_id` int(11) NOT NULL,
 `alias` varchar(255) NOT NULL,
 `content` text,
 `shrtdsc` text NOT NULL,
 `price` text NOT NULL,
 `old_price` float NOT NULL DEFAULT '0',
 `status` int(1) NOT NULL,
 `seo_ttl` text NOT NULL,
 `keywords` varchar(255) DEFAULT NULL,
 `description` varchar(255) DEFAULT NULL,
 `img` varchar(255) NOT NULL DEFAULT '/img/tech/_no-img.jpg',
 `imgs` text NOT NULL,
 `hit` int(1) NOT NULL,
 `rel` text,
 `uniq` text NOT NULL,
 `ava` int(11) NOT NULL COMMENT 'наличие',
 `prdts` text COMMENT 'из таблицы proddata id-шники',
 `tbl` int(11) NOT NULL,
 `szs` int(11) NOT NULL,
 `colr` int(11) NOT NULL,
 `sizes` text NOT NULL,
 `date_pub` datetime NOT NULL,
 `rait` text NOT NULL,
 `dateup` datetime NOT NULL,
 `uid` int(11) NOT NULL,
 `pid` int(11) NOT NULL,
 `chek` int(11) NOT NULL DEFAULT '1',
 `uslugi` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user` (
 `id` int(10) UNSIGNED NOT NULL,
 `login` varchar(255) NOT NULL,
 `password` varchar(255) NOT NULL,
 `email` varchar(255) NOT NULL,
 `name` varchar(255) NOT NULL,
 `stra` text NOT NULL,
 `gor` text NOT NULL,
 `fam` text NOT NULL,
 `address` varchar(255) NOT NULL,
 `role` int(11) NOT NULL DEFAULT '4',
 `contacts` text NOT NULL,
 `discount` float NOT NULL,
 `qp` int(11) NOT NULL COMMENT 'кол-во товара для постщка',
 `dreg` datetime NOT NULL,
 `comm` int(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Пример базы данных позволит Вам, возможно, расширить свою или просто создать в той таблице, где лежат товары/материалы/услуги (`product`) поле `rait`. В нем будет храниться JSON

{"rait":3.29375,"raitmark":{"a":3.5,"b":3.125,"c":2.75,"d":3.125,"e":3.5},"raitTtl":3,"raitEvery":{"a":{"f":2,"g":0,"h":0,"i":0,"j":1},"b":{"f":1,"g":1,"h":0,"i":0,"j":1},"c":{"f":1,"g":0,"h":1,"i":0,"j":1},"d":{"f":1,"g":1,"h":0,"i":0,"j":1},"e":{"f":2,"g":0,"h":0,"i":0,"j":1}}}

Где rait - общий рейтинг
raitMark - голоса за 1-5 критериев по среднему показателю
raitTtl - проголосовало всего людей
raitEvery - проголосовало за каждый из критериев раз за каждую оценку.

Формула расчета рейтинга с примером использования функции.

<?php
function calcTtlVoteService($arr5,$rAll,$rCountTotalPeople,$service=1){
 $minPls=0;
 //$r=array(1,1,1,1,1);
 $r=$arr5;
 //var_dump($service);exit;

if($service==1) {
 $a=$r[0]*50*20;
 $b = $r[1] * 10 * 20;
 $c = $r[2] * 10 * 20;
 $d = $r[3] * 25 * 20;
 $e = $r[4] * 5 * 20;//-1 действие
 $sum=($a+$b+$c+$d+$e)/100;//89%-2
 }else{
 $a=$r[0];
 $sum=$a*100/5;//89%-2
 }

var_dump($sum);
 $sum=$sum*5/100;//3.5 -3
var_dump($sum);
 if($rAll>0)$sum=($rAll+$sum)/2;//(3.8+3.5)/2=3.65
 if($sum>$rAll){$minPls=1;}//для последнего действия
 if($rAll>0)$sum=$rAll-$sum;//0.15
 $sum=$sum/$rCountTotalPeople;//2


 if($sum<0)$sum=0-$sum;

if($rAll>0){($minPls==0)?$sum=$rAll-$sum:$sum=$rAll+$sum;}

echo $sum;
}

#пример:
calcTtlVoteService(array(1,5,5,5,5),0,1);
?>

Далее HTML код формы для голосования:

<?
$ser=0;//не услуги, а товар
if($_POST['ps']=='service'){
    $ser=1;//услуги, а не товар
}
$id=(int)$_POST['id'];
?>
<div class="br3 p4 aj_commHtml">
 <h3><i class="fa-comment"></i> Добавить отзыв</h3>
 <div>
 <textarea type="text" class="br3 txt" cols="92" placeholder="Текст сообщения" maxlength="1000"></textarea>
 <span class="gry txtInputbcomm">Осталось: 1000 символов</span>
 </div>
 <div>

 <?
 //если оцениваем услугу
 if($ser==1){
 echo '
 <div class="p4 aj_commD">
 <h4 class="fB">Предлагаем Вам оценить качество предоставляемых услуг для данного поставщика</h4>

 <div class="mv320 tableDiv">
 <div class="table">

 <div class="tr">
 <div class="tbc brdrb">
 Качество:
 </div>
 <div class="tbc brdrb">
 <div class="in rate1"></div>
 </div>
 </div>


 <div class="tr">
 <div class="tbc brdrb">
 Вежливость:
 </div>
 <div class="tbc brdrb">
 <div class="in rate2"></div>
 </div>
 </div>


 <div class="tr">
 <div class="tbc brdrb">
 Пунктуальность:
 </div>
 <div class="tbc brdrb">
 <div class="in rate3"></div>
 </div>
 </div>


 <div class="tr">
 <div class="tbc brdrb">
 Время отклика на заказ:
 </div>
 <div class="tbc brdrb">
 <div class="in rate4"></div>
 </div>
 </div>


 <div class="tr">
 <div class="tbc">
 Цена:
 </div>
 <div class="tbc">
 <div class="in rate5"></div>
 </div>
 </div>
 </div>
 </div>
</div>
 ';
 }else{
 //если оцениваем товар
 echo '<div class="p4 aj_commD">
 <h4 class="fB">Предлагаем Вам оценить данный товар</h4>

 <div class="mv320 tableDiv">
 <div class="table">

 <div class="tr">
 <div class="tbc brdrb">
 Ваша оценка:
 </div>
 <div class="tbc brdrb">
 <div class="in rate1"></div>
 </div>
 </div>
 </div>
 </div>
</div>';
 }


 ?>

 </div>
 <div class="br3 p4 ajx_err" style="display:none"></div>
 <div class="br3 p4 ajx_goo" style="display:none"></div>
 <div class="fc fsa fw">
 <div class="br3 cp p_commBtn p_commBS" data-service="<?=$ser?>" data-id="<?=$id?>"><i class="fa-check-circle"></i> Отправить</div>
 <!--<div class="br3 cp p_commBtn p_commBR"><i class="fa-refresh"></i> Обновить отзывы</div>-->
 </div>
 <div class="mh10 fB gry"><i class="fa-info-circle"></i> <a href="/text/comments-rules" target="_blank">Прочитайте правила для отзывов</a></div>
</div>

JS для этой формы + система звездочного рейтинга на JS

<script>

    $('.p_commBS').on('click',function () {
        var a,b,c,d,e,f,g=1,h=$(this).data('service'),j=$(this).data('id');
        if(h==0){//product
            a=$('.rate1').data('rate-value')
            if(a===undefined){
                $('.warning').html('Вы не оценили данный товар. Оцените его').slideDown(200)
                g = 0
                return false
            }
            f=[a]
        }else {//service
            a = $('.rate1').data('rate-value')
            b = $('.rate2').data('rate-value')
            c = $('.rate3').data('rate-value')
            d = $('.rate4').data('rate-value')
            e = $('.rate5').data('rate-value')
            f = [a, b, c, d, e]

            $.each(f, function (k, v) {
                if (v === undefined) {
                    $('.warning').html('Вы не оценили один из критериев или несколько, оцените его/их').slideDown(200)
                    g = 0
                    return false
                } else {
                    $('.warning').slideUp(100)
                }
            })
        };//\service
        if($('.aj_commHtml .txt').val().length<21)$('.warning').html('Текст отзыва должен содержать не менее 20 символов. Заполните текстовое поле.').slideDown(100),g=0

        if(g==1){
            $.ajax({
                url: '/ajx',
                data: {location_:location.href,set:'setFrmComm',vote:f,txt:$('.aj_commHtml .txt').val(),service:h,id:j},
                type: 'POST',
                beforeSend:function () {
                    $('.loading').slideDown(0);
                },
                success: function(d){
console.log(d)
                    $('.ajx_goo,.ajx_err').slideUp(50)
                    var jsn=JSON.parse(d);
                    if(jsn.txt=='success'){
                        $('.ajx_goo').slideDown(100).html('<i class="fa-bullhorn"></i> Ваш отзыв успешно добавлен. После проверки нашими сотрудниками, он повится на сайте. Ожидайте...<br>Благодарим за взаимодействие с нами!')
                        $('.p_loadComFrm').first().after(jsn.comment)
                        $('.txt,.p_commBS,.p_btnLdFrmCom,.aj_commD').slideUp(50, function() {
                            $('.txt,.p_commBS,.p_btnLdFrmCom,.aj_commHtml h3,.txtInputbcomm,.aj_commD').remove()
                        });
                        $('html, body').animate({
                            scrollTop:$('.ajx_otz').offset().top-$(window).height()/2
                        }, 1000);
                        g=0
                    }
                    if(jsn.txt!='success'){
                        $('.ajx_err').slideDown(100).html('<i class="fa-ban"></i> '+jsn.txt)
                        g=1
                    }

                    $('.loading').slideUp(0);
                    return false
                },
                error: function(){
                    alert('Ошибка. Попробуйте позже...');
                    $('.loading').slideUp(0);
                }
            });
        }

    });

    //Count how many characters left
    $('.txt').on('keyup paste change input propertychange',function(){
        countTxt($('.txt'),$('.txtInputbcomm'));
        if($('.aj_commHtml .txt').val().length>20)$('.warning').slideUp(100)
    });
    function countTxt(id,span){
        var txtl =id.val().length;
        var tl=1000-txtl;
        if(tl>0){
            span.text('Осталось символов: '+tl)
        }else{span.text('Введено максимальное количество символов: '+txtl)}
    };
    //\//Count how many characters left

    //rait
    /*
 * A highly customizable rating widget that supports images, utf8 glyphs and other html elements!
 * https://github.com/auxiliary/rater
 */
        $.fn.textWidth = function()
        {
            var html_calc = $('<span>' + $(this).html() + '</span>');
            html_calc.css('font-size',$(this).css('font-size')).hide();
            html_calc.prependTo('body');
            var width = html_calc.width();
            html_calc.remove();

            if (width == 0)
            {
                var total = 0;
                $(this).eq(0).children().each(function(){
                    total += $(this).textWidth();
                });
                return total;
            }
            return width;
        };

        $.fn.textHeight = function()
        {
            var html_calc = $('<span>' + $(this).html() + '</span>');
            html_calc.css('font-size',$(this).css('font-size')).hide();
            html_calc.prependTo('body');
            var height = html_calc.height();
            html_calc.remove();
            return height;
        };

        /*
       * IE8 doesn't support isArray!
       */
        Array.isArray = function (obj) {
            return Object.prototype.toString.call(obj) === "[object Array]";
        };

        /*
       * Utf-32 isn't supported by default, so we have to use Utf-8 surrogates
       */
        String.prototype.getbcodePointLength = function() {
            return this.length-this.split(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g).length+1;
        };

        String.fromCodePoint= function() {
            var chars= Array.prototype.slice.call(arguments);
            for (var i= chars.length; i-->0;) {
                var n = chars[i]-0x10000;
                if (n>=0)
                    chars.splice(i, 1, 0xD800+(n>>10), 0xDC00+(n&0x3FF));
            }
            return String.fromCharCode.apply(null, chars);
        };

        /*
       * Starting the plugin itself
       */
        $.fn.rate = function(options)
        {
            if (options === undefined || typeof options === 'object')
            {
                return this.each(function(){
                    if (!$.data(this, "rate"))
                    {
                        $.data(this, "rate", new Rate(this, options));
                    }
                });
            }
            else if (typeof options === 'string')
            {
                var args = arguments;
                var returns;
                this.each(function(){
                    var instance = $.data(this, "rate");
                    if (instance instanceof Rate && typeof instance[options] === 'function')
                    {
                        returns = instance[options].apply(instance, Array.prototype.slice.call(args, 1));
                    }
                    if (options === 'destroy')
                    {
                        // Unbind all events and empty the plugin data from instance
                        $(instance.element).off();
                        $.data(this, 'rate', null);
                    }
                });

                return returns !== undefined ? returns : this;
            }
        };

        function Rate(element, options)
        {
            this.element = element;
            this.settings = $.extend({}, $.fn.rate.settings, options);
            this.set_faces = {}; // value, symbol pairs
            this.build();
        }

        Rate.prototype.build = function()
        {
            this.layers = {};
            this.value = 0;
            this.raise_select_layer = false;

            if (this.settings.initial_value)
            {
                this.value = this.settings.initial_value;
            }
            if ($(this.element).attr("data-rate-value"))
            {
                this.value = $(this.element).attr("data-rate-value");
            }

            /*
          * Calculate the selected width based on the initial value
          */
            var selected_width = this.value / this.settings.max_value * 100;

            /*
          * Let's support single strings as symbols as well as objects
          */
            if (typeof this.settings.symbols[this.settings.selected_symbol_type] === 'string')
            {
                var symbol = this.settings.symbols[this.settings.selected_symbol_type];
                this.settings.symbols[this.settings.selected_symbol_type] = {};
                this.settings.symbols[this.settings.selected_symbol_type]['base'] = symbol;
                this.settings.symbols[this.settings.selected_symbol_type]['selected'] = symbol;
                this.settings.symbols[this.settings.selected_symbol_type]['hover'] = symbol;
            }

            /*
          * Making the three main layers (base, select, hover)
          */
            var base_layer = this.addLayer("base-layer t", 100, this.settings.symbols[
                this.settings.selected_symbol_type]["base"], true);

            var select_layer = this.addLayer("select-layer t", selected_width,
                this.settings.symbols[this.settings.selected_symbol_type]["selected"], true);

            var hover_layer = this.addLayer("hover-layer t", 0, this.settings.symbols[
                this.settings.selected_symbol_type]["hover"], false);

            /* var face_layer = this.addLayer("face-layer", 1, this.settings
            .symbols[this.settings.face_layer_symbol_type][0], true); */

            this.layers["base_layer"] = base_layer;
            this.layers["select_layer"] = select_layer;
            this.layers["hover_layer"] = hover_layer;

            /*
          * Bind the container to some events
          */
            $(this.element).on("mousemove", $.proxy(this.hover, this));
            $(this.element).on("click", $.proxy(this.select, this));
            $(this.element).on("mouseleave", $.proxy(this.mouseout, this));

            /*
          * Set the main element as unselectable
          */
            $(this.element).css({
                "-webkit-touch-callout": "none",
                "-webkit-user-select": "none",
                "-khtml-user-select": "none",
                "-moz-user-select": "none",
                "-ms-user-select": "none",
                "user-select": "none",
            });

            /*
          * Update custom input field if provided
          */
            if (this.settings.hasOwnProperty("update_input_field_name"))
            {
                this.settings.update_input_field_name.val(this.value);
            }
        }

        /*
       * Function to add a layer
       */
        Rate.prototype.addLayer = function(layer_name, visible_width, symbol, visible)
        {
            var layer_body = "<div>";
            for (var i = 0; i < this.settings.max_value; i++)
            {
                if (Array.isArray(symbol))
                {
                    if (this.settings.convert_to_utf8)
                    {
                        symbol[i] = String.fromCodePoint(symbol[i]);
                    }
                    layer_body += "<span>" + (symbol[i]) + "</span>";
                }
                else
                {
                    if (this.settings.convert_to_utf8)
                    {
                        symbol = String.fromCodePoint(symbol);
                    }
                    layer_body += "<span>" + symbol + "</span>";
                }
            }
            layer_body += "</div>";
            var layer = $(layer_body).addClass("rate-" + layer_name).appendTo(this.element);

            $(layer).css({
                width: visible_width + "%",
                height: $(layer).children().eq(0).textHeight(),
                overflow: 'hidden',
                position: 'absolute',
                top: 0,
                display: visible ? 'block' : 'none',
                'white-space': 'nowrap'
            });
            $(this.element).css({
                width: $(layer).textWidth() + "px",
                height: $(layer).height(),
                position: 'relative',
                cursor: this.settings.cursor,
            });

            return layer;
        }

        Rate.prototype.updateServer = function()
        {
            if (this.settings.url != undefined)
            {
                $.ajax({
                    url: this.settings.url,
                    type: this.settings.ajax_method,
                    data: $.extend({}, { value: this.getValue() }, this.settings.additional_data),
                    success: $.proxy(function(data){
                        $(this.element).trigger("updateSuccess", [data]);
                    }, this),
                    error: $.proxy(function(jxhr, msg, err){
                        $(this.element).trigger("updateError", [jxhr, msg, err]);
                    }, this)
                });
            }
        }

        Rate.prototype.getValue = function()
        {
            return this.value;
        }

        Rate.prototype.hover = function(ev)
        {
            var pad = parseInt($(this.element).css("padding-left").replace("px", ""));
            var x = ev.pageX - $(this.element).offset().left - pad;
            var val = this.toValue(x, true);

            if (val != this.value)
            {
                this.raise_select_layer = false;
            }

            if (!this.raise_select_layer && !this.settings.readonly)
            {
                var visible_width = this.toWidth(val);
                this.layers.select_layer.css({display: 'none'});
                if (!this.settings.only_select_one_symbol)
                {
                    this.layers.hover_layer.css({
                        width: visible_width + "%",
                        display: 'block'
                    });
                }
                else
                {
                    var index_value = Math.floor(val);
                    this.layers.hover_layer.css({
                        width: "100%",
                        display: 'block'
                    });
                    this.layers.hover_layer.children("span").css({
                        visibility: 'hidden',
                    });
                    this.layers.hover_layer.children("span").eq(index_value != 0 ? index_value - 1 : 0).css({
                        visibility: 'visible',
                    });
                }
            }
        }

        /*
       * Event for when a rating has been selected (clicked)
       */
        Rate.prototype.select = function(ev)
        {
            if (!this.settings.readonly)
            {
                var old_value = this.getValue();
                var pad = parseInt($(this.element).css("padding-left").replace("px", ""));
                var x = ev.pageX - $(this.element).offset().left - pad;
                var selected_width = this.toWidth(this.toValue(x, true));
                this.setValue(this.toValue(selected_width));
                this.raise_select_layer = true;
            }
        }

        Rate.prototype.mouseout = function()
        {
            this.layers.hover_layer.css({display: 'none'});
            this.layers.select_layer.css({display: 'block'});
        }

        /*
       * Takes a width (px) and returns the value it resembles
       */
        Rate.prototype.toWidth = function(val)
        {
            return val / this.settings.max_value * 100;
        }

        /*
       * Takes a value and calculates the width of the selected/hovered layer
       */
        Rate.prototype.toValue = function(width, in_pixels)
        {
            var val;
            if (in_pixels)
            {
                val = width / this.layers.base_layer.textWidth() * this.settings.max_value;
            }
            else
            {
                val = width / 100 * this.settings.max_value;
            }

            // Make sure the division doesn't cause some small numbers added by
            // comparing to a small arbitrary number.
            var temp = val / this.settings.step_size;
            if (temp - Math.floor(temp) < 0.00005)
            {
                val = Math.round(val / this.settings.step_size) * this.settings.step_size;
            }
            val = (Math.ceil(val / this.settings.step_size)) * this.settings.step_size;
            val = val > this.settings.max_value ? this.settings.max_value : val;
            return val;
        }

        Rate.prototype.getElement = function(layer_name, index)
        {
            return $(this.element).find(".rate-" + layer_name + " span").eq(index - 1);
        }

        Rate.prototype.getLayers = function()
        {
            return this.layers;
        }

        Rate.prototype.setFace = function(value, face)
        {
            this.set_faces[value] = face;
        }

        Rate.prototype.setAdditionalData = function(data)
        {
            this.settings.additional_data = data;
        }

        Rate.prototype.getAdditionalData = function()
        {
            return this.settings.additional_data;
        }

        Rate.prototype.removeFace = function(value)
        {
            delete this.set_faces[value];
        }

        Rate.prototype.setValue = function(value)
        {
            if (!this.settings.readonly)
            {
                if (value < 0)
                {
                    value = 0;
                }
                else if (value > this.settings.max_value)
                {
                    value = this.settings.max_value;
                }

                var old_value = this.getValue();
                this.value = value;

                /*
             * About to change event, should support prevention later
             */
                var change_event = $(this.element).trigger("change", {
                    "from": old_value,
                    "to": this.value
                });

                /*
             * Set/Reset faces
             */
                $(this.element).find(".rate-face").remove();
                $(this.element).find("span").css({
                    visibility: 'visible'
                });
                var index_value = Math.ceil(this.value);
                if (this.set_faces.hasOwnProperty(index_value))
                {
                    var face = "<div>" + this.set_faces[index_value] + "</div>";
                    var base_layer_element = this.getElement('base-layer', index_value);
                    var select_layer_element = this.getElement('select-layer', index_value);
                    var hover_layer_element = this.getElement('hover-layer', index_value);

                    var left_pos = base_layer_element.textWidth() * (index_value - 1)
                        + (base_layer_element.textWidth() - $(face).textWidth()) / 2;

                    $(face).appendTo(this.element).css({
                        display: 'inline-block',
                        position: 'absolute',
                        left: left_pos,
                    }).addClass("rate-face");

                    base_layer_element.css({
                        visibility: 'hidden'
                    });
                    select_layer_element.css({
                        visibility: 'hidden'
                    });
                    hover_layer_element.css({
                        visibility: 'hidden'
                    });
                }

                /*
             * Set styles based on width and value
             */
                if (!this.settings.only_select_one_symbol)
                {
                    var width = this.toWidth(this.value);
                    this.layers.select_layer.css({
                        display: 'block',
                        width: width + "%",
                        height: this.layers.base_layer.css("height")
                    });
                    this.layers.hover_layer.css({
                        display: 'none',
                        height: this.layers.base_layer.css("height")
                    });
                }
                else
                {
                    var width = this.toWidth(this.settings.max_value);
                    this.layers.select_layer.css({
                        display: 'block',
                        width: width + "%",
                        height: this.layers.base_layer.css("height")
                    });
                    this.layers.hover_layer.css({
                        display: 'none',
                        height: this.layers.base_layer.css("height")
                    });
                    this.layers.select_layer.children("span").css({
                        visibility: 'hidden',
                    });
                    this.layers.select_layer.children("span").eq(index_value != 0 ? index_value - 1 : 0).css({
                        visibility: 'visible',
                    });
                }

                // Update the data-rate-value attribute
                $(this.element).attr("data-rate-value", this.value);

                if (this.settings.change_once)
                {
                    this.settings.readonly = true;
                }
                this.updateServer();
                var change_event = $(this.element).trigger("afterChange", {
                    "from": old_value,
                    "to": this.value
                });

                /*
             * Update custom input field if provided
             */
                if (this.settings.hasOwnProperty("update_input_field_name"))
                {
                    this.settings.update_input_field_name.val(this.value);
                }

            }
        }

        Rate.prototype.increment = function()
        {
            this.setValue(this.getValue() + this.settings.step_size);
        }

        Rate.prototype.decrement = function()
        {
            this.setValue(this.getValue() - this.settings.step_size);
        }

        $.fn.rate.settings = {
            max_value: 5,
            step_size: 0.5,
            initial_value: 0,
            symbols: {
                fontawesome_star: {
                    base: '<i class="fa fa-star-o"></i>',
                    hover: '<i class="fa fa-star"></i>',
                    selected: '<i class="fa fa-star"></i>',
                }
            },
            selected_symbol_type: 'utf8_star', // Must be a key from symbols
            convert_to_utf8: false,
            cursor: 'default',
            readonly: false,
            change_once: false, // Determines if the rating can only be set once
            only_select_one_symbol: false, // If set to true, only selects the hovered/selected symbol and nothing prior to it
            ajax_method: 'POST',
            additional_data: {}, // Additional data to send to the server
            //update_input_field_name = some input field set by the user
        };
//load rait
        var options = {
            max_value: 6,
            step_size: 0.5,
            selected_symbol_type: 'hearts',
            url: 'http://localhost/test.php',
            initial_value: 3,
            update_input_field_name: $("#input2"),
        }
        <?

        if($ser==1){//services
            echo '$(".rate1,.rate2,.rate3,.rate4,.rate5").rate({
            selected_symbol_type: 'fontawesome_star',
            max_value: 5,
            step_size: 1
        });';
        }else{//product
            echo '$(".rate1").rate({
            selected_symbol_type: 'fontawesome_star',
            max_value: 5,
            step_size: 1
        });';
        }

        ?>

    //\rait
</script>

Как можете видеть, сами звездочки взяты с GitHub (данный архив будет в архиве с другими файлами ниже), и подключаются с помощью JS, который между //rait ... //\rait в коде выше.

Подключать звездочки необходимо так:

Скачиваете архив (ниже), достаете оттуда stars.zip, распаковываете, берете файл JS rater.js или его уменьшенную версию rater.min.js

Подключаете к странице jQuery (можно и другую версию > 3, если есть уже на странице, то подключите только rater.js) и файл rater.js

<script src="https://code.jquery.com/jquery-1.11.3.min.js" charset="utf-8"></script>
<script src="/rater.js"></script>

Далее, на странице, где необходимо отобразить эти звездочки добавим JS код:

$.fn.rate.settings = {
 max_value: 5,
 step_size: 0.5,
 initial_value: 0,
 symbols: {
 fontawesome_star: {
 base: '<i class="fa fa-star-o"></i>',
 hover: '<i class="fa fa-star"></i>',
 selected: '<i class="fa fa-star"></i>',
 }
 },
 selected_symbol_type: 'utf8_star', // Must be a key from symbols
 convert_to_utf8: false,
 cursor: 'default',
 readonly: false,
 change_once: false, // Determines if the rating can only be set once
 only_select_one_symbol: false, // If set to true, only selects the hovered/selected symbol and nothing prior to it
 ajax_method: 'POST',
 additional_data: {}, // Additional data to send to the server
 //update_input_field_name = some input field set by the user
 };

$(".rate1").rate({
 selected_symbol_type: 'fontawesome_star',
 max_value: 5,
 step_size: 1
 });

Обратите внимание! Подключен шрифт fontawesome

Вы можете создать свой класс для звездочек, нарисовать их или взять готовое изображение и фантазировать как угодно, но мне было удобно взять именно FA, т.к. он уже был на сайте.

Также необходимо подключить стили

<link rel="stylesheet" href="/css/styleRater.css">

Файлы HTML+JS+CSS лежат в папке DEMO
stars.zip — это https://github.com/auxiliary/rater

first.sql – это пример базы данных, с DEMO данными

Файлы PHP даны в пример выполнения сценария PHP.
А именно, добавлены файлы, которые так или иначе имеют отношение к системе отзывов на сайте, который показан в видео и речь о котором идет в этом материале.

Итак, как Вы подгрузите форму для комментариев – решать Вам. То ли это будет AJAX запрос, то ли – готовый вывод через PHP.

Важно просто соблюсти HTML, а также подключить JS, CSS, и постараться изменить CSS, если названия классов совпадают.

Логика выполнения скрипта у меня:

1. commHtml.php

<?
#здесь важно все. ниже - это 5 звезд или 1, услуга - 5, товар - одна
$ser=0;//не услуги, а товар
if($_POST['ps']=='service'){
 $ser=1;//услуги, а не товар
}
$id=(int)$_POST['id'];
?>



<div class="br3 p4 aj_commHtml">
 <h3><i class="fa-comment"></i> Добавить отзыв</h3>
 <div>
 <textarea type="text" class="br3 txt" cols="92" placeholder="Текст сообщения" maxlength="1000"></textarea>
 <span class="gry txtInputbcomm">Осталось: 1000 символов</span>
 </div>
 <div>

 <?
 //если оцениваем услугу
 if($ser==1){
 echo '
 <div class="p4 aj_commD">
 <h4 class="fB">Предлагаем Вам оценить качество предоставляемых услуг для данного поставщика</h4>

 <div class="mv320 tableDiv">
 <div class="table">

 <div class="tr">
 <div class="tbc brdrb">
 Качество:
 </div>
 <div class="tbc brdrb">
 <div class="in rate1"></div>
 </div>
 </div>


 <div class="tr">
 <div class="tbc brdrb">
 Вежливость:
 </div>
 <div class="tbc brdrb">
 <div class="in rate2"></div>
 </div>
 </div>


 <div class="tr">
 <div class="tbc brdrb">
 Пунктуальность:
 </div>
 <div class="tbc brdrb">
 <div class="in rate3"></div>
 </div>
 </div>


 <div class="tr">
 <div class="tbc brdrb">
 Время отклика на заказ:
 </div>
 <div class="tbc brdrb">
 <div class="in rate4"></div>
 </div>
 </div>


 <div class="tr">
 <div class="tbc">
 Цена:
 </div>
 <div class="tbc">
 <div class="in rate5"></div>
 </div>
 </div>
 </div>
 </div>
</div>
 ';
 }else{
 //если оцениваем товар
 echo '<div class="p4 aj_commD">
 <h4 class="fB">Предлагаем Вам оценить данный товар</h4>

 <div class="mv320 tableDiv">
 <div class="table">

 <div class="tr">
 <div class="tbc brdrb">
 Ваша оценка:
 </div>
 <div class="tbc brdrb">
 <div class="in rate1"></div>
 </div>
 </div>
 </div>
 </div>
</div>';
 }


 ?>

 </div>
 <div class="br3 p4 ajx_err" style="display:none"></div>
 <div class="br3 p4 ajx_goo" style="display:none"></div>
 <div class="fc fsa fw">
 <div class="br3 cp p_commBtn p_commBS" data-service="<?=$ser?>" data-id="<?=$id?>"><i class="fa-check-circle"></i> Отправить</div>
 <!--<div class="br3 cp p_commBtn p_commBR"><i class="fa-refresh"></i> Обновить отзывы</div>-->
 </div>
 <div class="mh10 fB gry"><i class="fa-info-circle"></i> <a href="/text/comments-rules" target="_blank">Прочитайте правила для отзывов</a></div>
</div>
<script>

 $('.p_commBS').on('click',function () {
 var a,b,c,d,e,f,g=1,h=$(this).data('service'),j=$(this).data('id');
 if(h==0){//product
 a=$('.rate1').data('rate-value')
 if(a===undefined){
 $('.warning').html('Вы не оценили данный товар. Оцените его').slideDown(200)
 g = 0
 return false
 }
 f=[a]
 }else {//service
 a = $('.rate1').data('rate-value')
 b = $('.rate2').data('rate-value')
 c = $('.rate3').data('rate-value')
 d = $('.rate4').data('rate-value')
 e = $('.rate5').data('rate-value')
 f = [a, b, c, d, e]

 $.each(f, function (k, v) {
 if (v === undefined) {
 $('.warning').html('Вы не оценили один из критериев или несколько, оцените его/их').slideDown(200)
 g = 0
 return false
 } else {
 $('.warning').slideUp(100)
 }
 })
 };//\service
 if($('.aj_commHtml .txt').val().length<21)$('.warning').html('Текст отзыва должен содержать не менее 20 символов. Заполните текстовое поле.').slideDown(100),g=0

 if(g==1){
 $.ajax({
 url: '/ajx',
 data: {location_:location.href,set:'setFrmComm',vote:f,txt:$('.aj_commHtml .txt').val(),service:h,id:j},
 type: 'POST',
 beforeSend:function () {
 $('.loading').slideDown(0);
 },
 success: function(d){
console.log(d)
 $('.ajx_goo,.ajx_err').slideUp(50)
 var jsn=JSON.parse(d);
 if(jsn.txt=='success'){
 $('.ajx_goo').slideDown(100).html('<i class="fa-bullhorn"></i> Ваш отзыв успешно добавлен. После проверки нашими сотрудниками, он повится на сайте. Ожидайте...<br>Благодарим за взаимодействие с нами!')
 $('.p_loadComFrm').first().after(jsn.comment)
 $('.txt,.p_commBS,.p_btnLdFrmCom,.aj_commD').slideUp(50, function() {
 $('.txt,.p_commBS,.p_btnLdFrmCom,.aj_commHtml h3,.txtInputbcomm,.aj_commD').remove()
 });
 $('html, body').animate({
 scrollTop:$('.ajx_otz').offset().top-$(window).height()/2
 }, 1000);
 g=0
 }
 if(jsn.txt!='success'){
 $('.ajx_err').slideDown(100).html('<i class="fa-ban"></i> '+jsn.txt)
 g=1
 }

 $('.loading').slideUp(0);
 return false
 },
 error: function(){
 alert('Ошибка. Попробуйте позже...');
 $('.loading').slideUp(0);
 }
 });
 }

 });

 //Count how many characters left
 $('.txt').on('keyup paste change input propertychange',function(){
 countTxt($('.txt'),$('.txtInputbcomm'));
 if($('.aj_commHtml .txt').val().length>20)$('.warning').slideUp(100)
 });
 function countTxt(id,span){
 var txtl =id.val().length;
 var tl=1000-txtl;
 if(tl>0){
 span.text('Осталось символов: '+tl)
 }else{span.text('Введено максимальное количество символов: '+txtl)}
 };
 //\//Count how many characters left

 //rait
 /*
 * A highly customizable rating widget that supports images, utf8 glyphs and other html elements!
 * https://github.com/auxiliary/rater
 */
 $.fn.textWidth = function()
 {
 var html_calc = $('<span>' + $(this).html() + '</span>');
 html_calc.css('font-size',$(this).css('font-size')).hide();
 html_calc.prependTo('body');
 var width = html_calc.width();
 html_calc.remove();

 if (width == 0)
 {
 var total = 0;
 $(this).eq(0).children().each(function(){
 total += $(this).textWidth();
 });
 return total;
 }
 return width;
 };

 $.fn.textHeight = function()
 {
 var html_calc = $('<span>' + $(this).html() + '</span>');
 html_calc.css('font-size',$(this).css('font-size')).hide();
 html_calc.prependTo('body');
 var height = html_calc.height();
 html_calc.remove();
 return height;
 };

 /*
 * IE8 doesn't support isArray!
 */
 Array.isArray = function (obj) {
 return Object.prototype.toString.call(obj) === "[object Array]";
 };

 /*
 * Utf-32 isn't supported by default, so we have to use Utf-8 surrogates
 */
 String.prototype.getbcodePointLength = function() {
 return this.length-this.split(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g).length+1;
 };

 String.fromCodePoint= function() {
 var chars= Array.prototype.slice.call(arguments);
 for (var i= chars.length; i-->0;) {
 var n = chars[i]-0x10000;
 if (n>=0)
 chars.splice(i, 1, 0xD800+(n>>10), 0xDC00+(n&0x3FF));
 }
 return String.fromCharCode.apply(null, chars);
 };

 /*
 * Starting the plugin itself
 */
 $.fn.rate = function(options)
 {
 if (options === undefined || typeof options === 'object')
 {
 return this.each(function(){
 if (!$.data(this, "rate"))
 {
 $.data(this, "rate", new Rate(this, options));
 }
 });
 }
 else if (typeof options === 'string')
 {
 var args = arguments;
 var returns;
 this.each(function(){
 var instance = $.data(this, "rate");
 if (instance instanceof Rate && typeof instance[options] === 'function')
 {
 returns = instance[options].apply(instance, Array.prototype.slice.call(args, 1));
 }
 if (options === 'destroy')
 {
 // Unbind all events and empty the plugin data from instance
 $(instance.element).off();
 $.data(this, 'rate', null);
 }
 });

 return returns !== undefined ? returns : this;
 }
 };

 function Rate(element, options)
 {
 this.element = element;
 this.settings = $.extend({}, $.fn.rate.settings, options);
 this.set_faces = {}; // value, symbol pairs
 this.build();
 }

 Rate.prototype.build = function()
 {
 this.layers = {};
 this.value = 0;
 this.raise_select_layer = false;

 if (this.settings.initial_value)
 {
 this.value = this.settings.initial_value;
 }
 if ($(this.element).attr("data-rate-value"))
 {
 this.value = $(this.element).attr("data-rate-value");
 }

 /*
 * Calculate the selected width based on the initial value
 */
 var selected_width = this.value / this.settings.max_value * 100;

 /*
 * Let's support single strings as symbols as well as objects
 */
 if (typeof this.settings.symbols[this.settings.selected_symbol_type] === 'string')
 {
 var symbol = this.settings.symbols[this.settings.selected_symbol_type];
 this.settings.symbols[this.settings.selected_symbol_type] = {};
 this.settings.symbols[this.settings.selected_symbol_type]['base'] = symbol;
 this.settings.symbols[this.settings.selected_symbol_type]['selected'] = symbol;
 this.settings.symbols[this.settings.selected_symbol_type]['hover'] = symbol;
 }

 /*
 * Making the three main layers (base, select, hover)
 */
 var base_layer = this.addLayer("base-layer t", 100, this.settings.symbols[
 this.settings.selected_symbol_type]["base"], true);

 var select_layer = this.addLayer("select-layer t", selected_width,
 this.settings.symbols[this.settings.selected_symbol_type]["selected"], true);

 var hover_layer = this.addLayer("hover-layer t", 0, this.settings.symbols[
 this.settings.selected_symbol_type]["hover"], false);

 /* var face_layer = this.addLayer("face-layer", 1, this.settings
 .symbols[this.settings.face_layer_symbol_type][0], true); */

 this.layers["base_layer"] = base_layer;
 this.layers["select_layer"] = select_layer;
 this.layers["hover_layer"] = hover_layer;

 /*
 * Bind the container to some events
 */
 $(this.element).on("mousemove", $.proxy(this.hover, this));
 $(this.element).on("click", $.proxy(this.select, this));
 $(this.element).on("mouseleave", $.proxy(this.mouseout, this));

 /*
 * Set the main element as unselectable
 */
 $(this.element).css({
 "-webkit-touch-callout": "none",
 "-webkit-user-select": "none",
 "-khtml-user-select": "none",
 "-moz-user-select": "none",
 "-ms-user-select": "none",
 "user-select": "none",
 });

 /*
 * Update custom input field if provided
 */
 if (this.settings.hasOwnProperty("update_input_field_name"))
 {
 this.settings.update_input_field_name.val(this.value);
 }
 }

 /*
 * Function to add a layer
 */
 Rate.prototype.addLayer = function(layer_name, visible_width, symbol, visible)
 {
 var layer_body = "<div>";
 for (var i = 0; i < this.settings.max_value; i++)
 {
 if (Array.isArray(symbol))
 {
 if (this.settings.convert_to_utf8)
 {
 symbol[i] = String.fromCodePoint(symbol[i]);
 }
 layer_body += "<span>" + (symbol[i]) + "</span>";
 }
 else
 {
 if (this.settings.convert_to_utf8)
 {
 symbol = String.fromCodePoint(symbol);
 }
 layer_body += "<span>" + symbol + "</span>";
 }
 }
 layer_body += "</div>";
 var layer = $(layer_body).addClass("rate-" + layer_name).appendTo(this.element);

 $(layer).css({
 width: visible_width + "%",
 height: $(layer).children().eq(0).textHeight(),
 overflow: 'hidden',
 position: 'absolute',
 top: 0,
 display: visible ? 'block' : 'none',
 'white-space': 'nowrap'
 });
 $(this.element).css({
 width: $(layer).textWidth() + "px",
 height: $(layer).height(),
 position: 'relative',
 cursor: this.settings.cursor,
 });

 return layer;
 }

 Rate.prototype.updateServer = function()
 {
 if (this.settings.url != undefined)
 {
 $.ajax({
 url: this.settings.url,
 type: this.settings.ajax_method,
 data: $.extend({}, { value: this.getValue() }, this.settings.additional_data),
 success: $.proxy(function(data){
 $(this.element).trigger("updateSuccess", [data]);
 }, this),
 error: $.proxy(function(jxhr, msg, err){
 $(this.element).trigger("updateError", [jxhr, msg, err]);
 }, this)
 });
 }
 }

 Rate.prototype.getValue = function()
 {
 return this.value;
 }

 Rate.prototype.hover = function(ev)
 {
 var pad = parseInt($(this.element).css("padding-left").replace("px", ""));
 var x = ev.pageX - $(this.element).offset().left - pad;
 var val = this.toValue(x, true);

 if (val != this.value)
 {
 this.raise_select_layer = false;
 }

 if (!this.raise_select_layer && !this.settings.readonly)
 {
 var visible_width = this.toWidth(val);
 this.layers.select_layer.css({display: 'none'});
 if (!this.settings.only_select_one_symbol)
 {
 this.layers.hover_layer.css({
 width: visible_width + "%",
 display: 'block'
 });
 }
 else
 {
 var index_value = Math.floor(val);
 this.layers.hover_layer.css({
 width: "100%",
 display: 'block'
 });
 this.layers.hover_layer.children("span").css({
 visibility: 'hidden',
 });
 this.layers.hover_layer.children("span").eq(index_value != 0 ? index_value - 1 : 0).css({
 visibility: 'visible',
 });
 }
 }
 }

 /*
 * Event for when a rating has been selected (clicked)
 */
 Rate.prototype.select = function(ev)
 {
 if (!this.settings.readonly)
 {
 var old_value = this.getValue();
 var pad = parseInt($(this.element).css("padding-left").replace("px", ""));
 var x = ev.pageX - $(this.element).offset().left - pad;
 var selected_width = this.toWidth(this.toValue(x, true));
 this.setValue(this.toValue(selected_width));
 this.raise_select_layer = true;
 }
 }

 Rate.prototype.mouseout = function()
 {
 this.layers.hover_layer.css({display: 'none'});
 this.layers.select_layer.css({display: 'block'});
 }

 /*
 * Takes a width (px) and returns the value it resembles
 */
 Rate.prototype.toWidth = function(val)
 {
 return val / this.settings.max_value * 100;
 }

 /*
 * Takes a value and calculates the width of the selected/hovered layer
 */
 Rate.prototype.toValue = function(width, in_pixels)
 {
 var val;
 if (in_pixels)
 {
 val = width / this.layers.base_layer.textWidth() * this.settings.max_value;
 }
 else
 {
 val = width / 100 * this.settings.max_value;
 }

 // Make sure the division doesn't cause some small numbers added by
 // comparing to a small arbitrary number.
 var temp = val / this.settings.step_size;
 if (temp - Math.floor(temp) < 0.00005)
 {
 val = Math.round(val / this.settings.step_size) * this.settings.step_size;
 }
 val = (Math.ceil(val / this.settings.step_size)) * this.settings.step_size;
 val = val > this.settings.max_value ? this.settings.max_value : val;
 return val;
 }

 Rate.prototype.getElement = function(layer_name, index)
 {
 return $(this.element).find(".rate-" + layer_name + " span").eq(index - 1);
 }

 Rate.prototype.getLayers = function()
 {
 return this.layers;
 }

 Rate.prototype.setFace = function(value, face)
 {
 this.set_faces[value] = face;
 }

 Rate.prototype.setAdditionalData = function(data)
 {
 this.settings.additional_data = data;
 }

 Rate.prototype.getAdditionalData = function()
 {
 return this.settings.additional_data;
 }

 Rate.prototype.removeFace = function(value)
 {
 delete this.set_faces[value];
 }

 Rate.prototype.setValue = function(value)
 {
 if (!this.settings.readonly)
 {
 if (value < 0)
 {
 value = 0;
 }
 else if (value > this.settings.max_value)
 {
 value = this.settings.max_value;
 }

 var old_value = this.getValue();
 this.value = value;

 /*
 * About to change event, should support prevention later
 */
 var change_event = $(this.element).trigger("change", {
 "from": old_value,
 "to": this.value
 });

 /*
 * Set/Reset faces
 */
 $(this.element).find(".rate-face").remove();
 $(this.element).find("span").css({
 visibility: 'visible'
 });
 var index_value = Math.ceil(this.value);
 if (this.set_faces.hasOwnProperty(index_value))
 {
 var face = "<div>" + this.set_faces[index_value] + "</div>";
 var base_layer_element = this.getElement('base-layer', index_value);
 var select_layer_element = this.getElement('select-layer', index_value);
 var hover_layer_element = this.getElement('hover-layer', index_value);

 var left_pos = base_layer_element.textWidth() * (index_value - 1)
 + (base_layer_element.textWidth() - $(face).textWidth()) / 2;

 $(face).appendTo(this.element).css({
 display: 'inline-block',
 position: 'absolute',
 left: left_pos,
 }).addClass("rate-face");

 base_layer_element.css({
 visibility: 'hidden'
 });
 select_layer_element.css({
 visibility: 'hidden'
 });
 hover_layer_element.css({
 visibility: 'hidden'
 });
 }

 /*
 * Set styles based on width and value
 */
 if (!this.settings.only_select_one_symbol)
 {
 var width = this.toWidth(this.value);
 this.layers.select_layer.css({
 display: 'block',
 width: width + "%",
 height: this.layers.base_layer.css("height")
 });
 this.layers.hover_layer.css({
 display: 'none',
 height: this.layers.base_layer.css("height")
 });
 }
 else
 {
 var width = this.toWidth(this.settings.max_value);
 this.layers.select_layer.css({
 display: 'block',
 width: width + "%",
 height: this.layers.base_layer.css("height")
 });
 this.layers.hover_layer.css({
 display: 'none',
 height: this.layers.base_layer.css("height")
 });
 this.layers.select_layer.children("span").css({
 visibility: 'hidden',
 });
 this.layers.select_layer.children("span").eq(index_value != 0 ? index_value - 1 : 0).css({
 visibility: 'visible',
 });
 }

 // Update the data-rate-value attribute
 $(this.element).attr("data-rate-value", this.value);

 if (this.settings.change_once)
 {
 this.settings.readonly = true;
 }
 this.updateServer();
 var change_event = $(this.element).trigger("afterChange", {
 "from": old_value,
 "to": this.value
 });

 /*
 * Update custom input field if provided
 */
 if (this.settings.hasOwnProperty("update_input_field_name"))
 {
 this.settings.update_input_field_name.val(this.value);
 }

 }
 }

 Rate.prototype.increment = function()
 {
 this.setValue(this.getValue() + this.settings.step_size);
 }

 Rate.prototype.decrement = function()
 {
 this.setValue(this.getValue() - this.settings.step_size);
 }

 $.fn.rate.settings = {
 max_value: 5,
 step_size: 0.5,
 initial_value: 0,
 symbols: {
 fontawesome_star: {
 base: '<i class="fa fa-star-o"></i>',
 hover: '<i class="fa fa-star"></i>',
 selected: '<i class="fa fa-star"></i>',
 }
 },
 selected_symbol_type: 'utf8_star', // Must be a key from symbols
 convert_to_utf8: false,
 cursor: 'default',
 readonly: false,
 change_once: false, // Determines if the rating can only be set once
 only_select_one_symbol: false, // If set to true, only selects the hovered/selected symbol and nothing prior to it
 ajax_method: 'POST',
 additional_data: {}, // Additional data to send to the server
 //update_input_field_name = some input field set by the user
 };
//load rait

 <?

 if($ser==1){//services
 echo '$(".rate1,.rate2,.rate3,.rate4,.rate5").rate({
 selected_symbol_type: \'fontawesome_star\',
 max_value: 5,
 step_size: 1
 });';
 }else{//product
 echo '$(".rate1").rate({
 selected_symbol_type: \'fontawesome_star\',
 max_value: 5,
 step_size: 1
 });';
 }

 ?>

 //\rait
</script>
<style>
 .aj_commHtml{background:#efffce}
 .aj_commHtml h3{font:27px/1 Bebas,Arial,sans}
 .aj_commD{background: #f8ffc2}
</style>

2. AjxController.php – через него происходит обработка запроса

<?
if($_SERVER['REQUEST_METHOD']=='POST' && $_SERVER['HTTP_X_REQUESTED_WITH']=='XMLHttpRequest'){

//set comm
 if(!empty($_POST['set'])){
 if($_POST['set'] == 'setFrmComm'){
 $serOrProd = (int)AppController::clearCodeAll($_POST['service']);//1-service,0-product
 if ($serOrProd==1) {
 if (is_array($_POST['vote'])) {
 foreach ($_POST['vote'] as $k => $v) {
 $v = (int)$v;
 if (empty($v)) {
 echo '{"txt":"Вы не оценили один или несколько пунктов"}';
 exit;
 }
 if ($v > 5 OR $v < 1) {
 echo '{"txt":"Вы не оценили один или несколько пунктов"}';
 exit;
 }
 }
 }

$vote = array(
 'a' => AppController::clearCodeAll($_POST['vote'][0]),
 'b' => AppController::clearCodeAll($_POST['vote'][1]),
 'c' => AppController::clearCodeAll($_POST['vote'][2]),
 'd' => AppController::clearCodeAll($_POST['vote'][3]),
 'e' => AppController::clearCodeAll($_POST['vote'][4])
 );
 }else{
 if(!empty(AppController::clearCodeAll($_POST['vote'][0]))) {
 $vote = array(
 'a' => AppController::clearCodeAll($_POST['vote'][0])
 );
 }
 }
 //load comm to DB
 $comm = new Comments();
 $data = $_POST;
 $comm->load($data);
 $data_json = json_encode($vote);
 $uidLogged=(int)$_SESSION['user']['id'];
 if(
 empty(AppController::clearCodeHtml($_POST['txt'])) OR
 AppController::clearCodeHtml(iconv_strlen($_POST['txt'],'UTF-8')) < 19
 ){
 echo '{"txt":"Возможно, данные заполнены не полностью"}';exit;
 }

if(!$comm->validate($data)){
 $comm->getErrors();
 $_SESSION['form_data'] = $data;
 redirect();
 }

if($idComm = $comm->save('comm')) {
 $comm = \R::load('comm', $idComm);

$comm->pub = 2;
 $comm->ip = getenv('REMOTE_ADDR');
 $comm->date = date("Y-m-d H:i:s");
 $comm->rait = $data_json;
 $comm->uid = $uidLogged;
 $comm->ptyp = $serOrProd;
 $comm->prid = (int)AppController::clearCodeAll($_POST['id']);
 $comm->text = trim(AppController::clearCodeHtml($_POST['txt']));
 \R::store($comm);

//show comment to user
 $uNm = '';
 if (isset($_SESSION['user'])) $uNm = $_SESSION['user']['name'];
 $response = [];
 $response['comment'] = '
 <div data-id="' . $comm->id . '" class="pr br3 ovh p_comm">
 <div class="pa z1 br3 t fc p_comNVNotice"><i class="br3 p4 fa-bullhorn"></i> Это Ваш отзыв. После проверки он будет доступен всем пользователям.</div>
 <span class="pr br03 us t o5"><i class="fa-user"></i><span class="autor_name">' . $uNm . '</span></span>
 <div class="in br03 p_comD p_comRait t o5" title="Средняя оценка пользователя">';
 $count=0;
 $raitAr = [];
 $raitJ = json_decode($data_json);
 //если рейтинг услуг
 foreach ($raitJ as $z) {
 $raitAr[] = $z;
 }
 if ($serOrProd == 1) {//service
 $raitInt = $raitAr[0] * 50 * 20;//kach
 $raitInt += $raitAr[1] * 10 * 20;
 $raitInt += $raitAr[2] * 10 * 20;
 $raitInt += $raitAr[3] * 25 * 20;
 $raitInt += $raitAr[4] * 5 * 20;
 $raitInt = $raitInt * 100 / 10000;
 $raitInt = round($raitInt * 5 / 100);
 $count = 5 - $raitInt;
 }else{//product
 $count=5-$raitAr[0];
 }
 //вывод средней оценки, если оценка НЕ 5
 if ($count > 0 AND $count < 5) {
 $i = 1;
 $i2 = 5;
 while ($count < $i2) {
 $response['comment'] .= '<i class="fa-star"></i>';
 $i2--;
 }
 while ($count >= $i) {
 $response['comment'] .= '<i class="gry fa-star-o"></i>';
 $i++;
 }
 } else {
 $i3 = 5;
 //если оценка 5
 while ($count <= $i3) {
 $response['comment'] .= '<i class="fa-star"></i>';
 $i3--;
 }
 }
 $response['comment'].= '</div>
 <div class="in br03 p_comD t o5">'.$comm->date.'</div>
 <div class="ova comment_text t o5">'.$comm->text.'</div>
 </div>
 ';
 //\show comment to user
 $response['txt']='success';
 echo (json_encode($response, JSON_UNESCAPED_UNICODE));

//send admin email
/* $transport = (new Swift_SmtpTransport(App::$app->getProperty('smtp_host'), App::$app->getProperty('smtp_port'), App::$app->getProperty('smtp_protocol')))
 ->setUsername(App::$app->getProperty('smtp_login'))
 ->setPassword(App::$app->getProperty('smtp_password'))
 ;*/
 // Create the Mailer using your created Transport
/* $mailer = new Swift_Mailer($transport);
 $date=date("Y-m-d H:i:s");
 $body = '<!DOCTYPE html>
 <html>
 <head>
 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
 </head>
 <body>
 '.$date.' Пользователь оставил новый отзыв.
 <br><a href="'. App::$app->getProperty('path') .'superintendence/comments/edit?id='.$comm->id.'">Перейти в административную панель и опубликовать его.</a>
 </p>
 </body>
 </html>';

$message_admin = (new Swift_Message($date.' новый отзыв'))
 ->setFrom([App::$app->getProperty('smtp_login') => App::$app->getProperty('shop_name')])
 ->setTo(App::$app->getProperty('admin_email'))
 ->setBody($body, 'text/html')
 ;
 $mailer->send($message_admin);*/
 //\send admin email

exit;
 }
 exit;
 }//setFrmComm
 }

}else{#\xmlHttp
 echo 'err';exit;
 }
 #END IF XML

3. AppController.php очищает код:

<? public static function clearCodeAll($data){
    $array1=array(
        '\'','"','*','?','%','+','=','0x','&','\0',' ',"\n","\r","\s",'\\',',','.','~','`','^','$',';',':','{','}','[',']','(',')','//','http','wss','blob','localhost','|'
    );
    $data=strip_tags($data);
    $data=htmlspecialchars($data,ENT_QUOTES);
    $data=str_ireplace($array1,'',$data);
    return $data;
}
public static function clearCodeSearch($data){
    $array1=array(
        '+','=','0x','\0','\\','~','`','^','$',';',':','{','}','[',']','(',')','//','http','wss','blob','localhost','|'
    );
    $data=strip_tags($data);
    $data=htmlspecialchars($data,ENT_QUOTES);
    $data=str_ireplace($array1,'',$data);
    return $data;
}

public static function clearCodeHtml($data){
    $data=strip_tags($data);
    $data=htmlspecialchars($data,ENT_QUOTES);
    return $data;
}

4. CommentsController.php

Здесь идет обработка рейтинга конкретного товара/услуги и обновление в БД данных о рейтинге.

Также здесь формируется большой JSON, который хранит в себе рейтинг одного товара/услуги в товаре/услуге.

<?//get Rating
 $rait=explode(',',$data['rait']);
 $a1=0;$a2=0;$a3=0;$a4=0;$a5=0;
 $b1=0;$b2=0;$b3=0;$b4=0;$b5=0;
 $c1=0;$c2=0;$c3=0;$c4=0;$c5=0;
 $d1=0;$d2=0;$d3=0;$d4=0;$d5=0;
 $e1=0;$e2=0;$e3=0;$e4=0;$e5=0;

 //если рейтинга еще нет
 if(empty($pUP->rait)){

 if($rait[0]==5)$a1=1;
 if($rait[0]==4)$a2=1;
 if($rait[0]==3)$a3=1;
 if($rait[0]==2)$a4=1;
 if($rait[0]==1)$a5=1;

 if($pUP->uslugi==1) {//service если нет рейт
 if ($rait[1] == 5) $b1 = 1;
 if ($rait[1] == 4) $b2 = 1;
 if ($rait[1] == 3) $b3 = 1;
 if ($rait[1] == 2) $b4 = 1;
 if ($rait[1] == 1) $b5 = 1;

 if ($rait[2] == 5) $c1 = 1;
 if ($rait[2] == 4) $c2 = 1;
 if ($rait[2] == 3) $c3 = 1;
 if ($rait[2] == 2) $c4 = 1;
 if ($rait[2] == 1) $c5 = 1;

 if ($rait[3] == 5) $d1 = 1;
 if ($rait[3] == 4) $d2 = 1;
 if ($rait[3] == 3) $d3 = 1;
 if ($rait[3] == 2) $d4 = 1;
 if ($rait[3] == 1) $d5 = 1;

 if ($rait[4] == 5) $e1 = 1;
 if ($rait[4] == 4) $e2 = 1;
 if ($rait[4] == 3) $e3 = 1;
 if ($rait[4] == 2) $e4 = 1;
 if ($rait[4] == 1) $e5 = 1;

 $rait = array(
 'rait' => calcTtlVoteService($rait, 0, 1,1),
 'raitmark' => array('a' => $rait[0], 'b' => $rait[1], 'c' => $rait[2], 'd' => $rait[3], 'e' => $rait[4]),
 'raitTtl' => 1,
 'raitEvery' => array(
 'a' => array(
 'f' => $a1,//THIS REVERSE (5)
 'g' => $a2,//4
 'h' => $a3,//3
 'i' => $a4,//2
 'j' => $a5//1
 ),
 'b' => array(
 'f' => $b1,
 'g' => $b2,
 'h' => $b3,
 'i' => $b4,
 'j' => $b5,
 ),
 'c' => array(
 'f' => $c1,
 'g' => $c2,
 'h' => $c3,
 'i' => $c4,
 'j' => $c5,
 ),
 'd' => array(
 'f' => $d1,
 'g' => $d2,
 'h' => $d3,
 'i' => $d4,
 'j' => $d5,
 ),
 'e' => array(
 'f' => $e1,
 'g' => $e2,
 'h' => $e3,
 'i' => $e4,
 'j' => $e5,
 )
 )
 );
 }else{//product если рейт нет
 $rait = array(
 'rait' => calcTtlVoteService($rait[0], 0, 1,0),
 'raitmark' => array('a' => $rait[0]),
 'raitTtl' => 1,
 'raitEvery' => array(
 'a' => array(
 'f' => $a1,//THIS REVERSE (5)
 'g' => $a2,//4
 'h' => $a3,//3
 'i' => $a4,//2
 'j' => $a5//1
 )
 )
 );
 }
 $pUP->rait = json_encode($rait);
 \R::store($pUP);
 //empty($pUP->rait)-если рейтинга еще нет
 redirect();
 }else{
 //если рейт уже есть
 //getThisProdAllRaitValuesFromBase
 $raitJSON=json_decode($pUP['rait']);

 function jsonToArr($json){
 $jsonToAr=[];
 foreach ($json as $z) {
 $jsonToAr[]=$z;
 }
 return $jsonToAr;
 }

 /**/
 if($rait[0]==5)$a1=1;
 if($rait[0]==4)$a2=1;
 if($rait[0]==3)$a3=1;
 if($rait[0]==2)$a4=1;
 if($rait[0]==1)$a5=1;

 if($pUP->uslugi==1) {//service если нет рейт
 if ($rait[1] == 5) $b1 = 1;
 if ($rait[1] == 4) $b2 = 1;
 if ($rait[1] == 3) $b3 = 1;
 if ($rait[1] == 2) $b4 = 1;
 if ($rait[1] == 1) $b5 = 1;

 if ($rait[2] == 5) $c1 = 1;
 if ($rait[2] == 4) $c2 = 1;
 if ($rait[2] == 3) $c3 = 1;
 if ($rait[2] == 2) $c4 = 1;
 if ($rait[2] == 1) $c5 = 1;

 if ($rait[3] == 5) $d1 = 1;
 if ($rait[3] == 4) $d2 = 1;
 if ($rait[3] == 3) $d3 = 1;
 if ($rait[3] == 2) $d4 = 1;
 if ($rait[3] == 1) $d5 = 1;

 if ($rait[4] == 5) $e1 = 1;
 if ($rait[4] == 4) $e2 = 1;
 if ($rait[4] == 3) $e3 = 1;
 if ($rait[4] == 2) $e4 = 1;
 if ($rait[4] == 1) $e5 = 1;

 $rait = array(
 'rait' => calcTtlVoteService($rait, $raitJSON->rait, $raitJSON->raitTtl, 1),
 'raitmark' => array('a' => calcTtlVoteServiceNA($rait[0], $raitJSON->raitmark->a, $raitJSON->raitTtl),
 'b' => calcTtlVoteServiceNA($rait[1], $raitJSON->raitmark->b, $raitJSON->raitTtl),
 'c' => calcTtlVoteServiceNA($rait[2], $raitJSON->raitmark->c, $raitJSON->raitTtl),
 'd' => calcTtlVoteServiceNA($rait[3], $raitJSON->raitmark->d, $raitJSON->raitTtl),
 'e' => calcTtlVoteServiceNA($rait[4], $raitJSON->raitmark->e, $raitJSON->raitTtl)),
 'raitTtl' => $raitJSON->raitTtl + 1,
 'raitEvery' => array(
 'a' => array(
 'f' => $a1 + $raitJSON->raitEvery->a->f,
 'g' => $a2 + $raitJSON->raitEvery->a->g,
 'h' => $a3 + $raitJSON->raitEvery->a->h,
 'i' => $a4 + $raitJSON->raitEvery->a->i,
 'j' => $a5 + $raitJSON->raitEvery->a->j
 ),
 'b' => array(
 'f' => $b1 + $raitJSON->raitEvery->b->f,
 'g' => $b2 + $raitJSON->raitEvery->b->g,
 'h' => $b3 + $raitJSON->raitEvery->b->h,
 'i' => $b4 + $raitJSON->raitEvery->b->i,
 'j' => $b5 + $raitJSON->raitEvery->b->j
 ),
 'c' => array(
 'f' => $c1 + $raitJSON->raitEvery->c->f,
 'g' => $c2 + $raitJSON->raitEvery->c->g,
 'h' => $c3 + $raitJSON->raitEvery->c->h,
 'i' => $c4 + $raitJSON->raitEvery->c->i,
 'j' => $c5 + $raitJSON->raitEvery->c->j
 ),
 'd' => array(
 'f' => $d1 + $raitJSON->raitEvery->d->f,
 'g' => $d2 + $raitJSON->raitEvery->d->g,
 'h' => $d3 + $raitJSON->raitEvery->d->h,
 'i' => $d4 + $raitJSON->raitEvery->d->i,
 'j' => $d5 + $raitJSON->raitEvery->d->j
 ),
 'e' => array(
 'f' => $e1 + $raitJSON->raitEvery->e->f,
 'g' => $e2 + $raitJSON->raitEvery->e->g,
 'h' => $e3 + $raitJSON->raitEvery->e->h,
 'i' => $e4 + $raitJSON->raitEvery->e->i,
 'j' => $e5 + $raitJSON->raitEvery->e->j
 )
 )
 );
 }else{//prod если рейт есть
 $rait = array(
 'rait' => calcTtlVoteService($rait, $raitJSON->rait, $raitJSON->raitTtl, 0),
 'raitmark' => array('a' => calcTtlVoteServiceNA($rait[0], $raitJSON->raitmark->a, $raitJSON->raitTtl)),
 'raitTtl' => $raitJSON->raitTtl + 1,
 'raitEvery' => array(
 'a' => array(
 'f' => $a1 + $raitJSON->raitEvery->a->f,
 'g' => $a2 + $raitJSON->raitEvery->a->g,
 'h' => $a3 + $raitJSON->raitEvery->a->h,
 'i' => $a4 + $raitJSON->raitEvery->a->i,
 'j' => $a5 + $raitJSON->raitEvery->a->j
 )
 )
 );
 }
 $pUP->rait = json_encode($rait);
 \R::store($pUP);
 redirect();
 /*\*/
 }

Пример JSON'а, который формируется выше:

{
"rait":3.303125,"raitmark":
{"a":3.5833333333333335,"b":3.4375,"c":2.7916666666666665,"d":2.9375,"e":3.0833333333333335}
,"raitTtl":4,"raitEvery":
{"a":
{"f":2,"g":1,"h":0,"i":0,"j":1}
,"b":
{"f":2,"g":1,"h":0,"i":0,"j":1}
,"c":
{"f":1,"g":0,"h":2,"i":0,"j":1}
,"d":
{"f":1,"g":1,"h":0,"i":1,"j":1}
,"e":
{"f":2,"g":0,"h":0,"i":0,"j":2}
}
}

Где rait - общий рейтинг
raitMark - голоса за 1-5 критериев по среднему показателю
raitTtl - проголосовало всего людей
raitEvery - проголосовало за каждый из критериев раз за каждую оценку.

Например, всего проголосовало 11 человек, за первый критерий проголосовали так:

Пятерку поставили 7 раз, четверку - 1 раз, тройку - 2 раза, двойку - 0 раз, единицу - 0 раз (всего 11). Выглядеть будет так:

{"f":7,"g":1,"h":2,"i":0,"j":0}

Еще пример: всего проголосовало 19 человек, за пятерку - 4 раза, за четверку - 7 раз, за тройку - 2 раза, за двойку - 2 раза, за единицу - 4 раза. Всего - 19. Выглядеть так будет:

{"f":4,"g":7,"h":2,"i":2,"j":4}

Сейчас я понимаю, что нужно было просто сделать так:

{5:7,4:1,3:2,2:0,1:0}

5. view.php – визуализация рейтинга товара/услуги

<?//rating

if(!empty($product->rait)){
 $raitJSON=json_decode($product->rait);
 $rOut='';
 $countR=5-round($raitJSON->rait, 0, PHP_ROUND_HALF_UP);
 $iR=1;$i2R=5;$i3R=4;
 if($countR>0 AND $countR<5){
 while($countR<$i2R){
 $rOut.= '<i class="cg fa-star"></i>';$i2R--;
 }
 while($countR>=$iR){
 $rOut.= '<i class="gry fa-star-o"></i>';$iR++;
 }
 }else{
 //если оценка 5
 while($countR==5){
 $rOut.= '<i class="cg fa-star"></i>';$i3R--;
 }
 }

function setP($val){
 return 100-($val*100/5);
 }

if($usl==1){//service
 echo '<div class="fc fxas mh10 br3 p_rait">
 <div class="p4 bgLg">
 <div class="fs52 tc fB">'.substr($raitJSON->rait, 0, 4).'</div>
 <div class="tc">'.$rOut.'</div>
 <div class="tc gry">общий рейтинг'.
 '<div class="line"></div>Всего проголосовало: <span class="fB">'.$raitJSON->raitTtl.'</span></div>
 </div>
 <div class="mv320 bgDg p4 tableDiv">
 <div class="table">
 <div class="tr">
 <div class="pr z1 tbc brdrb p_rtP">Качество:<div class="pa z- p_rtngGr" style="right:'.setP($raitJSON->raitmark->a).'%"></div></div>
 <div class="tbc brdrb fB">'.substr($raitJSON->raitmark->a, 0, 4).'</div>
 </div>
 <div class="tr">
 <div class="pr z1 tbc brdrb p_rtP">Вежливость:<div class="pa z- p_rtngGr" style="right:'.setP($raitJSON->raitmark->b).'%"></div></div>
 <div class="tbc brdrb fB">'.substr($raitJSON->raitmark->b, 0, 4).'</div>
 </div>
 <div class="tr">
 <div class="pr z1 tbc brdrb p_rtP">Пунктуальность:<div class="pa z- p_rtngGr" style="right:'.setP($raitJSON->raitmark->c).'%"></div></div>
 <div class="tbc brdrb fB">'.substr($raitJSON->raitmark->c, 0, 4).'</div>
 </div>
 <div class="tr">
 <div class="pr z1 tbc brdrb p_rtP">Время отклика на заказ:<div class="pa z- p_rtngGr" style="right:'.setP($raitJSON->raitmark->d).'%"></div></div>
 <div class="tbc brdrb fB">'.substr($raitJSON->raitmark->d, 0, 4).'</div>
 </div>
 <div class="tr">
 <div class="pr z1 tbc p_rtP">Цена:<div class="pa z- p_rtngGr" style="right:'.setP($raitJSON->raitmark->e).'%"></div></div>
 <div class="tbc fB">'.substr($raitJSON->raitmark->e, 0, 4).'</div>
 </div>
 </div>
 </div>
 </div>';
 }else{
 echo '<div class="fc fxas mh10 br3 p_rait">
 <div class="p4 bgLg">
 <div class="fs52 tc fB">'.substr($raitJSON->rait, 0, 4).'</div>
 <div class="tc">'.$rOut.'</div>
 <div class="tc gry">общий рейтинг'.
 '<div class="line"></div>Всего проголосовало: <span class="fB">'.$raitJSON->raitTtl.'</span></div>
 </div>
 </div>';
 }
 }

/*\rating*/?>

Визуализация комментария:

<?

//Отображаем комм
 $serOrProd = $usl;//1-service,0-product
 $raitInt=0;

if(!empty($getAllPComm)){
 $clss='';
 $clss2='';
 foreach ($getAllPComm as $k=>$v){
 if(isset($_SESSION['user'])&&$v['uid']==$_SESSION['user']['id']&&$v['pub']==2){
 $clss=' t p_comNV';
 $txtNV='<div class="pa z1 br3 t fc p_comNVNotice"><i class="br3 p4 fa-bullhorn"></i> Это Ваш отзыв. После проверки он будет доступен всем пользователям.</div>';
 }else{$clss='';$txtNV='';}
 $uNm='';
 $count=0;
 $raitAr = [];
 $raitJ = json_decode($v['rait']);
 //если рейтинг услуг
 foreach ($raitJ as $z) {
 $raitAr[] = $z;
 }
 if ($serOrProd == 1) {//service
 $raitInt = $raitAr[0] * 50 * 20;
 $raitInt += $raitAr[1] * 10 * 20;
 $raitInt += $raitAr[2] * 10 * 20;
 $raitInt += $raitAr[3] * 25 * 20;
 $raitInt += $raitAr[4] * 5 * 20;
 $raitInt = $raitInt * 100 / 10000;
 $raitInt = round($raitInt * 5 / 100);
 $count = 5 - $raitInt;
 }else{//product
 $count=5-$raitAr[0];
 }

foreach ($getAllUsrNmsComm as $k=>$v_){
 if($v_['id']==$v['uid']){
 $uNm=$v_['name'];
 }
 }

//если польз. авторизирован и он оставил отзыв, то отображаем ему отзыв с пометкой на проверке
 //comment=2(new) for logged User
 if(isset($_SESSION['user'])&&$v['uid']==$_SESSION['user']['id']&&$v['pub']==2){
 $out= '
 <div data-id="'.$v['id'].'" class="pr br3 ovh p_comm">
 '.$txtNV.'
 <span class="pr br03 us'.$clss.'"><i class="fa-user"></i><span class="autor_name">'.$uNm.'</span></span>
 <div class="in br03 p_comD p_comRait'.$clss.'" title="Средняя оценка пользователя">';
 //вывод средней оценки, если оценка НЕ 5
 $i=1;$i2=5;$i3=4;
 if($count>0 AND $count<5){
 while($count<$i2){
 $out.= '<i class="fa-star"></i>';$i2--;
 }
 while($count>=$i){
 $out.= '<i class="gry fa-star-o"></i>';$i++;
 }
 }else{
 //если оценка 5
 while($count==5){
 $out.= '<i class="fa-star"></i>';$i3--;
 }
 }
 $out.= '</div>
 <div class="in br03 p_comD'.$clss.'">'.$v['date'].'</div>
 <div class="p4 br3 ova comment_text'.$clss.'">'.nl2br($v['text']).'</div>
 </div>
 ';
 echo $out;
 }//\comment=2 for logged User

//comment=1(одобренные) for All
 if($v['pub']==1){
 $out= '
 <div data-id="'.$v['id'].'" class="pr br3 ovh p_comm">
 '.$txtNV.'
 <span class="pr br03 us'.$clss.'"><i class="fa-user"></i><span class="autor_name">'.$uNm.'</span></span>
 <div class="in br03 p_comD p_comRait'.$clss.'" title="Средняя оценка пользователя">';

//вывод средней оценки, если оценка НЕ 5
 $i=1;$i2=5;$i3=4;
 if($count>0 AND $count<5){
 while($count<$i2){
 $out.= '<i class="fa-star"></i>';$i2--;
 }
 while($count>=$i){
 $out.= '<i class="gry fa-star-o"></i>';$i++;
 }
 }else{
 //если оценка 5
 while($count<=$i3){
 $out.= '<i class="fa-star"></i>';$i3--;
 }
 }
 $out.= '</div>
 <div class="in br03 p_comD'.$clss.'">'.$v['date'].'</div>
 <div class="p4 br3 ova comment_text'.$clss.'">'.nl2br($v['text']).'</div>
 </div>
 ';
 echo $out;
 }//\comment=2 for All

}
 }

Стили [CSS]:



/*rait*/
.rate1,.rate2,.rate3,.rate4,.rate5{min-width: 74px}
.rate-base-layer{color:#aaa}
.rate-hover-layer{color:orange}
.mv320{max-width:320px}
.rate-select-layer,.p_comRait{color:#4caf50}
.t{transition:all .5s}
.p_rtngGr{background:#e5f9bd;box-shadow:inset -1px 0 #a7cc5f}
/*\rait*/

/*div table*/
.brdrb{border-bottom:1px dashed #a2d43e}
.tableDiv{overflow:auto}
.table{display:table;text-align:left}
.tr{display:table-row;transition:all .5s linear}
.tbc{display:table-cell;white-space:nowrap;padding:1px 4px}
@media(max-width:760px){
    .tableDiv{display:block}
    .tbc{white-space:normal}
}
/*\div table*/

/*View rate*/
.p_rait{border:1px solid #ccc}
.p_rait .bgLg{border-right:1px solid #ccc}
.p_rait .brdrb{border-bottom:1px solid #dadada}
.p_rait .p_rtP{background:#fdfffa;box-shadow:inset -1px 0 #4caf50}
.line{width:100%;height:1px;background:#ccc;margin-top:4px}
/*\View rate*/

/*DEMO PAGE*/
@font-face{font-family:'Bebas';src:url("../fonts/BebasNeue.ttf")}

body{font:16px/18px 'Exo 2',Arial,sans}

.aj_commHtml{background:#efffce}
.aj_commHtml h3{font:27px/1 Bebas,Arial,sans}
.aj_commD{background: #f8ffc2}

.in{display: inline-block}

.pa,.us:after{position:absolute;top:0;right:0;bottom:0;left:0}
.p{position:absolute}
.ova{overflow:auto}
.p4{padding:4px}
.br3{border-radius:3px}
.cp{cursor:pointer}

.fs52{font-size:52px}

.fwb{font-weight:bold}

.pr{position:relative}
.tc{text-align:center}
.tj{text-align:justify}

.ovh,.ov{overflow:hidden}
.z1{z-index:1}
.z-{z-index:-1}

.aj_commHtml{max-width:960px;margin: 0 auto}

.fB{font:18px/1 Bebas,Arial,sans}

textarea{border:2px solid #f3f3f5;box-sizing: border-box;height:240px;padding:5px 10px;width:100%;transition:all 0.4s ease;color: inherit;font: inherit;margin: 0}
textarea:focus{border:2px solid #A2D43E;box-shadow:0 0 4px 0 #A2D43E}

.fc,.basketClass:after{display:-webkit-flex;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;-webkit-align-items:center;align-items:center}
.fx{display:-webkit-flex;display:flex}
.fsb{justify-content:space-between}
.fsa{justify-content:space-around}
.fjs{justify-content:flex-start}
.fw{flex-wrap:wrap}
.fe{align-items:flex-end}
.fxas{align-items:stretch}
.fv{align-items:flex-start;justify-content:flex-end}
.faz{align-items:flex-start}
.fg1{flex-grow:1}
.fx6{flex:6}
.fx1{flex:1}

.cg{color:#4caf50}
.bgLg{background:#f7f7f7}
.bgDg{background:#efefef}

.p_commBtn{position:relative;width:auto;display:block;background:#fff;box-sizing:border-box;color:#000;clear:both;text-align:center;text-transform:uppercase;font-size:20px;padding:20px;margin:10px 0;border:1px solid #A2D43E}

.fs14,.sml,.gry,.gry a,.related a,.short-desc{font-size:14px}
.gry a{color:#6f9b19}
.gry,.gry a{color:#666}

.fs52{font-size:52px}

/*wrn*/
.warning,.success{position:fixed;font:16px/22px 'Exo 2',sans;z-index:999999;top:0;left:0;right:0;background:rgba(255,134,0,.95);text-align:center;color:#fff;padding:5px 3px;display:none}
.success{background:#588E00}
.warning:after{content:'\f057';font-family:'FontAwesome';position:absolute;right:3px;opacity:.5;animation:o .7s infinite ease-in-out;-webkit-animation:o .7s infinite ease-in-out;cursor:pointer}
.warning i{-webkit-animation:o 1.5s infinite ease-in-out;animation:o 1.5s infinite ease-in-out}
@keyframes o{50%{opacity:.5}60%{opacity:1}}
@-webkit-keyframes o{50%{opacity:.5}60%{opacity:1}}
.warning:before{content:'';position:absolute;bottom:-3px;left:0;right:0;height:3px;animation:warn 3s infinite ease-in-out;-webkit-animation:warn 3s infinite ease-in-out}
@keyframes warn{0%{box-shadow:inset 0 -3px #FF4B00}20%{box-shadow:inset 0 3px #00D6FF}40%{box-shadow:inset 0 -3px #FF4B00}60%{box-shadow:inset 0 3px #00D6FF}80%{box-shadow:inset 0 -3px #FF4B00}100%{box-shadow:inset 0 3px #00D6FF}}
/*\wrn*/

/*comm*/
.br03,.us:after{-webkit-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px}
.p_comm{min-width:44vw;padding:4px 8px;margin:0 0 10px 0;border:1px solid #efefef}
.comment_text{font-size:17px;margin:10px 0 0 0;line-height:23px}
.autor_name{font-size:13px;padding:4px;color:#fff;margin:0 0 15px 1px}
.p_comD{font-size:10px;padding:4px;margin:-1px 0 0 20px;float:right;background: #e3ffe4;box-shadow:0 -3px}
.us{background:#4caf50;padding:3px 1px 5px 5px;box-shadow:inset 0 2px 3px #063309}
.p_commBtn{position:relative;width:auto;display:block;background:#fff;box-sizing:border-box;color:#000;clear:both;text-align:center;text-transform:uppercase;font-size:20px;padding:20px;margin:10px 0;border:1px solid #A2D43E}
.ajx_err{border:1px solid #FF4B00;background:#ffebeb;border-left-width:3px}
.ajx_goo{border:1px solid #4caf50;background:#e5ffd8;border-left-width:3px;animation:aa 1s infinite .5s}
@keyframes aa{50%{border-left-color:#32fd3a}}
.o5,.p_comNV{opacity:.5}
.p_comNVNotice{background:rgba(255,255,255,.72);border:1px solid #e3ff48}
.p_comm:hover .p_comNVNotice{opacity:0;transform:matrix3d(1,0,0.00,0,0.00,0,1.00,0.001,0,-1,0,0,0,0,0,1)}
.p_comm:hover .p_comNV,.p_comm:hover .o5{opacity:1}
.p_comNVNotice i{background:#313131;margin:0 7px 0 0;color:#fff}
.comment_text{background:#f9f9f9}
.us:after{content:'';z-index:-1;background:#26792a;transform:matrix3d(1,0,0.00,0,0.00,1,0.00,0.005,0,0,1,0,0,0,0,1);left:-3px;bottom:-3px;right:-3px}
/*\comm*/
Система рейтинга на сайт

Файлы Система рейтинга на сайт:

Скачать файл 1
DEMO