Рейтинг для новостей на 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, поэтому вам тоже придется ее подключать, если хотите использовать этот код.
Архив со скриптами и дампом базы данных можете скачать тут.

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


Оценить:
(42 оценок, среднее: 4,14 из 5)

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

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

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

  3. Привет. Спасибо за скрипт. Все работает, но есть вопрос. Как на одной странице новости отобразить рейтинг только для этой конкретной страницы, не отображая рейтинг всех новостей?

    Спасибо.

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

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

*

code