Ajax и XML: Применение Ajax для создания рейтингов и комментариев

Добавьте в ваше приложение удобные функции оценки и комментирования с помощью Ajax

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

Джек Д Херрингтон, главный инженер-программист, Leverage Software Inc.

Джек Д. Херрингтон (Jack D. Herrington) - главный инженер-программист с более чем двадцатилетним опытом работы. Он автор трех книг: "Генерирование кода в действии", "Podcasting Hacks" и "PHP Hacks". Написал более 30 статей. Вы можете связаться с Джеком по адресу jherr@pobox.com.



17.10.2007

Все мы любим оценивать. Я думаю, это заложено в наших генах. Мы с дочерью любим ходить в кино; раньше ей нравилось все подряд, но теперь, когда ей исполнилось четыре года, она стала значительно разборчивей. Я научил ее использовать систему рейтингов "хорошо" и "плохо" (большой палец вверх и вниз) à как в программе Ebert and Roeper. (Она далаШреку Третьему оценку «палец вверх».) Я думаю, именно поэтому в Интернете так популярны оценки и рецензирование продуктов, статей и т.п.

В этой статье показывается, как с помощью сочетания MySQL, PHP, Prototype.js и асинхронного JavaScript™ и XML (Ajax) добавить на любой сайт простые функции оценки и комментирования.

Система рейтингования

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

В листинге 1 показана схема для этого примера, начиная с таблицы для фильмов. Она очень проста: идентификатор фильма с автоинкрементом и название фильма. Все голоса за фильмы хранятся в таблице рейтингов. Эта таблица связывается с таблицей фильмов посредством movie_id и содержит всего одно дополнительное поле — оценку, выраженную числом от 1 до 5.

Листинг 1. rating.sql
DROP TABLE IF EXISTS movies;

CREATE TABLE movies (
  movie_id INTEGER NOT NULL AUTO_INCREMENT,
  name VARCHAR( 128 ) NOT NULL,
  PRIMARY KEY ( movie_id )
);

DROP TABLE IF EXISTS ratings;

CREATE TABLE ratings (
  movie_id INTEGER NOT NULL,
  rating INTEGER NOT NULL
);

Если вы хотите реализовать систему «один человек - один голос», вам нужно будет добавить к таблице ratings идентификатор пользователя, а также сделать так, чтобы один пользователь мог отдать за один фильм только один голос. Я хотел сделать этот пример более простым, поэтому опустил эту часть.

Чтобы реализовать эту схему в базе данных, нужно сначала создать базу данных, а потом – добавить в нее схему. Это можно сделать, выполнив в командной строке следующие команды:

% mysqladmin create comments
% mysql comments < ratings.sql

В зависимости от типа установки MySQL может потребоваться указание имени пользователя и пароля.

Далее, нам понадобится страница, в которой будут выводиться доступные фильмы и указываться ссылка на страницу оценки каждого из них. Эта индексная страница показана в листинге 2.

Листинг 2. index.php
<html>
<body>
<?php
require_once("DB.php");

$db =& DB::Connect( 'mysql://root@localhost/comments', array() );
if (PEAR::isError($db)) { die($db->getMessage()); }

$res = $db->query( 'SELECT * FROM movies' );
while( $res->fetchInto( $row ) )
{
?>
<a href="rate.php?id=<?php echo($row[0]) ?>"><?php echo($row[1]) ?></a><br/>
<?php
}
?>
</body>
</html>

Чтобы облегчить себе работу, я добавил в таблицу movies несколько известных фильмов с помощью SQL-запроса, который вы можете загрузить из раздела Материалы для загрузки . Список фильмов на индексной странице показан на рисунке 1.

Рисунок 1. Список фильмов
Список фильмов

Если в результате выполнения приведенного кода вы не получили этого списка, скорее всего, у вас не установлен модуль баз данных DB из хранилища PEAR. Чтобы установить его, просто запустите в командной строке следующую команду:

% pear install DB

Теперь, когда мы показали список фильмов, настало время создать страницу, которая позволит вам оценивать фильмы и выводить суммарные данные по уже отданным голосам и среднее значение рейтинга. В листинге 3 показано, как все это можно сделать на странице rate.php.

Листинг 3. rate.php
<?php
require_once("DB.php");

$db =& DB::Connect( 'mysql://root@localhost/comments', array() );
if (PEAR::isError($db)) { die($db->getMessage()); }

$id = $_GET['id'];
$title = '';

$res = $db->query( 'SELECT name FROM movies WHERE movie_id=?', array( $id ) );
while( $res->fetchInto( $row ) ) { $title = $row[0]; }
?>
<html>
<head>
<title><?php echo($title); ?></title>
<script src="prototype.js"></script>
<script>
function rate( value ) {
        new Ajax.Updater( 'rating', 'ratemovie.php?id=<?php echo($id)?>&v='+value );
}
</script>
</head>
<body>
<h1><?php echo($title); ?></h1>

<div id="rating">
<img src="star_off.gif" onclick="rate(1)"></img>
<img src="star_off.gif" onclick="rate(2)"></img>
<img src="star_off.gif" onclick="rate(3)"></img>
<img src="star_off.gif" onclick="rate(4)"></img>
<img src="star_off.gif" onclick="rate(5)"></img>
<br/><br/>
<?php

$res2 = $db->query(
        'SELECT count( rating ), sum(rating ) FROM ratings WHERE movie_id=?',
        $id
);
while( $res2->fetchInto( $row ) )
{
?>
Votes: <?php echo($row[0]); ?><br/>
Average Rating: <?php echo($row[1]/$row[0]); ?>
<?php
}
?>
</div>

</body>
</html>

В самом начале файла мы получаем название фильма по полученному в качестве параметра идентификатору. В средней части сценария мы включаем файл библиотеки prototype.js и создаем функцию JavaScipt rate(), которая вызывает страницу ratemovie.php посредством Ajax, используя библиотеку Prototype. После этого добавляем несколько изображений со звездами, при нажатии каждой из которых вызывается функция rate().

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

Сценарий ratemovie.php, приведенный в листинге 4, выполняет добавление оценки в базу данных, после чего возвращает фрагмент HTML-кода, замещающего звездочки и счетчик голосов исходной страницы.

Листинг 4. ratemovie.php
<?php
require_once("DB.php");

$db =& DB::Connect( 'mysql://root@localhost/comments', array() );
if (PEAR::isError($db)) { die($db->getMessage()); }

$v = $_GET['v'];
$id = $_GET['id'];

$sth = $db->prepare( 'INSERT INTO ratings VALUES ( ?,? )' );
$db->execute( $sth, array( $id, $v ) );
?>
<img src="star_<?php echo( ($v>0)?'on':'off' ) ?>.gif"></img>
<img src="star_<?php echo( ($v>1)?'on':'off' ) ?>.gif"></img>
<img src="star_<?php echo( ($v>2)?'on':'off' ) ?>.gif"></img>
<img src="star_<?php echo( ($v>3)?'on':'off' ) ?>.gif"></img>
<img src="star_<?php echo( ($v>4)?'on':'off' ) ?>.gif"></img>
<br/><br/>
<?php
$res2 = $db->query(
  'SELECT count( rating ), sum(rating ) FROM ratings WHERE movie_id=?',
  $id
);
while( $res2->fetchInto( $row ) )
{
?>
Votes: <?php echo($row[0]); ?><br/>
Average Rating: <?php echo($row[1]/$row[0]); ?>
<?php
}
?>

Страница рейтинга в действии показана на рисунке 2.

Рисунок 2. Страница оценки
Страница оценки

Когда вы нажимаете на одну из звезд, голос добавляется в базу данных, HTML-код для звезд, количества голосов и средней оценки изменяется, отражая добавление голоса.

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

Листинг 5. index2.php
<html>
<body>
<table>
<?php
require_once("DB.php");

$db =& DB::Connect( 'mysql://root@localhost/comments', array() );
if (PEAR::isError($db)) { die($db->getMessage()); }

$res = $db->query( 'SELECT * FROM movies' );
while( $res->fetchInto( $row ) )
{

$res2 = $db->query(
  'SELECT count( rating ), sum(rating ) FROM ratings WHERE movie_id=?', $row[0]
);
$rating = 0.0;
while( $res2->fetchInto( $row2 ) ) { $rating = $row2[1] / $row2[0]; }
?>
<tr><td align="center">
<?php echo( $rating > 0 ? $rating : 0 ) ?>
<td><td>
<a href="rate2.php?id=<?php echo($row[0]) ?>"><?php echo($row[1]) ?></a>
</td></tr>
<?php
}
?>
</table>
</body>
</html>

Новая версия индексной страницы с рейтингами для каждого фильма показана на рисунке 3.

Рисунок 3. Обновленная страница фильмов
Обновленная страница фильмов

И это все, что нужно для реализации голосования с помощью Ajax, PHP, MySQL и крайне удобной библиотеки JavaScript Prototype.js.

В следующей части примера мы реализуем комментарии или рецензии для фильмов.


Комментирование

В Интернете существует множество систем комментирования, от простейших — как в большинстве блогов, где имеется просто строка комментариев к записи блога — до интеллектуальных систем разветвленных комментариев, пример которых можно увидеть на Slashdot.

В этом примере мы будем реализовать систему попроще. В зависимости от ваших потребностей вы сможете впоследствии усложнить ее.

Начнем с добавления в существующую схему базы данных еще одной таблицы, как показано в листинге 6.

Листинг 6. comments.sql
DROP TABLE IF EXISTS comments;
CREATE TABLE comments (
  movie_id INTEGER NOT NULL,
  email VARCHAR(255) NOT NULL,
  name VARCHAR(255) NOT NULL,
  comment TEXT NOT NULL
);

Эта таблица comments, которая связывается с фильмом по полю movie_id. В ней также содержатся адрес электронной почты, имя комментатора и текст комментария. Это элементарная система комментирования, похожая на ту, что входит в состав программного обеспечения блогов, таких как WordPress и MoveableType.

Если вы хотите создать древовидную систему комментирования, вам нужно будет добавить к таблице идентификатор с автоинкрементом, после чего добавить поле parent_id , указывающее на родительский комментарий, которое также может быть пустым. Если это поле не содержит данных, комментарий считается комментарием верхнего уровня.

Чтобы добавить возможность комментирования на страницу оценки, нам нужно включить в нижнюю часть страницы еще один небольшой сценарий. В листинге 7 показан новый код PHP.

Листинг 7. rate2.php
...
<h2>Comments</h2>
<div id="comments">
<?php
$res3 = $db->query(
  'SELECT * FROM comments WHERE movie_id=?',
  $id
);
while( $res3->fetchInto( $row3 ) )
{
?>
<div>
<a href="mailto:<?php echo($row3[1]) ?>"><?php echo($row3[2]) ?></a> says:
'<?php echo($row3[3]) ?>'
</div>
<?php
}
?>
</div>

<div style="margin-top:20px;">Add your own comment:</div>

<form id="cform">
<input type="hidden" name="id" value="<?php echo($id)?>">
<table>
<tr><td>Name:</td><td><input type="text" name="name"></td></tr>
<tr><td>Email:</td><td><input type="text" name="email"></td></tr>
<tr><td>Comment:</td><td><textarea name="comment" id="comment_text"></textarea></td></tr>
</table>
</form>
<button onclick="addcomment()">Add Comment</button>

<script>
function addcomment()
{
  new Ajax.Updater( 'comments', 'addcomment.php',
  {
    method: 'post',
    parameters: $('cform').serialize(),
    onSuccess: function() {
       $('comment_text').value = '';
    }
  } );
}
</script>
</body>
</html>

Сценарий начинает с заполнения тега <div> "comments" текущими комментариями к этому фильму из базы данных. После этого указывается стандартный HTML-тег <form>, содержащий поля для имени комментатора, адреса его электронной почты и собственно комментария. В форме также содержится скрытое значение (идентификатор просматриваемого на данный момент фильма), чтобы сценарий, добавляющий комментарий, знал, какому фильму этот комментарий сопоставить.

Кнопка Add Comment, расположенная под формой, вызывает функцию JavaScript addcomment(). Эта функция через объект Ajax.Updater библиотеки Prototype.js вызывает сценарий addcomment.php. Она упаковывает имя, адрес электронной почты и комментарий с помощью очень удобной функции serialize(), также входящей в состав Prototype.js.

Код устроен таким образом, что если отправка комментария была удачной, он очищает поле комментария, чтобы пользователь мог добавить столько комментариев, сколько захочет, не повторяя ввода имени и адреса электронной почты.

В листинге 8 показан сценарий addcomment.php.

Листинг 8. addcomment.php
<?php
require_once("DB.php");

$id = $_POST['id'];

$db =& DB::Connect( 'mysql://root@localhost/comments', array() );
if (PEAR::isError($db)) { die($db->getMessage()); }

$sth = $db->prepare( 'INSERT INTO comments VALUES ( ?, ?, ?, ? )' );
$db->execute( $sth, array( $id,
  $_POST['email'], $_POST['name'], $_POST['comment' ] ) );

$res = $db->query('SELECT * FROM comments WHERE movie_id=?', $id );
while( $res->fetchInto( $row ) )
{
?>
<div>
<a href="mailto:<?php echo($row[1]) ?>"><?php echo($row[2]) ?></a> says:
'<?php echo($row[3]) ?>'
</div>
<?php
}
?>

Сначала сценарий добавляет комментарий, указанный в запросе POST, в базу данных. После этого он выводит все комментарии в HTML-коде, точно так же, как это было сделано на исходной странице, с тем лишь исключением, что на этот раз выводится комментарий, добавленный пользователем.

Новая страница оценки показана на рисунке 4.

Рисунок 4. Обновленная страница рейтинга, теперь с комментариями
Обновленная страница рейтинга

Такая система комментирования дает пользователю немедленную обратную связь. Когда пользователь нажимает кнопку Add Comment, новая запись сразу же добавляется к комментариям. Система также покажет все комментарии, добавленные в это время другими пользователями.

При желании вы можете расширить этот пример и регулярно опрашивать сервер, обновляя раздел комментариев свежими данными. Конечно же, для этого не нужно вызывать сценарий addcomment.php. Вам нужен другой сценарий, который будет просто возвращать комментарии, не добавляя новые. Чтобы упростить эту процедуру, в Prototype.js реализован класс Ajax.PeriodicalUpdater, который — по заданному идентификатору, частоте обновления и URL — будет обновлять любую часть Web-страницы с указанной частотой.


Добавление каналов RSS

Другой простой способ расширения этого примера - экспорт списка фильмов с оценками в виде канала RSS. В листинге 9 представлен код, который делает это.

Листинг 9. rss.php
<?php
header( "content-type:text/xml" );
?>
<rss version="0.91">
<channel>
<title>Movie rankings</title>
<link>http://localhost/comments/rss.php</link>
<?php
require_once("DB.php");

$db =& DB::Connect( 'mysql://root@localhost/comments', array() );
if (PEAR::isError($db)) { die($db->getMessage()); }

$res = $db->query( 'SELECT * FROM movies' );
while( $res->fetchInto( $row ) )
{

$res2 = $db->query(
  'SELECT count( rating ), sum(rating ) FROM ratings WHERE movie_id=?', $row[0]
);
$rating = 0.0;
while( $res2->fetchInto( $row2 ) ) { if ( $row2[0] > 0 ) $rating = $row2[1] / $row2[0]; }
?>
<item>
<title><?php echo($row[1]) ?> - 
<?php echo( $rating > 0 ? $rating : 0 ) ?> stars</title>
<link>http://localhost/commentsts/rate2.php?id=<?php echo($row[0]) ?></link>
<description><?php echo($row[1]) ?></description>
</item>
<?php
}
?>
</channel>
</rss>

Код, приведенный в листинге 9, является альтернативой вывода данных в формате HTML. Вместо тегов <table>, <tr> и <td> я использовал теги <title>, <description> и <link>, указывающие на страницу каждого фильма. Когда я перехожу на эту страницу в браузере Firefox, я вижу что-то похожее на рисунок 5.

Рисунок 5. Канал RSS в браузере
Канал RSS в браузере

Все достаточно просто. Получить каналы XML из PHP несложно.

Когда я запускаю этот код локально, используя следующую команду:

% php rss.php

я могу видеть непосредственно XML-код канала RSS. Фрагмент этого RSS-канал показан в листинге 10.

Листинг 10. Фрагмент RSS
<rss version="0.91">
<channel>
<title>Movie rankings</title>
<link>http://localhost/comments/rss.php</link>
<item>
<title>Star Wars - 4.5 stars</title>
<link>http://localhost/commentsts/rate2.php?id=1</link>
<description>Star Wars</description>
</item>
<item>
...

Заключение

Все много говорят о контенте, формируемом пользователями, и о том, что он является движущей силой Web 2.0. Как мы увидели из примеров, приведенных в этой статье, можно с легкостью создавать приложения Ajax с помощью таких замечательных инструментов, как библиотека Prototype.js. Функции, которые дают посетителям возможность оценивать и комментировать материалы вашего сайта, предоставляют вам отличный контент, формируемый пользователями.


Загрузка

ОписаниеИмяРазмер
Исходный код для этой статьиx-ajaxxml5-code.zip22 KB

Ресурсы

Научиться

  • Оригинал статьи Ajax for ratings and comments (EN).
  • Домашняя страница PHP: Посетите бесценный ресурс для программистов PHP, где можно найти информацию об этом широко распространенном языке сценариев.(EN)
  • Библиотека Prototype: Откройте для себя эту среду разработки JavaScript, предназначенную для облегчения разработки динамических Web-приложений.(EN)
  • Библиотека JavaScript Scriptaculous: В этой библиотеке, построенной на базе Prototype, содержатся средства и эффекты вывода, которые позволят сделать ваш сайт более эффектным.(EN)
  • Страница документирования Prototype.js: Найдите дополнительную информацию о библиотеке JavaScript Prototype, со ссылками на официальный блог Prototype и множество других ресурсов.(EN)
  • jQuery: Еще одна библиотека JavaScript, которая предоставляет функциональные возможности, схожие с Prototype.js.(EN)
  • Библиотека интерфейсов Yahoo: Попробуйте инструментарий Yahoo! для работы с Ajax.(EN)
  • Раздел XML на developerWorks (EN) : Узнайте все об XML в разделе XML сайта developerWorks.
  • Сертификация по XML корпорации IBM: Узнайте, как вы можете стать сертифицированным разработчиком IBM в области XML и связанных с ним технологий. (EN)
  • Техническая библиотека по XML : Познакомьтесь с библиотекой по XML сайта developerWorks, где вы можете найти множество технических статей и советов, рекомендаций, стандартов и руководств IBM.
  • Технические мероприятия и Web-трансляции developerWorks (EN): В этих разделах можно получить самую актуальную информацию о современных технологиях.
  • Ajaxian: Изучите этот великолепный ресурс, чтобы идти в ногу с развитием Ajax и использующих его виджетов.(EN)

Получить продукты и технологии

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=XML, Open source
ArticleID=262645
ArticleTitle=Ajax и XML: Применение Ajax для создания рейтингов и комментариев
publish-date=10172007