with the flow

WEBプログラマを目指すWEBデザイナーが書き綴る開発日誌のようなもの

php+mySQL+jQueryで、コメントフォームのajax化を試す

jQueryの簡易ajax通信関数、POST()を使った処理を色々試してみたくて最近やったことを放出します。

php+mySQL+jQueryで、コメント投稿をajaxで表示するのを作ってみた。
ネタ元はNETTUTS。ってか、ほぼそのまんまやっただけw
Learn how to AJAXify Comment Forms
http://net.tutsplus.com/articles/news/learn-how-to-ajaxify-comment-forms/

懇切丁寧な作り方、考え方のscreencast(英語)がPodCastでも配信されているのでそっちを見ながら勉強がてら作ってみた。

海外サイトのチュートリアルだから、とーぜんマルチバイト言語のことは考慮なし。自分なりにエスケープ処理等細かい部分の改善は一応行なって毛を生やした感じ。
作った結果のソースは以下。

config.php

<?php

$server = 'localhost';
$username = 'root';
$password = 'root';//macでMAMPを使用している場合。
$db = 'ajaxComments';//DB側に予め作っておく。
$charset = 'UTF-8';

define('IS_AJAX', isset($_SERVER['HTTP_X_REQUESTED_WITH']));//ajax通信を判定する定数「IS_AJAX」を定義

index.php

<?php
<?php
if(isset($_POST['submit'])) {//ここはJS無効時に動くスクリプト
	$post = array();
	foreach ($_POST as $key => $value) {
		if(isset($_POST[$key]) && strlen(trim($value)) >= 3) {//簡易チェック
			$value = htmlspecialchars($value, 3, $charset);//htmlタグなどをエスケープ
			$post[$key] = $value;
		}
		else $error = true;
		if($key === 'email') {
			if(!filter_var($value, FILTER_VALIDATE_EMAIL)) $error = true;//emailチェック用関数を使用
		}
	}
	if(!$error) {
		require 'leaveComment.php';//エラーがなかったらコメントを出力するファイルを呼ぶ
	}
}

?>
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>ajaxComments</title>
<link rel="stylesheet" type="text/css" href="style.css" media="screen" />
</head>
<body>
	<div id="container">
		<ul id="comments"><?php include 'getItemsFromDatabase.php'; ?></ul>
		<h2 id="leaveAComment">Leave a Comment</h2>
		<div id="addComment">
			<form action="index.php" method="post">
				<p><input type="text" id="name" name="name" value="vname" /></p>
				<p><input type="text" id="email" name="email" value="ve-mail" /></p>
				<p><textarea name="comment" id="comment" rows="8" cols="40">vcomment</textarea>
				<p><input type="submit" name="submit" id="submit" value="コメント投稿" /></p>
			</form>
		<!-- //#addComment --></div>
	<!-- //#container --></div>
	<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js" charset="utf-8"></script>
	<script type="text/javascript" charset="utf-8">
		var tgAddComment = $("#addComment"),
			tgComments = $("#comments"),
			tgName = $("#name"),
			tgEmail = $("#email"),
			tgComment = $("#comment"),
			tgSubmit = $("#submit");

		tgSubmit.click(function(e){
			var name = tgName.val(),
				email = tgEmail.val().match(/^[A-Za-z0-9]+[\w-]+@[\w\.-]+\.\w{2,}$/) ? $("#email").val() : null,
				comment = tgComment.val();
			if(name.length < 2 || comment.length < 3 || email === null) {
				if($('.error').length === 0) {
					tgAddComment.find("form").append('<p class="error">入力内容が正しくありません</p>');
				}
				return false
			}
			$.post(
				'leaveComment.php',{
				'name' : name,
				'email' : email,
				'comment' : comment
				},
				function(res) {
					$(".error").fadeOut(200);
					$('<div class="overlay"></div>')
						.appendTo('#addComment')
							.fadeIn(1000, function(){
								tgComments
									.append('<li>' + res + '</li>')
										.children(':last')
										.height(tgComments.find('li:last').height())
										.hide()
										.slideDown(800, function(){
											var bodyHeight = $('html').height();
											$('#leaveAComment').fadeOut(200, function(){
												$('html').height(bodyHeight);
												$(this).text('コメントが投稿されました。').fadeIn(200);
												tgAddComment.hide(200);
											});
										});
										
							});
				}
			);
			return false;
		});
	</script>
</body>
</html>

※ネタ元のnettutsでは、"name"のvalidateにvar name = $("#name").val().replace(/[^\w\d ]+/ig, '')ってのを使用してたけど、これは完全に欧米仕様で、日本語が見事に考慮されてない。。。


nettuts使用で突き進むと、すべて日本語の名前を入力した場合、ぜんぶ""(空白)にreplaceされ、何も入力していないとみなされて、「入力内容が正しくありません」とか事務的に言われるので、nameに関してはJavaScriptでのvalidationは行わない。そのかわりpostしたleaveComment.php側で、phpにて行うことにした。


leaveComment.php

<?php

require 'config.php';

if(IS_AJAX) {
	$post = array();
	foreach ($_POST as $key => $value) {
		$value = htmlspecialchars($value, 3, $charset);//JavaScriptから渡された値は、最低限エスケープくらいはやっておく。
		$post[$key] = $value;
	}
}
$mysqli = new mysqli($server, $username, $password, $db) or die('There was a problem connecting.');

if($stmt = $mysqli->prepare("INSERT INTO comments VALUES(NULL, ?,?,?)")) {
	$stmt->bind_param('sss', $post['name'], $post['email'], $post['comment']);
	
	if(!$stmt->execute()) die($mysqli->error);
	$stmt->close();
	if(IS_AJAX) {
		echo "<h3>" . $post['name'] . "</h3>";
		echo "<p>" . $post['comment'] . "</p>";
	} else {
		header('Location: index.php');
	}
} else {
	echo 'There was an error!';
}

※NETTUTSでは、JavaScriptからのPOSTを受けて、渡された値をそのまま出力しているけど、最低限htmlのエスケープはやるべき。


getItemsFromDatabase.php

<?php
//error log出力用。公開時には取る。すでにphp.iniで指定してある場合は必要なし。
error_reporting(E_ALL);
ini_set('display_errors', 1);
ini_set('log_errors', 1);

require 'config.php';
$mysqli = new mysqli($server, $username, $password, $db) or die('データベース接続でエラーが発生しました。');
$stmt = $mysqli->prepare('SELECT id, name, email, comment FROM comments ORDER BY id') or die($mysqli->error);
$stmt->execute();
$stmt->bind_result($id, $name, $email, $comment);

while($row = $stmt->fetch()) : ?>

<li>
	<h3><?php echo $name; ?></h3>
	<p><?php echo $comment; ?></p>
</li>

<?php endwhile; ?>


style.css

@charset 'UTF-8';

body {
	font-family: helvetica, arial;
	line-height: 1.4em;
}

#container {
	width: 700px;
	margin: 10px auto 150px;
	color: #454545;
}

ul {
	margin: 0;
	padding: 0;
	list-style: none;
}

h3 {
	margin: 0;
	padding: 0;
	color: black;
}

h2 {
	color: #292929;
	font-size: 40px;
	margin-top: 40px;
}

p {
	font-size: 14px;
}

#addComment {
	margin-top: 40px;
	position: relative;
}

input, textarea {
	padding: .4em;
}

#comments li {
	border-top: 1px solid white;
	border-bottom: 1px solid #bcbbbb;
	padding: 20px 0 14px;
}

#comments li:last-child {
	border-bottom: none;
}
#comments li:first-child {
	border-top: none;
	padding-top: 0;
}

#comments, #addComment {
	background: #e3e3e3;
	border: 1px solid #bcbbbb;
	padding: 2em;
	-moz-border-radius: 2px;
	-webkit-border-radius: 2px;
}

input[type=text] {
	width: 70%;
}
textarea {
	width: 100%;
}

.error {font-style: italic; color: red;}

.overlay {
	width: 100%;
	height: 100%;
	background: black url(loader.gif) no-repeat 50% 50%;
	position: absolute;
	left: 0;
	top: 0;
	display: none;
	opacity: .9;
}

※DBには予め"ajaxComments"というデータベーススペース、
"comments"テーブル、そして"id","name","email","comment"フィールドを作成しておく必要があります。もちろん文字コードは"utf8_general_ci"。

※あくまで練習用なので、適切なエスケープ処理が行われていない箇所があります。使用する場合はあくまでも自己責任で。。。