Лайки и Дизлайки в комментариях постов

Содержание

Просматривая плагины для добавления Лайков и Дизлайков к постам, я обычно скачиваю и смотрю их содержимое. Попадается такой запутанный код, что на свой сайт я такое устанавливать опасаюсь.

В логике исполнения такой функции нет ничего особенного – приделываем кнопки ко всем комментариям, а затем шлём запросы на сервер в бэкенд. В ответе от сервера по технологии AJAX мы получим количество лайков, которые будут храниться в скрытом произвольном поле _os_comment_votes в таблице wp_commentmeta.

Код JavaScript для Frontend

В поисках подходящего хука мне не встретился тот, в котором можно будет легко модифицировать часть кода для блока комментария. Распространённым вариантом вывода комментов в большинстве тем является функция wp_list_comments() располагаемая обычно в файле comments.php.

В документации WordPress предусмотрена возможность изменять шаблон комментария. Но предлагается это сделать заменой всего HTML markup, что, согласитесь, не выглядит надёжным решением. Ведь движок активно развивается, выходят регулярные обновления, которые могут затронуть и данный шаблон.

Сделаем проще: создадим обёртку на JavaScript средствами библиотеки jQuery. Так мы локанично пристроим требуемый код в нужный блок.

В примере я специально соблюдаю WordPress Coding Standards используя латиницу в комментариях для мульти-язычных шаблонов.

/**
 * File main.js.
 */
( function ( $ ) {
	$( document ).ready( function () {

		let html;
		let comment = '#comments article.comment-body';

		// AJAX comment votes markup.
		html  = '<div class="comment-votes btn-group btn-group-sm" role="group" aria-label="Comment votes">';
		html += '<span class="btn btn-secondary px-2 py-0 disabled"><span class="badge badge-secondary">0</span></span>';
		html += '<button type="button" class="btn btn-outline-secondary px-1 py-0 shadow-none"><span class="dashicons dashicons-arrow-up"></span></button>';
		html += '<button type="button" class="btn btn-outline-secondary px-1 py-0 shadow-none"><span class="dashicons dashicons-arrow-down"></span></button>';
		html += '</div>';

		$( html ).appendTo( comment );

		// AJAX votes during page load.
		$.ajax( {
			url: os_ajax.ajax_url,
			type: 'POST',
			dataType: 'json',
			data: {
				action: 'comment_votes',
				event: 'onload',
				nonce_code: os_ajax.ajax_nonce,
			},
			success: function ( response ) {
				$( comment ).each( function () {
					let id    = $( this ).attr( 'id' ).split( '-' ).pop();
					let count = response.data[ id ];

					if ( 'undefined' !== typeof( count ) ) {
						$( this ).find( '.badge' ).text( count );
					}
				} );
			},
		} );

		$( comment ).find( 'button.btn' ).one( 'click', function () {
			let id     = $( this ).closest( comment ).attr( 'id' ).split( '-' ).pop();
			let badge  = $( this ).siblings( 'span' ).find( '.badge' );
			let status = 'like';

			if ( $( this ).find( '.dashicons' ).hasClass( 'dashicons-arrow-down' ) ) {
				status = 'dislike';
			}

			// AJAX votes by click.
			$.ajax( {
				url: os_ajax.ajax_url,
				type: 'POST',
				dataType: 'json',
				data: {
					action: 'comment_votes',
					event: 'click',
					id: id,
					status: status,
					nonce_code: os_ajax.ajax_nonce,
				},
				success: function ( response ) {
					badge.text( response.data );
				},
			} );
		} );
	} );
} )( jQuery );

Данный код я помещаю в файл текущей темы по адресу: assets/js/main.js. Подключаю скрипт через специальный хук wp_enqueue_scripts, для этого открываю файл functions.php и в самый его конец добавляю код:

/**
 * Enqueue scripts and styles.
 */
function onstartup_scripts() {

	// Styles.
	wp_enqueue_style( 'dashicons' );
	wp_enqueue_style( 'bootstrap-style', 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/css/bootstrap.min.css', array(), '4.6.0' );

	// Scripts.
	wp_enqueue_script( 'onstartup-main-js', get_template_directory_uri() . '/assets/js/main.js', array( 'jquery' ), wp_get_theme()->get( 'Version' ), true );

	wp_localize_script(
		'onstartup-main-js',
		'os_ajax',
		array(
			'ajax_url'   => admin_url( 'admin-ajax.php' ),
			'ajax_nonce' => wp_create_nonce( 'ajax-nonce' ),
		)
	);
}
add_action( 'wp_enqueue_scripts', 'onstartup_scripts' );

Вначале будет подключён стиль из встроенного набора WP под названием Dashicons. Это нативный вариант векторных иконок, пригодится для кнопок.

Затем подключается файл стилей Twitter Bootstrap 4 через сервер CDN, но без проверки целостности (CDN integrity). Чтобы не перегружать статью дополнительными кодами, просто посмотрите данный пост.

Следующим подключается выше созданный скрипт main.js. К нему добавляется специальный код создания проверки безопасности, так как данный скрипт работает через AJAX. Библиотека jQuery подключится автоматически.

ВНИМАНИЕ

Вы добавляете код Twitter Bootstrap на свой сайт. Если ваша тема WordPress имеет конфликты с данными стилями, не используйте их.

PHP код серверной части

Приступаем к добавлению бэкенд-части. Код также помещается в файл темы functions.php, и это завершающий этап, можно проверять работу кнопок.

Для предотвращения повторного использования кнопок в браузер пользователя отправляются Куки с номером ID выбранного комментария. Поставить оценку отдельному комментарию можно только 1 раз в течение 30 дней.

/**
 * AJAX comment votes.
 */
function os_ajax_comment_votes() {
	check_ajax_referer( 'ajax-nonce', 'nonce_code' );

	$output = array();
	$event  = ( isset( $_POST['event'] ) ? $_POST['event'] : '' );

	if ( empty( $event ) ) {
		wp_send_json_error();
	}

	if ( 'onload' === $event ) {
		$referer  = wp_get_referer();
		$posts_id = url_to_postid( $referer );
		$comments = get_comments(
			array(
				'post_id'    => $posts_id,
				'meta_query' => array(
					array(
						'key'     => '_os_comment_votes',
						'value'   => '0',
						'compare' => '!=',
					),
				),
			)
		);

		foreach ( $comments as $comment ) {
			$id    = intval( $comment->comment_ID );
			$votes = intval( get_comment_meta( $id, '_os_comment_votes', true ) );

			$output[ $id ] = $votes;
		}
	} elseif ( 'click' === $event ) {
		$id = ( isset( $_POST['id'] ) ? intval( sanitize_text_field( $_POST['id'] ) ) : 0 );

		$cookie_name = 'comment_votes_' . $id;
		$get_cookie  = ( isset( $_COOKIE[ $cookie_name ] ) ? intval( $_COOKIE[ $cookie_name ] ) : 0 );
		$status      = ( isset( $_POST['status'] ) ? $_POST['status'] : '' );

		if ( empty( $id ) || empty( $status ) || $id === $get_cookie ) {
			wp_send_json_error();
		}

		$votes_meta = get_comment_meta( $id, '_os_comment_votes', true );
		$votes      = ( ! empty( $votes_meta ) ) ? intval( $votes_meta ) : 0;
		$count      = ( 'dislike' === $status ) ? -1 : 1;
		$output     = $votes + $count;

		update_comment_meta( $id, '_os_comment_votes', $output );

		setcookie( $cookie_name, $id, time() + 30 * DAY_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true );
	}

	wp_send_json_success( $output );
}
add_action( 'wp_ajax_comment_votes', 'os_ajax_comment_votes' );
add_action( 'wp_ajax_nopriv_comment_votes', 'os_ajax_comment_votes' );

Код стилей CSS

И напоследок можно выровнять кнопки применив CSS FlexBox, помещаем этот код в конец файла style.css:

#comments article.comment-body {
	display: -webkit-box;
	display: -ms-flexbox;
	display: flex;
	-ms-flex-wrap: wrap;
	flex-wrap: wrap;
}

#comments article.comment-body .comment-meta,
#comments article.comment-body .comment-content {
	width: 100%;
}

#comments article.comment-body .comment-votes {
	margin-left: auto;
}

#comments article.comment-body .comment-votes .disabled {
	opacity: 1;
}

Я полностью повторяю свои разработки на данном сайте, можете пройтись по страницам статей, посмотреть комментарии и протестировать работу кнопок Лайк и Дизлайк. Внешний вид кнопок могу со временем изменить, но все скрипты у этой инструкции будут в актуальном состоянии.

Задавайте вопросы в комментариях, буду рад обсудить статью, а возможно и плагин сделаю, если он будет востребован.