Рейтинг для новостей на ajax+php+mysql

Ajax лайки и дизлайки для записейВ прошлой статье я показывал как можно сделать лайки и дизлаки для новостей. Тема использования ajax весьма актуальна, и поэтому я решил написать еще одну статью о применении ajax.
В этой статье приведен пример создания рейтинга для новостей. Пользователь сможет оценивать запись по шкале от 1 до 5.
Для работы скрипта создадим базу данных с тремя таблицами — пользователи, новости и связь пользователя с голосом. Последняя таблица нужна для того, чтобы делать ограничение по голосования — один пользователь может проголосовать только один раз за статью. Чтобы создать таблицы и наполнить их тестовыми данными можно воспользоваться патчем:

-- Создание таблицы с новостями
CREATE TABLE IF NOT EXISTS `news` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `small_text` varchar(255) NOT NULL,
  `big_text` text NOT NULL,
  `date_create` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `is_active` tinyint(1) NOT NULL,
  `rating` float NOT NULL,
  `count_votes` int(10) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
-- Заполнение новостей
INSERT INTO `news` (`id`, `title`, `small_text`, `big_text`, `date_create`, `is_active`, `rating`, `count_votes`) VALUES
	(1, 'Новость 1', 'короткое описание 1', 'полный текст', '2014-01-24 17:15:38', 1, 0, 0),
	(2, 'Новость 2', 'короткое описание 2', 'полный текст', '2014-01-24 17:15:34', 1, 0, 0),
	(3, 'Новость 3', 'короткое описание 3', 'полный текст', '2014-01-24 17:14:47', 1, 0, 0);
    
-- Создание таблицы для пользователей
CREATE TABLE IF NOT EXISTS `users` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `login` varchar(255) NOT NULL,
  `password` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
-- Создаем тестового пользователя
INSERT INTO `users` (`id`, `login`, `password`) VALUES (1, 'test', 'pass');

-- Таблица для связей пользователей с голосами
CREATE TABLE IF NOT EXISTS `votes_news2user` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `id_user` int(10) NOT NULL,
  `id_news` int(10) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

Теперь создадим страницу для вывода новостей и добавим на нее кнопки для голосования

<style>
#num_vote{
	display: block;
	border: 1px dotted;
    cursor: pointer;
	float: left;
	width: 20px;
	height: 10px;
}
#num_vote:hover{
	border: 1px solid;
	background-color: #FF0000;
}
.current_vote{
	background-color: #00FF00;
}
</style>

<?php
	// подключение к бд
	include "db_connection.php";
	
	$userId = 1; // id пользователя
	// достаем все новости
	$sql = mysql_query("
		SELECT * FROM `news`
    ") or die(mysql_error());    
    $news = array();
    while($r = mysql_fetch_array($sql, MYSQL_ASSOC)){
        $news[] = $r;
    }
?>
<div>
	<input type="hidden" id="id_user" value="<?=$userId;?>" />
	<?php foreach($news as $oneNews){ ?>
	<div class="one_news">
		<h3><?=$oneNews['title'];?></h3>
		<p><?=$oneNews['title'];?></p>
		<div id="votes">
			Rating: <b><?=($oneNews['count_votes'] == 0) ? "Нет оценок" : $oneNews['rating'] . " Голосов: " . $oneNews['count_votes']; ?></b>
			<br/>
			<span <?=($oneNews['rating']>=1) ? 'class="current_vote" current="1"' : 'current="0"';?> id="num_vote" num_vote="1" ></span>
			<span <?=($oneNews['rating']>=2) ? 'class="current_vote" current="1"' : 'current="0"';?> id="num_vote" num_vote="2" ></span>
			<span <?=($oneNews['rating']>=3) ? 'class="current_vote" current="1"' : 'current="0"';?> id="num_vote" num_vote="3" ></span>
			<span <?=($oneNews['rating']>=4) ? 'class="current_vote" current="1"' : 'current="0"';?> id="num_vote" num_vote="4" ></span>
			<span <?=($oneNews['rating']>=5) ? 'class="current_vote" current="1"' : 'current="0"';?> id="num_vote" num_vote="5" ></span>
			<input type="hidden" id="id_news" value="<?=$oneNews['id'];?>" />
			<input type="hidden" id="count_votes" value="<?=$oneNews['count_votes'];?>" />
			<input type="hidden" id="rating" value="<?=$oneNews['rating'];?>" />
		</div>
	</div>
	<hr/>
	<?php } ?>
</div>

Код скрипта для подключения к базе данных храниться в файле db_connection.php. Вот его код:

define("HOST", "localhost");
define("USER", "root");
define("PASSWORD", "");
define("DB_NAME", "test_db");
$db_connect = mysql_connect(HOST, USER, PASSWORD, TRUE); 
mysql_selectdb(DB_NAME,$db_connect);
mysql_set_charset('utf8');

Теперь составим скрипт, который будет обрабатывать аякс запросы и если нужно, то делать записи в базу данных или выводить сообщения об ошибках. Назовем его ajax_test.php:

// подключение к бд
include "db_connection.php";

// контейнер для ошибок 
$error = false;
// получение данных
$userId = (int) $_POST['id_user'];
$newsId = (int) $_POST['id_news'];
$count = (int) $_POST['count'];

// проверяем, голосовал ранее пользователь за эту новость или нет
$sql = mysql_query("
	SELECT count(*) FROM `votes_news2user` WHERE `id_user` = $userId AND `id_news` = $newsId
") or die(mysql_error()); 
$result = mysql_fetch_row($sql);
// если что-то пришло из запроса, значит уже голосовал
//var_dump($result);exit;
if($result[0] > 0){
	$error = 'Вы уже голосовали';
}else{ // если пользователь не голосовал, проголосуем
	// делаем запись о том, что пользователь проголосовал
	mysql_query("
		INSERT INTO `votes_news2user` (`id_user`, `id_news`) VALUES ($userId, $newsId)
	") or die(mysql_error()); 
	// делаем запись для новости - увеличиваем количесво голосов и количество проголосовавших
	mysql_query("
		UPDATE `news` SET `rating` = (`rating` * `count_votes` + $count) / (`count_votes` + 1), `count_votes` = `count_votes` + 1 WHERE `id` = $newsId
	") or die(mysql_error());
}
	
// делаем ответ для клиента
if($error){
	// если есть ошибки то отправляем ошибку и ее текст
	echo json_encode(array('result' => 'error', 'msg' => $error));
}else{
	// если нет ошибок сообщаем об успехе
	echo json_encode(array('result' => 'success'));
}

И наконец напишем javascript, который будет отправлять ajax запросы к скрипту ajax_test.php, а также этот скрипт будет отвечать за некоторые эффекты при наведении на кнопки голосования:

$(document).ready(function() {
	$('span#num_vote').click(function(){
		var count = parseInt($(this).attr('num_vote'));
		setVote(count, $(this));
	});
	
	// Визуализация выбора рейтинга
	// при наведении подсвечиваем нужное количество блоков
	$('span#num_vote').hover(
		function () {
			var countSpan = $(this).parent().find('span#num_vote').length - 1;
			var num_vote = parseInt($(this).attr('num_vote'));
			for(i = 0; i <= countSpan; i++){
				if((num_vote-1) >= i){
					$(this).parent().find('span#num_vote').eq(i).addClass('current_vote');
				}else{
					$(this).parent().find('span#num_vote').eq(i).removeClass('current_vote');
				}
			}
		},
		function () {
			var countSpan = $(this).parent().find('span#num_vote').length - 1;
			for(i = 0; i <= countSpan; i++){
				var element = $(this).parent().find('span#num_vote').eq(i);
				var current = parseInt(element.attr('current'));
				if(current == 1){
					element.addClass('current_vote');
				}else{
					element.removeClass('current_vote');
				}
			}
		}
    );
});

// count - тип голоса. Лайк или дизлайк
// element - кнопка, по которой кликнули
function setVote(count, element){
	// получение данных из полей
	var id_user = $('#id_user').val();
	var id_news = element.parent().find('#id_news').val();
	$.ajax({
		// метод отправки 
		type: "POST",
		// путь до скрипта-обработчика
		url: "/ajax_test.php",
		// какие данные будут переданы
		data: {
			'id_user': id_user, 
			'id_news': id_news,
			'count': count
		},
		// тип передачи данных
		dataType: "json",
		// действие, при ответе с сервера
		success: function(data){
			// в случае, когда пришло success. Отработало без ошибок
			if(data.result == 'success'){	
				// Выводим сообщение
				alert('Голос засчитан');
				// увеличим визуальный счетчик (текст)
				var count_votes = parseInt(element.parent().find('#count_votes').val());
				var rating = element.parent().find('#rating').val();
				var new_rating = ((rating * count_votes) + count) / (count_votes + 1);				
				var new_html = new_rating.toFixed(5) + " Голосов: " + (count_votes + 1);
				element.parent().find('b').html(new_html);
				// увеличим визуальный счетчик (зеленые блоки)
				var floor_rating = Math.floor(new_rating);
				for(i = 0; i <= 4; i++){
					var element_span = element.parent().find('span#num_vote').eq(i);
					if((floor_rating-1) >= i){
						
						element_span.addClass('current_vote');
						element_span.attr('current', 1);
					}else{
						element_span.removeClass('current_vote');
						element_span.attr('current', 0);
					}
				}
			}else{
				// вывод сообщения об ошибке
				alert(data.msg);
			}
		}
	});
}

Вот и все, на этом создания рейтинга закончен. Напоследок хочу обратить внимание, что для работы с ajax я использовал библиотеку jQuery, поэтому вам тоже придется ее подключать, если хотите использовать этот код.
Архив со скриптами и дампом базы данных можете скачать тут.

Рассказать друзьям:


Рейтинг для новостей на ajax+php+mysql: 4 комментария

  1. Здравствуйте! Отличный сайт, много хороших скриптов. Взял Ваш скрипт и немного переделал для своего сайта, пользуюсь с удовольствием. Спасибо большое!

  2. Здравствуйте!
    А можно как-то сделать так, чтобы на главной (например) выводились только результаты рейтинга, т.е. без возможности проголосовать? Ну а в самой статье — всё как тут сделано.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*