Гостевая книга на PHP


Думаю, любой новичок, только начавший изучать php, хотел написать свою гостевую книгу. У многих, как например, когда-то и у меня, это был первый написанный в жизни скрипт. В этом уроке я попытаюсь рассказать, как написать на php, с использованием MySQL, свою собственную гостевую книгу. Итак, как это будет выглядеть…

Постановка задачи

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

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

В общем, должно получиться что-то вроде чата, вот только чтобы увидеть новые комментарии оставленные другими пользователями, всё же придётся перезагрузить страницу. Кстати, в скором времени мы собираемся переписать весь этот функционал с учётом того, чтобы новые сообщения появлялись без перезагрузки страниц. Но об этом в будущих статьях.

Подготовка

Для начала создадим таблицу в БД MySQL, в которой будем хранить все сообщения, написанные пользователями. Имя таблицы — guestbook.

Вот её структура:

  • Поле id обязательно должно иметь флаг AUTO_INCREMENT. Это необходимо для того, чтобы каждому последующему сообщению, добавленному через форму добавления записи в гостевую книгу, присваивался свой номер, причём по порядку.
  • uid — уникальный идентификатор пользователя. О нём и всём прочем, связанном с регистрацией и авторизацией пользователя описано в предыдущих статьях.
  • date — время добавления сообщения в UNIX-формате, т. е. в секундах, прошедших с начала эры UNIX (1 января 1970г.).
  • user — ник пользователя. Могло бы хватить и поля uid. В том случае, при выводе сообщений на страницу, чтобы узнать ник пользователя по его id, нам пришлось бы выполнять запрос к таблице users, в которой по идентификатору мы получали бы ник. А так как у нас будет 10 сообщений на страницу, то у нас получилось бы 10 лишних запросов к БД, коих можно избежать, если в таблицу guestbook писать ещё и ники, как мы это и сделаем. Правда это исключает возможность смены ника в будущем. Впрочем, тут решать вам.
  • mess — собственно, сам текст сообщения.

Реализация

Если таблица готова, система регистрации и авторизации написана, можно приступать к написанию самого скрипта.

Гостевая книга будет размещаться на главной странице сайта, поэтому в корне создаём файл index.php. Он и будет нашим главным контроллером. В папке lib создаём файл function_index.php, который будет содержать некоторые функции. Работать будем с этими двумя файлами, а также с файлом function_global, в который добавим ещё несколько полезных функций.

Шаблоны сайта будут лежать в папке template.

Вот иерархия файлов на сервере, которая должна у вас получиться:

Для начала рассмотрим код главной страницы сайта (index.php).
Вот его код:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<?
//подключаемся к базе данных, а также подключаем файлы модулей
include ('lib/connect.php');
include ('lib/function_global.php');
include ('lib/function_index.php');
 
$messOnPage = 10; //выставляем количество сообщений на страницу
 
//обработка разлогивания
if($_GET['action'] == "out") out();
 
//проверка авторизации (подробно описана в предыдущей статье)
if(login())
{
  $UID = $_SESSION['id'];
  $admin = isAdmin($UID);
}
else
{
  if(isset($_POST['log_in'])) 
  {
    $error = enter();
    if (count($error) == 0)
    {
      $UID = $_SESSION['id'];
      $admin = is_admin($UID);
    }
    else $admin = false;
  }
}
 
//если пользователь авторизирован
if ($UID)
{
  $userName = htmlspecialchars(nickname($UID)); //получаем ник пользователя по его id
  //обработчик отправки сообщения
  if(isset($_POST['go'])) 
  {
    if(trim($_POST['mess']) != "")
    {
      addMessage($UID, $userName, $_POST['mess']); //добавляем запись в БД, если сообщение не пусто
      header('Location: http://'.$_SERVER['HTTP_HOST'].'/');
    }
    else $noText = true;
  }
 
  $countPost = countPost(); //узнаём количество записей в БД
  $lastPage = ceil($countPost / $messOnPage); //считаем количество страниц
  //проверяем, был ли передан GET параметр page
  //также проверяем, является ли этот параметр числом
  //больше ли он единицы и существует ли такая страница
  //в противном случае считаем page равным 1
  if(!isset($_GET['page']) || !is_numeric($_GET['page']) || $_GET['page'] < 1 || $_GET['page'] > $lastPage) $page = 1;
  else $page = $_GET['page'];
 
  //если записи есть в БД, в массив data вернём сообщения со страницы page
  //а в массив arrayPage - массив переключателей страниц
  if($countPost != 0)
  {
    $arrayPage = printPage($lastPage, $page);
    $data = printMess($page, $messOnPage);
  }
  include ('template/index.html');
}
//если пользователь не авторизирован
else
{
  include ('template/home.html');
}
?>

Сейчас попробуем более подробно разобраться в коде, после чего опишем все встречающиеся в коде пользовательские функции.

В коде есть обработчик if(isset($_POST[‘go’])),который проверяет, была ли нажата кнопка отправки сообщения. Если она была нажата, в функцию addMessage передаются в качестве параметров uid пользователя и текст его сообщения. В случае если текст сообщения пуст или состоит из пробельных символов, функция не запустится и создастся переменная noText, которая чуть позже понадобится для вывода соответствующего сообщения.

Далее, для вывода данных на страницу нам понадобится ник пользователя. Для этого воспользуемся функцией nickname(), которая принимает id пользователя, а возвращает ник, соответствующий уникальному идентификатору пользователя.

Функция countPost возвращает количество записей в БД, а в переменную lastPage запишем количество страниц. Я очень часто в интернете встречал подобную строчку кода, которая на основании количества записей в БД и количества записей, которое необходимо выводить на страницу, должна возвращать количество страниц. Нередко допускают ошибку с округлением, округляя к ближайшему целому. На самом деле надо округлять в большую сторону, ведь если в БД, скажем, 13 записей, и мы собираемся выводить по 10 записей на страницу, должно получиться 2 страницы, поэтому используется функция ceil(), округляющая к большему числу:

1
$lastPage = ceil($countPost / $messOnPage);

Также скрипту может прилететь GET параметр page, указывающий, записи с какой страницы необходимо вывести на страницу. Если параметр некорректный, скажем, это не цифра или же такой страницы попросту не существует, то будем считать, что page = 1.

Если записи есть в БД, то заполним массивы data и arrayPage, вызвав соответственно функции printMess() и printPage(). Первая функция возвращает список записей, а вторая — массив переключателей.

Ну и, наконец, подключаем файл шаблона, в котором будет оформлен вывод всех данных:

1
include ('template/index.html');

Несколько слов о шаблоне (index.html).

Форма добавления сообщения будет иметь следующий вид:

1
2
3
4
<form action="/index.php" method="post">
	<textarea wrap="virtual" rows="4" name="mess"></textarea>
	<input type="submit" value="Заебенить!" name="go" />
</form>

Добавленные сообщения будут выводиться циклом foreach:

1
2
3
4
5
6
<? foreach ($data as $v) { ?>
	<!-- Далее оформняем вывод по своему усмотрению -->
	<?=$v['user'];?> <!-- Имя пользователя -->
	<?=$v['date'];?> <!-- дата и время сообщения -->
	<?=$v['mess'];?> <!-- Текст сообщения -->
<? } ?>

Теперь разберём все функции, использованные в статье. Начнём с функции добавления записи в БД.

1
2
3
4
5
6
7
function addMessage($uid, $name, $mess) //функция для добавления записи в БД
{
	$user = mysql_real_escape_string($name)
	$mess = mysql_real_escape_string($mess);
	$date = time();
	mysql_query("INSERT INTO guestbook (uid,date,user,mess) VALUES ('".$uid."','".$date."','".$user."','".$mess."')");
}

Функция просто добавляет в таблицу guestbook новую запись с uid пользователя, его ником, текстом сообщения и временем в формате UNIX. После вызова этой функции в файле index.php, используется перенаправление:

1
header('Location: http://'.$_SERVER['HTTP_HOST'].'/');

Дело в том, что после отправки сообщения при использовании метода POST, если обновить страницу, данные отправятся ещё раз. Перенаправление решает эту проблему. Также важно, чтобы до этой строчки не было никакого вывода на страницу, т. е. ни одного echo, print и т. д.

Функция nickname() возвращает ник пользователя по его id:

1
2
3
4
5
6
7
function nickname($id) {	 	
	$rez = mysql_query("SELECT login FROM users WHERE (id='$id')"); 	
	if(mysql_num_rows($rez) == 1)  	{ 		
		@$row = mysql_fetch_assoc($rez); 		
		return $row['login']; 	
	} 	else return false; 
}

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

1
2
3
4
5
6
function countPost() { 	
	$rez = mysql_query("SELECT COUNT(*) FROM guestbook"); 	
	if ($rez) $countPost = mysql_result($rez, 0); 	
	else $countPost = 0; 	
	return $countPost; 
}

Для выборки необходимых комментариев из БД используется следующая функция:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function printMess($page, $countOnPage)
{
	$p = ($page * $countOnPage) - $countOnPage; //считаем, с какой записи надо доставать данные из БД
	//выбираем все поля из таблицы guestbook, причём, только countOnPage записей, начиная с p-ой, упорядочив по id по убыванию
	$result = mysql_query("SELECT * FROM guestbook ORDER BY id DESC LIMIT ".$p.",."$countOnPage);
//Запишем полученные данные в массив data
	for ($data = array(); @$row = mysql_fetch_assoc($result); $data[] = $row)
	{
		$row['mess'] = nl2br(htmlspecialchars($row['mess']));
		$row['user'] = htmlspecialchars($row['user']);
		$row['date'] = date("j.m.Yв G:i", $row['date']);
	}
	return $data;
}

В цикле данные приводим к тому виду, в котором они должны выводиться на странице. Например, применяем к нику и тексту сообщения htmlspecialchars(), чтобы преобразовать все тэги в html-сущности. Дату также приводим к читаемому формату. Формат при желании можно легко поменять. Подробнее — тут.

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

1
$arrayPage = printPage($lastPage, $page, $messOnPage);

Сама функция пагинатора подробно рассмотрена в следующей статье.

  • «Умный блог» у тебя, полезного много — этот пост помог. благодарю

  • Никита

    Спасибо! Благодаря Вам перестал путаться)

  • Димон

    Было бы неплохо сопровождать все это исходниками

  • Владимир

    $result = mysql_query("SELECT * FROM guestbook ORDER BY id DESC LIMIT ".$p.",."$countOnPage);
    Забыл точку конкатенации
    $result = mysql_query("SELECT * FROM guestbook ORDER BY id DESC LIMIT ".$p.",.".$countOnPage);

  • Митя

    Очень долго возился с этой темой. Честно говоря, на то время даже основ php не знал)). Хорошо сайт один помог http://damdamysh.blogspot.ru/ У вас очень хорошие уроки!!

  • Владимир Масленко

    Ошибка в строке:
    $result = mysql_query(«SELECT * FROM guestbook ORDER BY id DESC LIMIT «.$p.»,.»$countOnPage);

    Надо:
    $result = mysql_query(«SELECT * FROM guestbook ORDER BY id DESC LIMIT «.$p.»,».$countOnPage);