XF 2.3 Модификация файла .php для увеличения рейтинга с 5 до 10 звезд.

Версия XenForo
2.3

nond

Проверенные
Сообщения
285
Реакции
118
Баллы
8,145
Стандартная система рейтинга заточена под 5 звезд.

Проверка на установку значения от 1 до 5 находится в .php файлах разных аддонов:

src\addons\XenAddons\AMS\Entity\ArticleRating.php
src\addons\XenAddons\Showcase\Entity\ItemRating.php
src\addons\XenAddons\UBS\Entity\BlogEntryRating.php
src\addons\XFMG\Entity\Rating.php
src\addons\XFRM\Entity\ResourceRating.php

Вся соль кроется в этой строке 'max' => 5
'rating' => ['type' => self::UINT, 'required' => true, 'min' => 1, 'max' => 5],

Естественно, при ручной замене в исходнике .php значения с 5 на 'max' => 10 дает результат, но:
'rating' => ['type' => self::UINT, 'required' => true, 'min' => 1, 'max' => 10],

В связи с этим возникает несколько проблем:

1. При обновлении плагина файл заменяется и прямые правки пропадают.
2. Инспектор файлов системы орёт красным цветом на "Неожиданное содержание" с указанием ссылок на эти файлы
3. Прямое редактирование .php - не комильфо.

Каким образом можно реализовать такие изменения в нескольких файлах?

Находится это в файлах в описаниях функции примерно так:
PHP:
public static function getStructure(Structure $structure)
    {
        $structure->table = 'xf_xa_ams_article_rating';
        $structure->shortName = 'XenAddons\AMS:ArticleRating';
        $structure->primaryKey = 'rating_id';
        $structure->contentType = 'ams_rating';
        $structure->columns = [
            'rating_id' => ['type' => self::UINT, 'autoIncrement' => true, 'nullable' => true],
            'article_id' => ['type' => self::UINT, 'required' => true],
            'user_id' => ['type' => self::UINT, 'required' => true],
            'username' => ['type' => self::STR, 'maxLength' => 50,
                'required' => 'please_enter_valid_name'
            ],
            'rating' => ['type' => self::UINT, 'required' => true, 'min' => 1, 'max' => 5],
            'title' => ['type' => self::STR, 'default' => '', 'maxLength' => 100],
            'rating_date' => ['type' => self::UINT, 'default' => \XF::$time],
            'rating_state' => ['type' => self::STR, 'default' => 'visible',
                'allowedValues' => ['visible', 'moderated', 'deleted']
            ],
            'is_review' => ['type' => self::BOOL, 'default' => false],
            'pros' => ['type' => self::STR, 'default' => ''],
            'cons' => ['type' => self::STR, 'default' => ''],
            'message' => ['type' => self::STR, 'default' => ''],
            'author_response_contributor_user_id' => ['type' => self::UINT, 'default' => 0],
            'author_response_contributor_username' => ['type' => self::STR, 'maxLength' => 50, 'default' => ''],
            'author_response' => ['type' => self::STR, 'default' => ''],
            'custom_fields' => ['type' => self::JSON_ARRAY, 'default' => []],
            'warning_id' => ['type' => self::UINT, 'default' => 0],
            'warning_message' => ['type' => self::STR, 'default' => '', 'maxLength' => 255],
            'is_anonymous' => ['type' => self::BOOL, 'default' => false],
            'attach_count' => ['type' => self::UINT, 'default' => 0],
            'last_edit_date' => ['type' => self::UINT, 'default' => 0],
            'last_edit_user_id' => ['type' => self::UINT, 'default' => 0],
            'edit_count' => ['type' => self::UINT, 'default' => 0],
            'ip_id' => ['type' => self::UINT, 'default' => 0],
            'embed_metadata' => ['type' => self::JSON_ARRAY, 'nullable' => true, 'default' => null]
        ];
 
Это всё прекрасно. Я обожаю короткие ответы людей связанных с программированием с предложением ссылок для изучения.
Понимаю, критический недостаток времени на ответ. Бывает.
А помимо предложения покурить мануалы, DEV и т.д. в кратце есть идеи, куда копать?
 
В создание плагина и расширение сущности через обработчики событий.
В одной из ранних тем было такое предложение от Mirovinger

И да, один момент не описали, в дополнении присутствует проверка, которая просто не даст установить больше, нужно модифицировать один файл.
src/addons/XFRM/Entity/ResourceRating.php

"не нужно модифицировать, а сделать оверрайд"

В связи с этим возникает вопрос:
Что подразумевается под "оверрайд"?
И если это рабочая тема, нет ли более простого решения для изменения .php, а конкретно значения проверки с 5 до 10 без написания плагина?
 
Нашел еще вариант ответа по возможному решению
Можно переопределить через плагин. В принципе процесс стандартен написанию любого другого плагина который что-то меняет в поведении движка, вот только учитывайте что расширением класса решить вопрос не получится и дополнение будет выглядеть как полное копирование существующей функции и внесение правок в нее. Соответственно если в будущих версиях движка авторы что-то поправят в функции - вам также придется править, иначе будет ломать движок. Но такое высокоуровневое обычно только на крупные релизы трогают.
 
Нужно создать обработчики события entity_structure для нужных сущностей и изменить параметры для нужного столбца. Примеры есть по ссылке выше.
 
Что подразумевается под "оверрайд"?
Перезапись существующего метода своим кодом. Так тоже можно сделать, но Atikin предлагает через обработчик событий

И если это рабочая тема, нет ли более простого решения для изменения .php, а конкретно значения проверки с 5 до 10 без написания плагина?
Нет. Плагин и есть решение, которое группирует всё то, что вы сделали. Плагин это база, а дальше плагин уже может быть или простым, или сложным

Нужно создать обработчики события entity_structure для нужных сущностей и изменить параметры для нужного столбца. Примеры есть по ссылке выше.
Прямо по ссылке вам дали пример, только вам нужно так сделать для, например, src\addons\XFRM\Entity\ResourceRating.php и столбца rating
PHP:
public static function resourceRatingEntityStructure(\XF\Mvc\Entity\Manager $em, \XF\Mvc\Entity\Structure &$structure)
{
    $structure->columns['rating'] = ['type' => self::UINT, 'required' => true, 'min' => 1, 'max' => 10, 'api' => true];
}
Только учитывайте, что указано в оригинальном файле. Параметры могут отличаться
 

Делать «хак» в исходниках — плохая идея​


  • обновление аддона перезапишет правки;
  • File health check («Инспектор файлов») будет ругаться;
  • код-стайл XenForo прямо запрещает редактировать чужие файлы.

В XenForo 2 для таких вещей предусмотрены расширения (class extension) и слушатели события entity_structure.
Мы создаём свой мини-аддон, который в момент построения структуры сущности меняет предел max с 5 на 10. Оригинальные файлы остаются нетронутыми, инспектор спокоен, а при апдейте аддонов наш патч применяется снова.




Шаг 1. Создаём каркас аддона​


bash
КопироватьРедактировать
php cmd.php xf-addon:create ratingTen
# отвечаем на вопросы мастера, получаем папку
# src/addons/RatingTen


(Можно и вручную, главное — чтобы получился addon.json и нужные директории.)




Шаг 2. Регистрируем слушатель entity_structure​


addon.json


jsonc
КопироватьРедактировать
{
"title": "10-Star Ratings",
"description": "Поднимает лимит рейтинга до 10 для AMS, Showcase, UBS, XFMG, XFRM",
"version_id": 1000010,
"version_string": "1.0.0",
"dev": "you",
"listeners": [
{
"event_id": "entity_structure",
"callback_class": "RatingTen\\Listener",
"callback_method": "entityStructure",
"execute_order": 10
}
]
}




Содержимое src/addons/RatingTen/Listener.php​


php
КопироватьРедактировать
<?php

namespace RatingTen;

use XF\Mvc\Entity\Structure;

class Listener
{
/**
* Срабатывает каждый раз, когда XF строит структуру сущности.
*
* @param Structure $structure Структура
* @param string $class Полное имя класса сущности
*/
public static function entityStructure(Structure &$structure, string $class): void
{
// Список нужных сущностей
static $targets = [
\XenAddons\AMS\Entity\ArticleRating::class,
\XenAddons\Showcase\Entity\ItemRating::class,
\XenAddons\UBS\Entity\BlogEntryRating::class,
\XFMG\Entity\Rating::class,
\XFRM\Entity\ResourceRating::class
];

if (!in_array($class, $targets, true))
{
return;
}

// Меняем ограничение поля rating
if (isset($structure->columns['rating']))
{
$structure->columns['rating']['max'] = 10;
}
}
}


  • Одного файла достаточно — он сработает для всех перечисленных сущностей.
  • При желании можно заменить слушатель пятью класс-расширениями — сути не меняет.



Шаг 3. Устанавливаем аддон​


bash
КопироватьРедактировать
php cmd.php xf-addon:install ratingTen


или через панель администратора.
 
  • Мне нравится
Реакции: nond
Нормальные герои всегда идут в обход! (с)
Еще в 2015 Bob Xenaddons реализовал в Showcase оценку автора.

"Элементы Showcase теперь имеют «дополнительную» оценку автора, которая используется, когда сам элемент является «ОБЗОРОМ» (как в случае с постами в этом предложении).

Вместо выбора из 5 звёзд, который позволяет выбрать только ЦЕЛЬНУЮ оценку 1, 2, 3, 4 или 5, добавлен раскрывающийся список с шагом 0,25 от 5,00 до 1,00, то есть 5.00, 4.75, 4.50, 4.25, 4.00, 3.75, 3.50, 3.25 и т. д. Это позволяет вам, если хотите, установить оценку автора 4,25. Кстати, эта оценка полностью отделена от «Оценок пользователей». В дополнении теперь есть 2 набора рейтинговых звёзд."


В текущих шаблонах v.3.2.22 xa_sc_category_add_item прописано так:

HTML:
<xf:macro template="xa_sc_item_edit_macros" name="description" arg-item="{$item}" />


            <xf:if is="$category.allow_author_rating && ($xf.visitor->hasPermission('xa_showcase', 'setAuthorRatingOwn') || $xf.visitor->hasPermission('xa_showcase', 'editAny'))">
                <xf:selectrow name="author_rating" value="{$item.author_rating}" label="{{ phrase('xa_sc_author_rating') }}">
                    <xf:option value="0" label="{{ phrase('xa_sc_no_rating') }}" />
                    <xf:option value="5" label="5.00" />
                    <xf:option value="4.75" label="4.75" />
                    <xf:option value="4.5" label="4.50" />
                    <xf:option value="4.25" label="4.25" />
                    <xf:option value="4" label="4.00" />
                    <xf:option value="3.75" label="3.75" />
                    <xf:option value="3.5" label="3.50" />
                    <xf:option value="3.25" label="3.25" />
                    <xf:option value="3" label="3.00" />
                    <xf:option value="2.75" label="2.75" />
                    <xf:option value="2.5" label="2.50" />
                    <xf:option value="2.25" label="2.25" />
                    <xf:option value="2" label="2.00" />
                    <xf:option value="1.75" label="1.75" />
                    <xf:option value="1.5" label="1.50" />
                    <xf:option value="1.25" label="1.25" />
                    <xf:option value="1" label="1.00" />
                </xf:selectrow>


В шаблоне xa_sc_item_edit

HTML:
<xf:macro template="xa_sc_item_edit_macros" name="description" arg-item="{$item}" />


            <xf:if is="$category.allow_author_rating && $item.canSetAuthorRating()">         
                <xf:selectrow name="author_rating" value="{$item.author_rating}" label="{{ phrase('xa_sc_author_rating') }}">
                    <xf:option value="0" label="{{ phrase('xa_sc_no_rating') }}" />
                    <xf:option value="5" label="5.00" />
                    <xf:option value="4.75" label="4.75" />
                    <xf:option value="4.5" label="4.50" />
                    <xf:option value="4.25" label="4.25" />
                    <xf:option value="4" label="4.00" />
                    <xf:option value="3.75" label="3.75" />
                    <xf:option value="3.5" label="3.50" />
                    <xf:option value="3.25" label="3.25" />
                    <xf:option value="3" label="3.00" />
                    <xf:option value="2.75" label="2.75" />
                    <xf:option value="2.5" label="2.50" />
                    <xf:option value="2.25" label="2.25" />
                    <xf:option value="2" label="2.00" />
                    <xf:option value="1.75" label="1.75" />
                    <xf:option value="1.5" label="1.50" />
                    <xf:option value="1.25" label="1.25" />
                    <xf:option value="1" label="1.00" />
                </xf:selectrow>

На вопрос "можно ли было бы убрать опции 1/4 звезды?"
На своем сайте пост №11 он , что из его шаблона достаточно удалить строки, скажем с шагом 0.25, чтобы оставить с шагом в 50% (0,5)

HTML:
<option value="4.25" {xen:selected "{$item.author_rating} == '4.25' "}>4.25</option>

Полный листинг с его старого примера:
HTML:
<xen:if is="{$category.allow_author_rating}">
            <dl class="ctrlUnit">
                <dt><label for="ctrl_author_rating">{xen:phrase nflj_showcase_author_rating}:</label></dt>
                <dd>
                    <select name="author_rating" class="textCtrl autoSize" id="ctrl_author_rating">
                        <option value="0" {xen:selected "{$item.author_rating} == '0' "}>No Rating</option>

                        <option value="5.00" {xen:selected "{$item.author_rating} == '5.00' "}>5.00</option>

                        <option value="4.75" {xen:selected "{$item.author_rating} == '4.75' "}>4.75</option>
                        <option value="4.50" {xen:selected "{$item.author_rating} == '4.50' "}>4.50</option>
                        <option value="4.25" {xen:selected "{$item.author_rating} == '4.25' "}>4.25</option>
                        <option value="4.00" {xen:selected "{$item.author_rating} == '4.00' "}>4.00</option>

                        <option value="3.75" {xen:selected "{$item.author_rating} == '3.75' "}>3.75</option>
                        <option value="3.50" {xen:selected "{$item.author_rating} == '3.50' "}>3.50</option>
                        <option value="3.25" {xen:selected "{$item.author_rating} == '3.25' "}>3.25</option>
                        <option value="3.00" {xen:selected "{$item.author_rating} == '3.00' "}>3.00</option>

                        <option value="2.75" {xen:selected "{$item.author_rating} == '2.75' "}>2.75</option>
                        <option value="2.50" {xen:selected "{$item.author_rating} == '2.50' "}>2.50</option>
                        <option value="2.25" {xen:selected "{$item.author_rating} == '2.25' "}>2.25</option>
                        <option value="2.00" {xen:selected "{$item.author_rating} == '2.00' "}>2.00</option>

                        <option value="1.75" {xen:selected "{$item.author_rating} == '1.75' "}>1.75</option>
                        <option value="1.50" {xen:selected "{$item.author_rating} == '1.50' "}>1.50</option>
                        <option value="1.25" {xen:selected "{$item.author_rating} == '1.25' "}>1.25</option>
                        <option value="1.00" {xen:selected "{$item.author_rating} == '1.00' "}>1.00</option>
                    </select>
                    <p class="explain">{xen:phrase nflj_showcase_author_rating_explain}</p>
                </dd>
            </dl>
        </xen:if>


Тут подумал, если с переделкой с 5 на 10 могут возникнуть сложности с отображением в шаблонах, насколько сложно провести модификацию нативной системы рейтинга Xenforo и переделать выпадающее меню с выбором от 5,00 до 1,00 с шагом 0,25, т.е. 5.00, 4.75, 4.50, 4.25, 4.00, 3.75, 3.50, 3.25, 3.00, 2.75, 2.50, 2.25 и т.д. как это реализовано в Showcase?

P.S. Пока реализацию макета аддона, предложенного выше не приступал.
P.P.S. А что по-сути может быть проще в реализации: с 5 до 10 как предложено, или с 1 до 5, но с шагом 1/4?
 
В XenForo 2 для таких вещей предусмотрены расширения (class extension) и слушатели события entity_structure.
Мы создаём свой мини-аддон, который в момент построения структуры сущности меняет предел max с 5 на 10. Оригинальные файлы остаются нетронутыми, инспектор спокоен, а при апдейте аддонов наш патч применяется снова.

Вобщем решил вспомнить навыки и потестировать. Собрал предлагаемый вариант аддона as-is.

В шаблоне rating_macros:
Ключ модификации: star10
Описание: Поднимает лимит звёзд рейтинга с 5 до 10

Добавил звезд в шаблон
HTML:
<xf:macro id="stars" arg-rating="!" arg-class="">
    <xf:css src="rating_stars.less" />
 
    <span class="ratingStars {$class}" title="{{ phrase('x_stars', {'rating': $rating|number(2)})|for_attr }}">
        <span class="ratingStars-star{{ $rating >= 1 ? ' ratingStars-star--full' : '' }}{{ ($rating >= 0.5 AND $rating < 1) ? ' ratingStars-star--half' : '' }}"></span>
        <span class="ratingStars-star{{ $rating >= 2 ? ' ratingStars-star--full' : '' }}{{ ($rating >= 1.5 AND $rating < 2) ? ' ratingStars-star--half' : '' }}"></span>
        <span class="ratingStars-star{{ $rating >= 3 ? ' ratingStars-star--full' : '' }}{{ ($rating >= 2.5 AND $rating < 3) ? ' ratingStars-star--half' : '' }}"></span>
        <span class="ratingStars-star{{ $rating >= 4 ? ' ratingStars-star--full' : '' }}{{ ($rating >= 3.5 AND $rating < 4) ? ' ratingStars-star--half' : '' }}"></span>
        <span class="ratingStars-star{{ $rating >= 5 ? ' ratingStars-star--full' : '' }}{{ ($rating >= 4.5 AND $rating < 5) ? ' ratingStars-star--half' : '' }}"></span>
        <span class="ratingStars-star{{ $rating >= 6 ? ' ratingStars-star--full' : '' }}{{ ($rating >= 5.5 AND $rating < 6) ? ' ratingStars-star--half' : '' }}"></span>
        <span class="ratingStars-star{{ $rating >= 7 ? ' ratingStars-star--full' : '' }}{{ ($rating >= 6.5 AND $rating < 7) ? ' ratingStars-star--half' : '' }}"></span>
        <span class="ratingStars-star{{ $rating >= 8 ? ' ratingStars-star--full' : '' }}{{ ($rating >= 7.5 AND $rating < 8) ? ' ratingStars-star--half' : '' }}"></span>
        <span class="ratingStars-star{{ $rating >= 9 ? ' ratingStars-star--full' : '' }}{{ ($rating >= 8.5 AND $rating < 9) ? ' ratingStars-star--half' : '' }}"></span>
        <span class="ratingStars-star{{ $rating >= 10 ? ' ratingStars-star--full' : '' }}{{ ($rating >= 9.5 AND $rating < 10) ? ' ratingStars-star--half' : '' }}"></span>
        <span class="u-srOnly">{{ phrase('x_stars', {'rating': $rating|number(2)}) }}</span>
    </span>
</xf:macro>


В шаблоне rating_macros:
Ключ модификации: star10frase
Описание: Добавляет фразы для позиций с 5 по 10 звёздного рейтинга
HTML:
arg-range="{{ {
        1: phrase('ужасно'),
        2: phrase('плохо'),
        3: phrase('так себе'),
        4: phrase('нормально'),
        5: phrase('средне'),
        6: phrase('выше среднего'),
        7: phrase('хорошо'),
        8: phrase('восхитительно'),
        9: phrase('божественно'),
        10: phrase('идеально')
    } }}">

Установка проходит БЕЗ проблем. Ошибок нет. В Логах тоже.
Пока НЕ работает. Пытаюсь разобраться, где косяк.

Прикол в том, что
$structure->columns['rating']['max'] = 10;
нет такого значения в базе. Логика сравнения 1-5 получается зашита в самом .php
Там в ячейке базы только само значение, например 9 и всё.

Listener.php
PHP:
<?php
    namespace RatingTen;
    use XF\Mvc\Entity\Structure;
    class Listener
    {
    /**
    * Срабатывает каждый раз, когда XF строит структуру сущности.
    * P.S. Должен срабатывать, но пока не работает
    * @param Structure $structure Структура
    * @param string $class Полное имя класса сущности
    */

    public static function entityStructure(Structure &$structure, string $class): void
        {
            // Список сущностей для которых необходимо изменить лимит значения по умолчанию с 5
            static $targets = [
            \XenAddons\AMS\Entity\ArticleRating::class,
            \XenAddons\Showcase\Entity\ItemRating::class,
            \XenAddons\UBS\Entity\BlogEntryRating::class,
            \XFMG\Entity\Rating::class,
            \XFRM\Entity\ResourceRating::class
            ];

            if (!in_array($class, $targets, true))
            {
                return;
            }

            // Меняем ограничение поля rating
            if (isset($structure->columns['rating']))
            {
                $structure->columns['rating']['max'] = 10;
            }


        }
    }

addon.json
Код:
{
    "legacy_addon_id": "",
    "title": "10-star Ratings",
    "description": "Ratings limit UP from standart 5 to 10 for AMS, Showcase, UBS, XFMG, XFRM",
    "version_id":1000010,
    "version_string": "1.0.0",
    "dev": "nond / grisha2217",
    "dev_url": "",
    "faq_url": "",
    "support_url": "",
    "extra_urls": [],
    "require": [],
    "icon": "",
    "listeners": [
            {
            "event_id": "entity_structure",
            "callback_class": "RatingTen\\Listener",
            "callback_method": "entityStructure",
            "execute_order": 10
            }
        ]
}

Возможно ЕЩЁ необходимо добавить вручную Обработчик события
 

Вложения

  • ratingTen.7z
    1.1 KB · Просмотры: 1
Последнее редактирование:
Прямо по ссылке вам дали пример, только вам нужно так сделать для, например, src\addons\XFRM\Entity\ResourceRating.php и столбца rating
PHP:
public static function resourceRatingEntityStructure(\XF\Mvc\Entity\Manager $em, \XF\Mvc\Entity\Structure &$structure)
{
    $structure->columns['rating'] = ['type' => self::UINT, 'required' => true, 'min' => 1, 'max' => 10, 'api' => true];
}
Только учитывайте, что указано в оригинальном файле. Параметры могут отличаться
Я сделал отдельный плагин, где сделал, как предложено.

PHP:
<?php
    namespace RatingTen;
    use XF\Mvc\Entity\Structure;
    class Listener
    {
        public static function ItemRatingEntityStructure(\XF\Mvc\Entity\Manager $em, \XF\Mvc\Entity\Structure &$structure)
        {
        $structure->columns['rating'] = ['type' => self::UINT, 'required' => true, 'min' => 1, 'max' => 10];
        }
    }

Затем создал обработчик в панели управления... и словил

Код:
An exception occurred: [Error] Undefined constant RatingTen\Listener::UINT in src\addons\ratingTen\Listener.php on line 19

Вот на эту строку движок матерится.
$structure->columns['rating'] = ['type' => self::UINT, 'required' => true, 'min' => 1, 'max' => 10, 'api' => true];

Пока не удалил папку плагина, зайти на форум невозможно :)
 
Там константа лежит в другом классе: XF\Mvc\Entity\Entity::UINT
Ну, если я правильно понял, то в админке Разработка > Обработчики событий добавляем новый обработчик события.
Обрабатывать событие: entity_structure
Выполнить обработчик: Класс::Метод
Выполнить обработчик: RatingTen\Listener::Listener - будет ошибка.
Выполнить обработчик: RatingTen\Listener::entityStructure - будет ошибка.
Выполнить обработчик: XF\Mvc\Entity\Structure::entityStructure - будет ошибка.

Я не могу понять, кого все-таки вызывать?
По идее я должен ссылаться на свой RatingTen\Listener::Listener и в нем собственно вся логика и должна срабатывать.

Здесь вероятна ошибка в ранее предложенном названии функции resourceRatingEntityStructure
PHP:
public static function resourceRatingEntityStructure(\XF\Mvc\Entity\Manager $em, \XF\Mvc\Entity\Structure &$structure)

Скорее всего должно быть entityStructure. Могу ошибаться.
PHP:
public static function entityStructure(\XF\Mvc\Entity\Manager $em, \XF\Mvc\Entity\Structure &$structure)

P.S.

По мануалу сделал обработчик так:
Обрабатывать событие: entity_structure
Подсказка события: XenAddons\Showcase
Выполнить обработчик: RatingTen\Listener::StarTenEntityStructure

соответственно изменил в addon.json
"callback_method": "StarTenEntityStructure",
а в Listener.php
public static function StarTenEntityStructure(\XF\Mvc\Entity\Manager $em, \XF\Mvc\Entity\Structure &$structure)
Но... пока не заводится :)
 
Последнее редактирование:
Современный облачный хостинг провайдер | Aéza
Назад
Сверху Снизу