Простая кнопка «Нравится» для сайта - Блог chomovva

Мне поставили задачу организовать сбор информации о лояльности пользователей к постам сайта «Проект 90». Такая информация используется для ранжирования постов по популярности. Кнопка «Нравится» от социальных сетей – один из самых простых способов получения такой информации. Но функции социальных сетей обязательно требуют процедуру регистрации и авторизации, а это наверняка отпугнёт пользователя. Дело в том, что основная аудитория сайта «Проект 90» плохо владеет навыками работы за компьютером и в сети интернет. Поэтому кнопка “Нравится” должна быть максимально похожа на аналогичный функционал социальных сетей и при этом не требовать отдельной авторизации. Так же необходима минимальная защита от накрутки и более-менее адекватный учет лайков.

Как это реализовать

Сайт «Проект 90» работает на CSM WordPress, поэтому в разработке учтены особенности именно этой системы.

Подробней о теме, которая используется на сайте проекта: WordPress-тема для сайта посвященного мероприятию.

Я не сторонник использования отдельного плагина для простых задач, поэтому кнопка “Нравится” добавлена в код WordPress-темы, а не подключена одтельно.

Для корректного сбора информации о лайках нужна идентификация пользователя, так как она позволяет избежать повторного голосования. Идентификатор формируется с помощью JavaScript в публичной части сайта на основе данных о браузере и текущем времени. Данные хранятся в локальном хранилище браузера (подробней про localStorage). При нажатии на кнопку «Нравится» идентификатор пользователя передаётся на сервер и записывается в базу данных.

Учёт «лайков» на сервере ведётся с помощью метаполей в таблице wp_postmeta. Чтобы не засорять базу данных метаполя удаляются при деактивации темы.

meta_idpost_idmeta_keymeta_value
auto$post_id'liked'$user_id

Чтобы уменьшить количество запросов на сервер в локальном хранилище браузера создаётся массив идентификаторов понравившихся постов. При загрузки странице с помощью JavaScript сравнивается список идентификаторов загруженных постов со списком понравившихся. На основе этого сравнения добавляются классы-состояния для кнопкок.

После нажатия на кнопку «Мне нравится» на сервер отправляется идентификатор пользователя и идентификатор понравившегося поста. В случае успеха к посту добавляется новое метаполе с идентификатором пользователя. При повторном лайке метаполе удаляется. Количество лайков поста равно количеству метаполей. В браузер возвращается информация о совершенном действии и количество лайков.

Схема работы кнопки Нравится
Работа кнопки Нравится

Серверная часть

Весь php-код подключается в файле functions.php темы.

Формирование кода кнопки

Формирование html-кода кнопки «Нравится» целесообразно вынести в отдельную функцию, которую в дальнейшем будет удобно использовать. Идентификатор поста и количество лайков хранятся в data-аттрибуте кнопки. При клике по кнопке JavaScript код подхватывает идентификатор и передаёт его на сервер.

function get_like_button( $post_id = false ) {
  if ( ! $post_id ) {
    $post_id = get_the_ID();
  }
  $count = count( get_post_meta( $post_id, '_liked', false ) );
  return sprintf(
    '<button class="like" data-like-id="%1$s" data-liked-count="%2$s"><span class="sr-only">%3$s</span></button>',
    $post_id,
    ( empty( $count ) ) ? '' : esc_attr( $count ),
    __( 'Поставить лайк', THEME_TEXTDOMAIN )
  );
}

Обработка ajax запроса

Информацию о нажатии кнопки “Нравится” сервер получает через AJAX. Для обработки такого запроса от всех пользователей нужно воспользоваться хуками wp_ajax_(action) и wp_ajax_nopriv_(action).

Подробней об использовании AJAX на сайте Ajax в WordPress.

function like_processing() {
  // проверяем код безопасности
  if ( isset( $_GET[ 'security' ] ) && wp_verify_nonce( $_GET[ 'security' ], 'liked' ) ) {
    // проверяем есть ли идентификатор поста и пользователя, без этих параметров можно дальше не продолжать
    if ( isset( $_GET[ 'post_id' ] ) && ! empty( $_GET[ 'client_id' ] ) && ! empty( $_GET[ 'client_id' ] ) ) {
      // очищаем идентификатор поста
      $post_id = sanitize_key( $_GET[ 'post_id' ] );
      // очищаем идентификатор пользователя
      $client_id = sanitize_text_field( $_GET[ 'client_id' ] );
      // получаем массив лайком для текущего поста
      $liked = get_post_meta(  $post_id, '_liked', false );
      // инициализируем переменную для ответа браузеру пользователя
      $action = '';
      // провверяем ставил ли уже пользователь лайк текущему посту
      if ( in_array( $client_id, $liked ) ) {
        // усли ставил, то удаляем лайк, и присваиваем соответствующий ответ для переменной $action
        delete_post_meta( $post_id, '_liked', $client_id );
        $action = 'delete';
      } else {
        // добавляем новый лайт и ответ в переменной $action
        add_post_meta( sanitize_key( $_GET[ 'post_id' ] ), '_liked', $client_id, false );
        $action = 'add';
      }
      // формируем ответ браузеру пользователя
      // будет передано количество поставленных лайков и информация
      // о выполненном на сервере действии (добавили или удалили)
      wp_send_json_success( array(
        'action' => $action,
        'count'  => count( get_post_meta(  $post_id, '_liked', false ) ),
      ) );
    }
  }
  // обравыем работу скрипта
  wp_die();
}
// цепляем хуки, action в нашем случае равен 'liked'
add_action( 'wp_ajax_liked', 'like_processing' );
add_action( 'wp_ajax_nopriv_liked', 'like_processing' );

Подключаем js-скрипт лицевой части сайта

Для возможности использования AJAX в лицевой части сайта нужно создать переменную js-переменную ajaxurl. Сделаем это с помощью функции wp_enqueue_script. Таким же способом передадим и код безопасности (nonce-код), который формируется с помощью функции wp_create_nonce. Код безопасности поможет отсеять часть ботов. Для проверки других действий неавторизованных пользователей его использовать бессмысленно.

function liked_scripts() {
  wp_enqueue_script( 'liked', THEME_URL . 'scripts/liked.js', array( 'jquery' ), THEME_VERSION, true );
  wp_localize_script( 'liked', 'ThemeLiked', array(
    'ajaxurl'  => admin_url( 'admin-ajax.php' ),
    'liked'    => wp_create_nonce( 'liked' ),
  ) );
}
add_action( 'wp_enqueue_scripts', 'liked_scripts' );

Использование кнопки в теме

Для добавления кнопки к посту нужно вызвать функцию get_like_button( $post_id ) с параметром идентификатор поста. В цикле WordPress это параметр $post_id не обязателен.

if ( have_post() ) {
  while ( have_post() ) {
    the_post();
    the_title();
    the_excerpt();
    echo get_like_button( get_the_ID() );
  }
}

Клиентская чаcть

Клиентская часть кода находится в файле liked.js. В нём формируется идентификатор пользователя, устанавливаются классы-состояния кнопкам “Нравится” и формируется запрос на сервер. Идентификатор пользователя это по сути hash-код сформированный из строки с информацией о браузере и текущей даты-времени.

jQuery( document ).ready( function () {

  // функция в которой отправляется AJAX запрос на сервер
  function set_like( $button ) {
    // получаем идентификатор поста (хранится в атрибуте кнопки)
    var post_id = $button.attr( 'data-like-id' );
    // создаём AJAX запрос на сервер
    jQuery.ajax( {
      type: 'GET',
      url: ThemeLiked.ajaxurl,
      data: {
        action: 'liked',
        security: ThemeLiked.liked,
        post_id: post_id,
        client_id: client_id,
      },
      // перед отправкой запроса добавляем класс для индикации загрузки
      beforeSend: function () {
        $button.addClass( 'loading' );
      },
      // действия в случае успешного запроса
      success: function ( answer ) {
        if ( answer.data.action == 'add' ) {
          liked[ liked.length ] = Number( post_id );
          $button.addClass( 'liked' );
        } else {
          liked.splice( liked.indexOf( Number( post_id ) ), 1 );
          $button.removeClass( 'liked' );
        }
        $button.attr( 'data-liked-count', answer.data.count );
      },
      error: function ( answer ) {
        console.log( answer );
      },
    // после обработки запроса убираем класс индикации загрузки
    } ).then( function () {
      window.localStorage.setItem( 'liked', JSON.stringify( liked ) );
      $button.removeClass( 'loading' );
    } );
  }

  // функция для формирования хеш-кода
  String.prototype.hashCode = function() {
    var hash = 0, i, chr;
    if ( this.length === 0) return hash;
    for (i = 0; i < this.length; i++) {
      chr   = this.charCodeAt(i);
      hash  = ( (hash << 5 ) - hash ) + chr;
      hash |= 0; // Convert to 32bit integer
    }
    return hash;
  };

  var date = new Date();
  var client_id = window.localStorage.getItem( 'client_id' );
  var liked;

  // если идентификатор клиента не задан, то формируем его
  if ( client_id == null ) {
    client_id = new String( navigator.userAgen + ' ' + date.toString() + ' ' ).hashCode();
    window.localStorage.setItem( 'client_id', client_id );
  }

  // получаем массив понравившихся постов
  if ( window.localStorage.getItem( 'liked' ) == null ) {
    liked = [];
    window.localStorage.setItem( 'liked', JSON.stringify( liked ) )
  } else {
    liked = JSON.parse( window.localStorage.getItem( 'liked' ) );
  }

  // устанавливаем статус "Понравился" выбранным кнопкам
  jQuery( '[data-like-id]' ).each( function ( index, button ) {
    var $button = jQuery( button );
    if ( liked.includes( Number( $button.attr( 'data-like-id' ) ) ) ) {
      $button.addClass( 'liked' );
      if ( $button.attr( 'data-liked-count' ).length == 0 || $button.attr( 'data-liked-count' ) == 0 ) {
        set_like( $button );
      }
    }
  } );

  // утановка событие на клик по кнопкам "Нравится"
  jQuery( 'body' ).on( 'click', '[data-like-id]:not( .loading )', function ( e ) {
    e.preventDefault();
    set_like( jQuery( this ) );    
  } );

} );

Стилевое оформление

Кнопка “Нравится” может находиться в трёх состояниях, это: “лайк не поставлен” (нет класса), “передача данных на сервер” (.loading) и “лайк поставлен” (.liked). Состояния визуализируются с помощью классов, которые добавляет JavaScript в браузере.

Во всех трёх классах используются svg-иконки, для их подбора удобно использовать сервис FlatIcon. Чтобы избежать увеличения времени загрузки при скачивании дополнительных файлов и кешировать иконки вместе с остальным css кодом, иконки конвертированный в BASE64 и добавлены в css.

@keyframes like-loading {
  from {
    transform: rotate( 0deg );
  }
  to {
    transform: rotate( 350deg );
  }
}

.like {

  display: inline-block;
  border: none;
  background-color: transparent;

  &::before {
    content: '';
    width: 1em;
    height: 1em;
    background-position: center center;
    background-repeat: no-repeat;
    background-size: contain;
    max-width: 512px;
    max-height: 512px;
    background-image: url( 'data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIC0yOCA1MTIuMDAxIDUxMiIgd2lkdGg9IjUxMnB4IiBoZWlnaHQ9IjUxMnB4Ij48cGF0aCBkPSJtMjU2IDQ1NS41MTU2MjVjLTcuMjg5MDYyIDAtMTQuMzE2NDA2LTIuNjQwNjI1LTE5Ljc5Mjk2OS03LjQzNzUtMjAuNjgzNTkzLTE4LjA4NTkzNy00MC42MjUtMzUuMDgyMDMxLTU4LjIxODc1LTUwLjA3NDIxOWwtLjA4OTg0My0uMDc4MTI1Yy01MS41ODIwMzItNDMuOTU3MDMxLTk2LjEyNS04MS45MTc5NjktMTI3LjExNzE4OC0xMTkuMzEyNS0zNC42NDQ1MzEtNDEuODA0Njg3LTUwLjc4MTI1LTgxLjQ0MTQwNi01MC43ODEyNS0xMjQuNzQyMTg3IDAtNDIuMDcwMzEzIDE0LjQyNTc4MS04MC44ODI4MTMgNDAuNjE3MTg4LTEwOS4yOTI5NjkgMjYuNTAzOTA2LTI4Ljc0NjA5NCA2Mi44NzEwOTMtNDQuNTc4MTI1IDEwMi40MTQwNjItNDQuNTc4MTI1IDI5LjU1NDY4OCAwIDU2LjYyMTA5NCA5LjM0Mzc1IDgwLjQ0NTMxMiAyNy43Njk1MzEgMTIuMDIzNDM4IDkuMzAwNzgxIDIyLjkyMTg3NiAyMC42ODM1OTQgMzIuNTIzNDM4IDMzLjk2MDkzOCA5LjYwNTQ2OS0xMy4yNzczNDQgMjAuNS0yNC42NjAxNTcgMzIuNTI3MzQ0LTMzLjk2MDkzOCAyMy44MjQyMTgtMTguNDI1NzgxIDUwLjg5MDYyNS0yNy43Njk1MzEgODAuNDQ1MzEyLTI3Ljc2OTUzMSAzOS41MzkwNjMgMCA3NS45MTAxNTYgMTUuODMyMDMxIDEwMi40MTQwNjMgNDQuNTc4MTI1IDI2LjE5MTQwNiAyOC40MTAxNTYgNDAuNjEzMjgxIDY3LjIyMjY1NiA0MC42MTMyODEgMTA5LjI5Mjk2OSAwIDQzLjMwMDc4MS0xNi4xMzI4MTIgODIuOTM3NS01MC43NzczNDQgMTI0LjczODI4MS0zMC45OTIxODcgMzcuMzk4NDM3LTc1LjUzMTI1IDc1LjM1NTQ2OS0xMjcuMTA1NDY4IDExOS4zMDg1OTQtMTcuNjI1IDE1LjAxNTYyNS0zNy41OTc2NTcgMzIuMDM5MDYyLTU4LjMyODEyNiA1MC4xNjc5NjktNS40NzI2NTYgNC43ODkwNjItMTIuNTAzOTA2IDcuNDI5Njg3LTE5Ljc4OTA2MiA3LjQyOTY4N3ptLTExMi45Njg3NS00MjUuNTIzNDM3Yy0zMS4wNjY0MDYgMC01OS42MDU0NjkgMTIuMzk4NDM3LTgwLjM2NzE4OCAzNC45MTQwNjItMjEuMDcwMzEyIDIyLjg1NTQ2OS0zMi42NzU3ODEgNTQuNDQ5MjE5LTMyLjY3NTc4MSA4OC45NjQ4NDQgMCAzNi40MTc5NjggMTMuNTM1MTU3IDY4Ljk4ODI4MSA0My44ODI4MTMgMTA1LjYwNTQ2OCAyOS4zMzIwMzEgMzUuMzk0NTMyIDcyLjk2MDkzNyA3Mi41NzQyMTkgMTIzLjQ3NjU2MiAxMTUuNjI1bC4wOTM3NS4wNzgxMjZjMTcuNjYwMTU2IDE1LjA1MDc4MSAzNy42Nzk2ODggMzIuMTEzMjgxIDU4LjUxNTYyNSA1MC4zMzIwMzEgMjAuOTYwOTM4LTE4LjI1MzkwNyA0MS4wMTE3MTktMzUuMzQzNzUgNTguNzA3MDMxLTUwLjQxNzk2OSA1MC41MTE3MTktNDMuMDUwNzgxIDk0LjEzNjcxOS04MC4yMjI2NTYgMTIzLjQ2ODc1LTExNS42MTcxODggMzAuMzQzNzUtMzYuNjE3MTg3IDQzLjg3ODkwNy02OS4xODc1IDQzLjg3ODkwNy0xMDUuNjA1NDY4IDAtMzQuNTE1NjI1LTExLjYwNTQ2OS02Ni4xMDkzNzUtMzIuNjc1NzgxLTg4Ljk2NDg0NC0yMC43NTc4MTMtMjIuNTE1NjI1LTQ5LjMwMDc4Mi0zNC45MTQwNjItODAuMzYzMjgyLTM0LjkxNDA2Mi0yMi43NTc4MTIgMC00My42NTIzNDQgNy4yMzQzNzQtNjIuMTAxNTYyIDIxLjUtMTYuNDQxNDA2IDEyLjcxODc1LTI3Ljg5NDUzMiAyOC43OTY4NzQtMzQuNjA5Mzc1IDQwLjA0Njg3NC0zLjQ1MzEyNSA1Ljc4NTE1Ny05LjUzMTI1IDkuMjM4MjgyLTE2LjI2MTcxOSA5LjIzODI4MnMtMTIuODA4NTk0LTMuNDUzMTI1LTE2LjI2MTcxOS05LjIzODI4MmMtNi43MTA5MzctMTEuMjUtMTguMTY0MDYyLTI3LjMyODEyNC0zNC42MDkzNzUtNDAuMDQ2ODc0LTE4LjQ0OTIxOC0xNC4yNjU2MjYtMzkuMzQzNzUtMjEuNS02Mi4wOTc2NTYtMjEuNXptMCAwIiBmaWxsPSIjMDAwMDAwIi8+PC9zdmc+Cg==' );
    width: 1.25em;
    height: 1.25em;
    display: inline-block;
    vertical-align: middle;
    opacity: .5;
    transition: transform .2s;
  }

  &:hover::before {
    transform: scale( 1.2 );
  }

  &.liked::before {
    background-image: url( 'data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIC0yOCA1MTIuMDAwMDEgNTEyIiB3aWR0aD0iNTEycHgiIGhlaWdodD0iNTEycHgiPjxwYXRoIGQ9Im01MTIgMTUzLjg2NzE4OGMwIDQzLjI5Mjk2OC0xNi4xMzI4MTIgODIuOTQxNDA2LTUwLjc3MzQzOCAxMjQuNzM0Mzc0LTMwLjk5NjA5MyAzNy4zOTg0MzgtNzUuNTMxMjUgNzUuMzU1NDY5LTEyNy4xMTMyODEgMTE5LjMwODU5NC0xNy42MjUgMTUuMDE1NjI1LTM3LjU5NzY1NiAzMi4wMzkwNjMtNTguMzIwMzEyIDUwLjE3MTg3NS01LjQyOTY4OCA0Ljc1LTEyLjM4NjcxOSA3LjM4NjcxOS0xOS42MTMyODEgNy40Mjk2ODhoLS4xNzk2ODhjLTcuMjg5MDYyIDAtMTQuMzE2NDA2LTIuNjQwNjI1LTE5Ljc5Mjk2OS03LjQzNzUtMjAuNjgzNTkzLTE4LjA4NTkzOC00MC42MjUtMzUuMDg5ODQ0LTU4LjIxODc1LTUwLjA4NTkzOGwtLjA4OTg0My0uMDY2NDA2Yy01MS41NzQyMTktNDMuOTU3MDMxLTk2LjEyODkwNy04MS45MjE4NzUtMTI3LjExNzE4OC0xMTkuMzIwMzEzLTM0LjY0ODQzOC00MS43OTI5NjgtNTAuNzgxMjUtODEuNDQxNDA2LTUwLjc4MTI1LTEyNC43MzQzNzQgMC00Mi4wNjY0MDcgMTQuNDI1NzgxLTgwLjg4MjgxMyA0MC42MTcxODgtMTA5LjI5Mjk2OSAyNi41MDc4MTItMjguNzUgNjIuODc1LTQ0LjU3NDIxOSAxMDIuNDE0MDYyLTQ0LjU3NDIxOSAyOS41NTg1OTQgMCA1Ni42MTcxODggOS4zMzU5MzggODAuNDQ5MjE5IDI3Ljc2MTcxOSAxMi4wMjczNDMgOS4zMDQ2ODcgMjIuOTIxODc1IDIwLjY3OTY4NyAzMi41MTk1MzEgMzMuOTY0ODQzbC4xNzk2ODgtLjIzODI4MWM5LjU1ODU5My0xMy4xODM1OTMgMjAuMzk0NTMxLTI0LjQ4MDQ2OSAzMi4zNDc2NTYtMzMuNzI2NTYyIDIzLjgyNDIxOC0xOC40MjU3ODEgNTAuODk0NTMxLTI3Ljc2MTcxOSA4MC40NDE0MDYtMjcuNzYxNzE5IDM5LjU0Njg3NSAwIDc1LjkxNDA2MiAxNS44MjQyMTkgMTAyLjQxNDA2MiA0NC41NzQyMTkgMjYuMTkxNDA3IDI4LjQxMDE1NiA0MC42MTcxODggNjcuMjE0ODQzIDQwLjYxNzE4OCAxMDkuMjkyOTY5em0wIDAiIGZpbGw9IiNmZjVlOTUiLz48cGF0aCBkPSJtNTEyIDE1My44NjcxODhjMCA0My4yOTI5NjgtMTYuMTMyODEyIDgyLjk0MTQwNi01MC43NzM0MzggMTI0LjczNDM3NC0zMC45OTYwOTMgMzcuMzk4NDM4LTc1LjUzMTI1IDc1LjM1NTQ2OS0xMjcuMTEzMjgxIDExOS4zMDg1OTQtMTcuNjI1IDE1LjAxNTYyNS0zNy41OTc2NTYgMzIuMDM5MDYzLTU4LjMyMDMxMiA1MC4xNzE4NzUtNS40Mjk2ODggNC43NS0xMi4zODY3MTkgNy4zODY3MTktMTkuNjEzMjgxIDcuNDI5Njg4di0zOTQuMDIzNDM4YzkuNTU4NTkzLTEzLjE4MzU5MyAyMC4zOTQ1MzEtMjQuNDgwNDY5IDMyLjM0NzY1Ni0zMy43MjY1NjIgMjMuODI0MjE4LTE4LjQyNTc4MSA1MC44OTQ1MzEtMjcuNzYxNzE5IDgwLjQ0MTQwNi0yNy43NjE3MTkgMzkuNTQ2ODc1IDAgNzUuOTE0MDYyIDE1LjgyNDIxOSAxMDIuNDE0MDYyIDQ0LjU3NDIxOSAyNi4xOTE0MDcgMjguNDEwMTU2IDQwLjYxNzE4OCA2Ny4yMTQ4NDMgNDAuNjE3MTg4IDEwOS4yOTI5Njl6bTAgMCIgZmlsbD0iI2ZmMzk4MCIvPjwvc3ZnPgo=' );
    opacity: 1;
  }

  &::after {
    content: attr( data-liked-count );
    margin-left: .25em;
    color: #000;
  }

  &.loading::after {
    content: '';
    width: 1em;
    height: 1em;
    background-position: center center;
    background-repeat: no-repeat;
    background-size: contain;
    max-width: 512px;
    max-height: 512px;
    width: 1em;
    height: 1em;
    display: inline-block;
    vertical-align: middle;
    transform-origin: center center;
    animation-name: like-loading;
    animation-duration: 1s;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
    animation-fill-mode: backwards;
    background-image: url( 'data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTYuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjUxMnB4IiBoZWlnaHQ9IjUxMnB4IiB2aWV3Qm94PSIwIDAgMzQ0LjM3IDM0NC4zNyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMzQ0LjM3IDM0NC4zNzsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8Zz4KCTxnPgoJCTxwYXRoIGQ9Ik0zMzQuNDg1LDM3LjQ2M2MtNi43NTMtMS40NDktMTMuMzk2LDIuODUzLTE0Ljg0Miw5LjYwM2wtOS4wODQsNDIuMzkxQzI4MS42MzcsNDAuMTE3LDIyOC41NTEsOS4xNTUsMTcwLjM2OCw5LjE1NSAgICBjLTg5LjYwMywwLTE2Mi41LDcyLjg5Ni0xNjIuNSwxNjIuNWMwLDYuOTAzLDUuNTk2LDEyLjUsMTIuNSwxMi41YzYuOTAzLDAsMTIuNS01LjU5NywxMi41LTEyLjUgICAgYzAtNzUuODE4LDYxLjY4Mi0xMzcuNSwxMzcuNS0xMzcuNWM0OS40MjksMCw5NC41MTUsMjYuNDAzLDExOC45MjUsNjguNDQzbC00MS42NzQtOC45MzFjLTYuNzUyLTEuNDQ3LTEzLjM5NiwyLjg1NC0xNC44NDEsOS42MDQgICAgYy0xLjQ0Niw2Ljc1LDIuODU0LDEzLjM5Niw5LjYwNCwxNC44NDJsNzEuNTM2LDE1LjMzYzEuMjE1LDAuMjYxLDIuNDQ5LDAuMzM2LDMuNjY2LDAuMjM0YzIuMDI3LTAuMTcxLDQuMDAzLTAuODM2LDUuNzQzLTEuOTYyICAgIGMyLjc4NC0xLjgwMSw0LjczOC00LjYzNCw1LjQzMy03Ljg3NWwxNS4zMzEtNzEuNTM2QzM0NS41MzUsNDUuNTU1LDM0MS4yMzUsMzguOTExLDMzNC40ODUsMzcuNDYzeiIgZmlsbD0iIzAwMDAwMCIvPgoJCTxwYXRoIGQ9Ik0zMjEuOTA3LDE1NS4yNzFjLTYuODk5LDAuMjI4LTEyLjMwOSw2LjAwNi0xMi4wODEsMTIuOTA1YzEuMjEyLDM2LjcwOC0xMS45NDIsNzEuNjg5LTM3LjA0Miw5OC41MDQgICAgYy0yNS4wOTksMjYuODEyLTU5LjEzNyw0Mi4yNDgtOTUuODQ0LDQzLjQ2Yy0xLjUzLDAuMDUtMy4wNTIsMC4wNzUtNC41NzYsMC4wNzVjLTQ3Ljg5Ni0wLjAwMi05Mi4wMTgtMjQuODc3LTExNi45MzYtNjUuMTggICAgbDQzLjQ0NywxMS42NWM2LjY2OCwxLjc4NywxMy41MjMtMi4xNjgsMTUuMzExLTguODM3YzEuNzg4LTYuNjY4LTIuMTY4LTEzLjUyMi04LjgzNi0xNS4zMTJsLTcwLjY2NC0xOC45NDYgICAgYy0zLjIwMi0wLjg1Ny02LjYxNS0wLjQwOS05LjQ4NSwxLjI0N2MtMi44NzIsMS42NTYtNC45NjcsNC4zODctNS44MjYsNy41ODlMMC40MywyOTMuMDkyICAgIGMtMS43ODgsNi42NjgsMi4xNjgsMTMuNTIyLDguODM2LDE1LjMxMWMxLjA4NSwwLjI5MSwyLjE3MywwLjQzMSwzLjI0NSwwLjQzMWM1LjUxOCwwLDEwLjU2OS0zLjY4NCwxMi4wNjYtOS4yNjdsMTAuNjQ5LTM5LjcxNyAgICBjMjkuNjI0LDQ2LjY0Nyw4MS4xODksNzUuMzY3LDEzNy4xMzIsNzUuMzY1YzEuNzk3LDAsMy42MDQtMC4wMjksNS40MDgtMC4wODljNDMuMzgxLTEuNDM0LDgzLjYwOC0xOS42NzQsMTEzLjI3MS01MS4zNjIgICAgczQ1LjIwOS03My4wMzEsNDMuNzc2LTExNi40MTNDMzM0LjU4NiwxNjAuNDUzLDMyOC44MDUsMTU1LjAyNiwzMjEuOTA3LDE1NS4yNzF6IiBmaWxsPSIjMDAwMDAwIi8+Cgk8L2c+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPC9zdmc+Cg==' );
  }

}

Вывод

Этот метод очень простой и не лишен недостатков связанных с возможностью накрутки, но при желании это можно исправить и усовершенствовать его:

  • добавить виджет или шорткод с выборкой популярных потов;
  • улучшить защиту от накрутки;
  • для идентификации пользователей использовать скрипт google-аналитики;
  • на основе массива идентификаторов понравившихся постов сформировать список избранного

Во время разработки WordPress-темы такую кнопку легко добавить как «дефолтную», а дальнейшем и при необходимости заменить на более продвинутый функционал. Вариантов множество.

Git-проект: https://github.com/chomovva/wp-simple-likes

vote
Article Rating
Подписаться
Уведомление о
guest
0 Комментарий
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x