Простая система комментариев для сайта на PHP+AJAX

06.11.2012

Общение всегда привлекало пользователей. Ведь люди хотят общаться, делиться впечатлениями и задавать различные вопросы. Но создавая сайт не каждый начинающий разработчик может осуществить систему комментариев у себя на сайте, да и так, чтобы она имела привлекательным вид, кроме того пользователям присваивался персональный аватар. На нашем сайте уже не раз возникали просьбы, объяснить, показать или просто навести пример как данную функцию можно реализовать у себя на сайте.


   

В данном уроке мы расскажем и наглядно покажем как сделать отличную систему комментариев для сайта с помощью PHP ,MySQL и AJAX. Также будет использована интеграция с gravatar.com. Для начала рассмотрим построение структуры  Данный код генерируется PHP  с классом Comment.

demo.php

<div>
<div>
<a href="http://www.rudebox.org.ua">
<img src="http://www.gravatar.com/avatar/112fdf7a8fe3609e7af2cd3873b5c6bd?size=50&default=http%3A%2F%2Fdemo.tutorialzine.com%2F2010%2F06%2Fsimple-ajax-commenting-system%2Fimg%2Fdefault_avatar.gif">
</a>
</div>
<div><a href="http://www.rudebox.org.ua">Имя пользователя</a></div>
<div title="Added at 06:40 on 30 Jun 2010">30 Jun 2010</div>
<p>Текст комментария</p>
</div>

div avatar содержит ссылку (если пользователь ввел правильный URL при размещении комментария) и изображение аватара, которое мы получаем с gravatar.com. Другим важным элементом в XHTML является форма комментария. Она отправляется с помощью POST. Все поля, кроме URL, должны быть заполнены.

demo.php

<div id="addCommentContainer">
<p>Добавить комментарий</p>
<form id="addCommentForm" method="post" action="">
<div>
<label for="name">Имя</label>
<input type="text" name="name" id="name" />
<label for="email">Email</label>
<input type="text" name="email" id="email" />
<label for="url">Вебсайт (не обязательно)</label>
<input type="text" name="url" id="url" />
<label for="body">Содержание комментария</label>
<textarea name="body" id="body" cols="20" rows="5"></textarea>
<input type="submit" id="submit" value="Отправить" />
</div>
</form>
</div>

Форма отправляется с помощью AJAX. Проверка выполняется в фоновом режиме в submit.php. Каждое поле имеет соответствующий элемент label, с установленным атрибутом for.

PHP обрабатывает коммуникацию с базой данных MySQL и генерирует разметку для комментария. Он также получает окончание запроса AJAX и вставляет данные комментария в таблицу comments.

demo.php

/*
/	Выбираем все комментарии и наполняем массив $comments объектами
*/

$comments = array();
$result = mysql_query("SELECT * FROM comments ORDER BY id ASC");

while($row = mysql_fetch_assoc($result))
{
	$comments[] = new Comment($row);
}

Запрос MySQL выбирает все записи из таблицы и заполняет массив $comments объектами класса comment. Данный массив выводится далее при выполнении скрипта.

demo.php

/*
/	Вывод комментариев один за другим:
*/

foreach($comments as $c){
	echo $c->markup();
}

Каждый комментарий имеет метод markup(), который генерирует правильный HTML код, готовый для вывода на страницу. Ниже приведены определения класса и метода. Класс получает строку из базы данных (получаемую с помощью mysql_fetch_assoc() ) и сохраняет ее в переменной  $data. Она доступна только методу класса.

comment.class.php

class Comment
{
	private $data = array();

	public function __construct($row)
	{
		/*
		/	Конструктор
		*/

		$this->data = $row;
	}

	public function markup()
	{
		/*
		/	Данный метод выводит разметку XHTML для комментария
		*/

		// Устанавливаем псевдоним, чтобы не писать каждый раз $this->data:
		$d = &$this->data;

		$link_open = '';
		$link_close = '';

		if($d['url']){

			// Если был введн URL при добавлении комментария,
			// определяем открывающий и закрывающий теги ссылки

			$link_open = '<a href="'.$d['url'].'">';
			$link_close =  '</a>';
		}

		// Преобразуем время в формат UNIX:
		$d['dt'] = strtotime($d['dt']);

		// Нужно для установки изображения по умолчанию:
		$url = 'http://'.dirname($_SERVER['SERVER_NAME'].$_SERVER["REQUEST_URI"]).'/img/default_avatar.gif';

		return '
<div>
<div>
					'.$link_open.'
<img src="http://www.gravatar.com/avatar/'.md5($d['email']).'?size=50&amp;default='.urlencode($url).'" />
					'.$link_close.'
</div>
<div>'.$link_open.$d['name'].$link_close.'</div>
<div title="Added at '.date('H:i \o\n d M Y',$d['dt']).'">'.date('d M Y',$d['dt']).'</div>
<p>'.$d['body'].'</p>
</div>
		';
	}

Скрипт определяет адрес URL, на котором выполняется, и определяет точный адрес изображения default_avatar.gif. Данное изображение передается на параллельно с хэшем md5, и если никакого аватар не было найдено для переданного email адреса, то будет выведено альтернативное изображение.

comment.class.php

public static function validate(&$arr)
{
	/*
	/	Данный метод используется для проверки данных отправляемых через AJAX.
	/
	/	Он возвращает true/false в зависимости от правильности данных, и наполняет
	/	массив $arr, который преается как параметр либо данными либо сообщением об ошибке.
	*/

	$errors = array();
	$data	= array();

	// Используем функцию filter_input, введенную в PHP 5.2.0

	if(!($data['email'] = filter_input(INPUT_POST,'email',FILTER_VALIDATE_EMAIL)))
	{
		$errors['email'] = 'Пожалуйста, введите правильный Email.';
	}

	if(!($data['url'] = filter_input(INPUT_POST,'url',FILTER_VALIDATE_URL)))
	{
		// Если в поле URL был введн неправильный URL,
		// действуем так, как будто URL не был введен:

		$url = '';
	}

	// Используем фильтр с возвратной функцией:

	if(!($data['body'] = filter_input(INPUT_POST,'body',FILTER_CALLBACK,array('options'=>'Comment::validate_text'))))
	{
		$errors['body'] = 'Пожалуйста, введите текст комментария.';
	}

	if(!($data['name'] = filter_input(INPUT_POST,'name',FILTER_CALLBACK,array('options'=>'Comment::validate_text'))))
	{
		$errors['name'] = 'Пожалуйста, введите имя.';
	}

	if(!empty($errors)){

		// Если есть ошибки, копируем массив $errors в $arr:

		$arr = $errors;
		return false;
	}

	// Если данные введены правильно, подчищаем данные и копируем их в $arr:

	foreach($data as $k=>$v){
		$arr[$k] = mysql_real_escape_string($v);
	}

	// email дожен быть в нижнем регистре:

	$arr['email'] = strtolower(trim($arr['email']));

	return true;

}

Метод validate() (также часть класса) определен как static. Это означает, что его можно вызвать непосредственно с помощью конструкции Comment::validate(), без создания объекта класса. Данный метод проверяет данные, которые передаются через AJAX. Метод использует новую функцию фильтра, которая стала доступна в PHP 5.2.0.. Например, filter_input(INPUT_POST,’url’,FILTER_VALIDATE_URL) означает, что мы проверяем, является ли  $_POST[‘url’] правильным адресом URL. Если это так, то функция возвращает значение переменной, в другом случае она возвращает значение false.

comment.class.php

private static function validate_text($str)
{
	/*
	/	Данный метод используется как FILTER_CALLBACK
	*/

	if(mb_strlen($str,'utf8')<1)
		return false;

	// Кодируем все специальные символы html (<, >, ", & .. etc) и преобразуем
	// символ новой строки в тег <br>:

	$str = nl2br(htmlspecialchars($str));

	// Удаляем все оставщиеся символы новой строки
	$str = str_replace(array(chr(10),chr(13)),'',$str);

	return $str;
}

Последний метод validate_text передаётся в качестве возвратной функции в два вызова filter_input. Он преобразует все специальные символы HTML, что эффективно блокирует атаки XSS. Также он заменяет символы новой строки тегами <br />.

submit.php

/*
/	Данный массив будет наполняться либо данными,
/	которые передаются в скрипт,
/	либо сообщениями об ошибке.
/*/

$arr = array();
$validates = Comment::validate($arr);

if($validates)
{
	/* Все в порядке, вставляем данные в базу: */

	mysql_query("	INSERT INTO comments(name,url,email,body)
					VALUES (
						'".$arr['name']."',
						'".$arr['url']."',
						'".$arr['email']."',
						'".$arr['body']."'
					)");

	$arr['dt'] = date('r',time());
	$arr['id'] = mysql_insert_id();

	/*
	/	Данные в $arr подготовлены для запроса mysql,
	/	но нам нужно делать вывод на экран, поэтому
	/	готовим все элементы в массиве:
	/*/

	$arr = array_map('stripslashes',$arr);

	$insertedComment = new Comment($arr);

	/* Вывод разметки только-что вставленного комментария: */

	echo json_encode(array('status'=>1,'html'=>$insertedComment->markup()));

}
else
{
	/* Вывод сообщений об ошибке */
	echo '{"status":0,"errors":'.json_encode($arr).'}';
}

submit.php получает комментарий из данных через запрос AJAX. Проверяет его и выводит объект JSON, в котором содержится либо разметка XHTML с вставленным комментарием, либо список ошибок. jQuery использует свойство status для определения того, что нужно выводить — либо сообщение об ошибках, либо добавлять разметку комментария к странице.

Успешный ответ:

{
    "status": 1,
    "html": "Html Code Of The Comment Comes Here..."
}

Свойство html содержит код комментария.

Ответ об ошибке:

{
    "status": 0,
    "errors": {
        "email": "Please enter a valid Email.",
        "body": "Please enter a comment body.",
        "name": "Please enter a name."
    }
}

При наличии ошибки jQuery проходит циклом по объекту ошибок и выводит сообщения рядом с полями, в которых есть ошибки.

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

styles.css

.comment,
#addCommentContainer{

	/* Стиль для комментариев  */

	padding:12px;
	width:400px;
	position:relative;
	background-color:#fcfcfc;
	border:1px solid white;
	color:#888;
	margin-bottom:25px;

	/* Скругленные углы и тени CSS3 */

	-moz-border-radius:10px;
	-webkit-border-radius:10px;
	border-radius:10px;

	-moz-box-shadow:2px 2px 0 #c2c2c2;
	-webkit-box-shadow:2px 2px 0 #c2c2c2;
	box-shadow:2px 2px 0 #c2c2c2;
}

.comment .avatar{

	/*
	/	Аватар позиционируется абсолютно.
	/	Внешнее смещение для div комментария
	/*/

	height:50px;
	left:-70px;
	position:absolute;
	width:50px;
	background:url('img/default_avatar.gif') no-repeat #fcfcfc;

	/* Центрируем вертикально: */

	margin-top:-25px;
	top:50%;

	-moz-box-shadow:1px 1px 0 #c2c2c2;
	-webkit-box-shadow:1px 1px 0 #c2c2c2;
	box-shadow:1px 1px 0 #c2c2c2;
}

div .comment  и #addCommentContainer имеют одинаковый стиль.  Используется несколько правил CSS3 для скругления углов и отражения теней.

styles.css

.comment .avatar img{
	display:block;
}

.comment .name{
	font-size:20px;
	padding-bottom:10px;
	color:#ccc;
}

.comment .date{
	font-size:10px;
	padding:6px 0;
	position:absolute;
	right:15px;
	top:10px;
	color:#bbb;
}

.comment p,
#addCommentContainer p{
	font-size:18px;
	line-height:1.5;
	overflow-x:hidden;
}

#addCommentContainer input[type=text],
#addCommentContainer textarea{

	/* Стиль для ввода */

	display:block;
	border:1px solid #ccc;
	margin:5px 0 5px;
	padding:3px;
	font-size:12px;
	color:#555;
	font-family:Arial, Helvetica, sans-serif;
}

#addCommentContainer textarea{
	width:300px;
}

label{
	font-size:10px;
}

label span.error{
	color:red;
	position:relative;
	right:-10px;
}

#submit{

	/* Кнопка "Отправить" */

	background-color:#58B9EB;
	border:1px solid #40A2D4;
	color:#FFFFFF;
	cursor:pointer;
	font-family:'Myriad Pro',Arial,Helvetica,sans-serif;
	font-size:14px;
	font-weight:bold;
	padding:4px;
	margin-top:5px;

	-moz-border-radius:4px;
	-webkit-border-radius:4px;
	border-radius:4px;
}

#submit:hover{
	background-color:#80cdf5;
	border-color:#52b1e2;
}

Во второй части мы задаем стили для комментариев и элементов формы. Отметим селектор input[type=text], который выделяет элементы в зависимости от атрибута type. Теперь рассмотрим jQuery.

script.js

$(document).ready(function(){
	/* Следующий код выполняется только после загрузки DOM */

	/* Данный флаг предотвращает отправку нескольких комментариев: */
	var working = false;

	/* Ловим событие отправки формы: */
	$('#addCommentForm').submit(function(e){

 		e.preventDefault();
		if(working) return false;

		working = true;
		$('#submit').val('Working..');
		$('span.error').remove();

		/* Отправляем поля формы в submit.php: */
		$.post('submit.php',$(this).serialize(),function(msg){

			working = false;
			$('#submit').val('Submit');

			if(msg.status){

				/*
				/	Если вставка была успешной, добавляем комментарий
				/	ниже последнего на странице с эффектом slideDown
				/*/

				$(msg.html).hide().insertBefore('#addCommentContainer').slideDown();
				$('#body').val('');
			}
			else {

				/*
				/	Если есть ошибки, проходим циклом по объекту
				/	msg.errors и выводим их на страницу
				/*/

				$.each(msg.errors,function(k,v){
					$('label[for='+k+']').append('<span>'+v+'</span>');
				});
			}
		},'json');

	});

});

Мы используем вызов функции $(document).ready(), которая привязывает функцию к событию загрузка контента DOM. Переменная working действует как флаг, который сигнализирует о том, что запрос AJAX находится в работе (таким образом предотвращается дублирование одного и того же комментария).

Заключение

Чтобы запустить скрипт на вашем сервере нужно создать таблицу comments в вашей базе данных MySQL. Вы можете сделать это с помощью кода SQL из файла table.sql, который надо ввести на закладке SQL в phpMyAdmin. Затем нужно установить параметры соединения с базой данных MySQL в файле connect.php.


Источник урока: rudebox.org.ua

Другие темы:

5- лучших Социальных сетей на php mysql
Киберсант-Деньги
Киберсант-Профессионал
PHP и MySQL с Нуля до Гуру 2.0 видеокурс