В личку попросили поделиться системой опыта, которая организована на моем сайте, так что напишу это тут, может кому еще будет интересно.
Внимание! Если вы полный ноль в php и не понимаете что тут написано (или вы считаете что мой код - быдлокод) проходите мимо. Разжевать куда и что вставлять, если вы вообще ничего не понимаете я не смогу, в таком случае вам легче найти какой-то плагин с похожим функционалом (например cubepoints)
Система очень примитивная, но возможно кто-то возьмет её за базу и сделает нормальный плагин, ну или просто вдохновиться и напишет все сам.
Итак, начнем с формулы конвертирования опыта в уровень и обратно. В нашем случае это будет:
//Кол-во опыта на уровень function getExpLevel($level) { return pow($level, 10/7) * 100; } //Уровень по опыту function getLevelExp($exp) { return pow($exp / 100, 7/10); }
Пример:
Опыта на уровень нам надо будет:
- 100
- 269
- 480
- 725
- 997
- 1293
- 1612
- 1950
- 2308
- 2683
- ....
А 37856 опыта будут соответствовать уровню 64
Вы можете самостоятельно отредактировать формулу, тогда кол-во опыта на уровень будет другое.
Теперь нам необходимо создать 2 таблицы (вы можете использовать для этого доп. поля профиля, мне удобнее отдельные таблицы)
В нашем случае это будут таблицы:
wp_day_user_exp (со столбцами user_id | date | exp) - таблица в которой будет хранится количества опыта набранного пользователем за сутки (дальше узнаете зачем)
wp_total_user_exp (со столбцами user_id | total_exp ) - таблица с общим опытом пользователя за все время
Теперь нам нужно как-то наполнять таблицы с опытом за активность на сайте. В нашем случае мы будет давать опыт за:
- Повышение рейтинга пользователя
- Если он добавил пост
- Если он добавил комментарий
// Повышаем exp пользователя если ему повышен рейтинг add_action('rcl_insert_rating','rcl_increase_user_exp'); function rcl_increase_user_exp($data){ global $wpdb; $date = date('Y-m-d', strtotime("+3 hours")); // сегодня в формате 2016-02-29 (+3 часа для МСК) $exp = $data['rating_value'] * 2; // Увеличиваем опыт на значение рейтинга х 2 $user_id = $data['object_author']; $exptodb = $wpdb->query("INSERT INTO wp_total_user_exp SET user_id = $user_id,total_exp = $exp ON DUPLICATE KEY UPDATE total_exp=total_exp + $exp"); // заносим общий рейтинг юзера $exptodbbyday = $wpdb->query("INSERT INTO wp_day_user_exp SET user_id = $user_id, date = '$date', exp = $exp ON DUPLICATE KEY UPDATE exp=exp + $exp"); // заносим суточный рейтинг юзера } // Повышаем exp пользователя если он добавит пост любого типа add_action('transition_post_status', 'add_post_exp', '100', '3');// хук срабатывающий при изменении статуса поста function add_post_exp($new_status, $old_status, $post){ if ($new_status == 'publish' && $old_status != 'publish') { // добавляем рейтинг только если пост опубликован // а не обновлен post_increase_user_exp($post); } } function post_increase_user_exp( $post ) { global $wpdb; $date = date('Y-m-d', strtotime("+3 hours")); // сегодня в формате 2016-02-29 (+3 часа для МСК) $user_id = $post->post_author; $posttip = get_post_type($post->ID); if ($posttip == 'post') $exp = 30; // За пост типа "post" даем 30 опыта if ($posttip == 'video') $exp = 5; // За пост типа "video" даем 5 опыта if ($posttip == 'reply') $exp = 5; // За ответ на форуме bbpress даем 5 опыта if ($posttip == 'topic') $exp = 20; // За новую тему на форуме bbpress даем 20 опыта $exptodb = $wpdb->query("INSERT INTO wp_total_user_exp SET user_id = $user_id,total_exp = $exp ON DUPLICATE KEY UPDATE total_exp=total_exp + $exp"); // заносим общий рейтинг юзера $exptodbbyday = $wpdb->query("INSERT INTO wp_day_user_exp SET user_id = $user_id, date = '$date', exp = $exp ON DUPLICATE KEY UPDATE exp=exp + $exp"); // заносим суточный рейтинг юзера } // Повышаем exp пользователя если он добавил комментарий add_action( 'comment_post', 'comment_increase_user_exp', 10, 2 ); function comment_increase_user_exp( $comment_ID, $comment_approved ) { if( 1 === $comment_approved ){ global $wpdb; $date = date('Y-m-d', strtotime("+3 hours")); // сегодня в формате 2016-02-29 (+3 часа для МСК) $comment = get_comment( $comment_ID ); $user_id = $comment->user_id; $exp = 5; // за комментарий даем 5 опыта $exptodb = $wpdb->query("INSERT INTO wp_total_user_exp SET user_id = $user_id,total_exp = $exp ON DUPLICATE KEY UPDATE total_exp=total_exp + $exp"); // заносим общий рейтинг юзера $exptodbbyday = $wpdb->query("INSERT INTO wp_day_user_exp SET user_id = $user_id, date = '$date', exp = $exp ON DUPLICATE KEY UPDATE exp=exp + $exp"); // заносим суточный рейтинг юзера } }
Теперь нам нужна функция которая будет переводить общий опыт пользователя из базы в уровень, вычислять опыт на следующий уровень и возвращать результат в виде готового html кода
function total_user_exp( $user_id ) { $cacheID = 'total_user_exp_'.$user_id; $exparray = wp_cache_get( $cacheID ); // Смотрим есть ли значение в кеше (если у вас стоит плагин // объектного кеширования) if ( false === $exparray ) { // Берем из базы общий опыт пользователя $totalexp = $wpdb->get_var("SELECT total_exp FROM wp_total_user_exp WHERE user_id = $user_id"); // Переводим опыт в уровень $mylevel = floor(getLevelExp($totalexp)); // мой уровень $exptomylevel = round(getExpLevel($mylevel)); // опыта на мой уровень $exptonextlevel = round(getExpLevel($mylevel+1)); // опыта на сл. уровень // Вычисляем сколько % набрано для следующего уровня $percenyearned = round((($totalexp - $exptomylevel) / ($exptonextlevel - $exptomylevel))*100, 2); //Вычисляем ранг по уровню /* Новичок - 0 Пользователь - 5 Активный - 15 Гигант мысли - 30 Маньяк - 45 Старейшина - 60 Знаток - 75 Ветеран - 100 Генерал - 125 Предводитель - 150 */ if ($mylevel < 5) {$rank='Новичок';} elseif ($mylevel < 15) {$rank='Пользователь';} elseif ($mylevel < 30) {$rank='Активный';} elseif ($mylevel < 45) {$rank='Гигант мысли';} elseif ($mylevel < 60) {$rank='Маньяк';} elseif ($mylevel < 75) {$rank='Старейшина';} elseif ($mylevel < 100) {$rank='Знаток';} elseif ($mylevel < 125) {$rank='Ветеран';} elseif ($mylevel < 150) {$rank='Генерал';} elseif ($mylevel < 200) {$rank='Предводитель';} //Массив с данными по уровню о пользователе $user_id $exparray = array( "mylevel" => $mylevel, "myexp" => $totalexp, "exptonextlevel" => $exptonextlevel, "percenyearned" => $percenyearned, "rank" => $rank ); wp_cache_set( $cacheID, $exparray, '', '360'); // Если кеша нет или время вышло - перезаписываем его } $result = "<p class='totalexpp' title='".$exparray['rank']."'>Уровень ".$exparray['mylevel']."</p> <div class='totalexpdiv' title='exp: ".$exparray['myexp']."/".$exparray['exptonextlevel']."'> <p>".$exparray['percenyearned']."%</p> <div class='totalexpdivdiv' style='width:".$exparray['percenyearned']."%;'></div> </div>"; // Возвращаем результат return $result; }
Мои css стили для результата:
.totalexpp { font-size: 13px!important; margin: 10px 0 0px 0!important; } .totalexpdiv { outline: 1px solid #2A2A2A; display: block; height: 15px; width: 100%; background-color: #D2D2D2; box-shadow: inset 0 3px 3px #E8E8E8; position: relative; text-align: center; margin-top: 10px; } .totalexpdiv>p { position: absolute; width: 100%; color: #FFF; text-shadow: 1px 1px 1px #000; font-size: 12px; line-height: 14px; } .totalexpdivdiv{ display: block; height: 15px; box-shadow: inset 0 3px 3px #DA8686; background-color: #822227; }
В результате функция:
total_user_exp( $user_id ); // передаем id пользователя
Вернет что-то типа:
<p class="totalexpp" title="Предводитель">Уровень 169</p> <div class="totalexpdiv" title="exp: 152388/153587"> <p>6.98%</p> <div class="totalexpdivdiv" style="width:6.98%;"></div> </div>
Что будет выглядеть как:
Ну и небольшой бонус. Если вы помните, то мы создавали таблицу с опытом который набирает пользователь за сутки. Давайте используем её содержимое что бы вывести пользователей с наибольшим числом опыта за неделю.
// Берем из базы пользователей с суммой опыта за неделю $allusertop = $wpdb->get_results("SELECT user_id, SUM(exp) FROM wp_day_user_exp WHERE date>=DATE_ADD(CURRENT_TIMESTAMP,INTERVAL -7 DAY) GROUP BY user_id", ARRAY_N); foreach ($allusertop as $oneuser) { // Берем только тех у кого опыт > 0 if ($oneuser[1] > 0) { $usertop[$oneuser[0]] = $oneuser[1]; } } arsort($usertop); // Сортируем пользователей по убыванию опыта echo "<ul class='top-user'>"; foreach ($usertop as $user_id => $exp) { $i++; echo "<li>"; echo "<figure>".get_avatar( $user_id, 120 )."<p>".$i."</p></figure>"; echo "<a href='".get_author_posts_url($user_id)."'>".get_the_author_meta('display_name', $user_id )."</a>"; echo "<p> Набрано опыта: ".$exp."</p>"; echo "</li>"; } echo "</ul>";
В результате получим что-то типа этого:
P.S.
Как вы могли заметить у нас таблицы
wp_day_user_exp
wp_total_user_exp
имеют префикс wp - если у вас он другой, либо впишите свой, либо используйте
$wpdb->prefix
Логика работы понятна, спасибо.
А почему не стали привязывать уровень у значению общего рейтинга пользователя? Там ведь все настройки есть из коробки, останется только забить значения и названия уровней и вычислять их исходя из текущего значения рейтинга пользователя.
Я уже писал нечто подобное, если помните показывалось одно время звание у пользователей, мне тогда не пришлось заводить новые таблицы, а лишь одну доп.настройку в общих настройках плагина.
А вот идея вычисления и вывода текущего опыта в процентах мне понравилась.
Ну тогда надо будет начислять рейтинг за каждую публикацию / комментарий / тему / ответ пользователя, т.к. каждый раз считать сколько у него постов / комментариев не очень хорошо.
Да и задумывалось этот как разные вещи, т.е. рейтинг - показатель качества постов и комментариев пользователя, а его уровень - показатель активности на сайте.
Но наверное можно и к рейтингу привязать, вариантов много. Меня пока устраивает )
Теперь нам необходимо создать 2 таблицы (вы можете использовать для этого доп. поля профиля, мне удобнее отдельные таблицы)
как это сделать - вы можете использовать для этого доп. поля?
А таблицу за месяц можно сделать? Заменить 7 на 30 в INTERVAL -7 DAY ?
Да если заменить 7 на 30 то будут считаться данные за 30 дней