ブログを作る - PHP入門
目次
- PHP入門
- PHPの概要
- PHP環境のインストール(XAMPP)・PHPの設定
- PHPの基本・簡単なプログラム
- 文法1
- 文法2
- 様々な関数を使う
- フォームの基本
- システム作成
- オブジェクト指向
- データベースとSQL
- PHPでMySQLを使う
- ブログを作る
- ECサイト(ショッピングサイト)を作る
このページの内容
はじめに
今まで学んだPHPとMySQLの知識を活かして、ブログを作成してみましょう。機能としては「記事・コメント表示」「記事投稿」「コメント投稿」があります。また、ウェブシステムを作成する上で必要となる、プログラムとデザインの分離方法も解説します。
データベースの設定
データベースの作成
まずブログで使うテーブルが入るデータベースを作成しましょう。名前は「blog」にします。phpMyAdminで以下のSQL文を発行してください。blogデータベース作成
CREATE DATABASE blog;
記事テーブルの作成
次に投稿記事が入る「記事テーブル」を作成します。データベースblogを選択してから(左カラムから「blog」をクリック)、以下のSQL文を発行します。postテーブル作成
CREATE TABLE post (
no SERIAL,
title TEXT,
content TEXT,
time TIMESTAMP
);
記事テーブル「post」はカラムが4つあります。「no」は「記事番号」です。コメントなどを付けるときに、どの記事かを一意に判別しないといけないので、そのための番号です。「SERIAL」型は、自動で「1,2,3,4…」と採番してくれます。自分で記事番号を管理してカウントアップしなくても良いので便利です。
「title」は「記事タイトル」で、「content」は「記事本文」です。
「time」は投稿日時です。DATETIME型でも良いのですが、TIMESTAMP型にしておくと、レコードが作成された日時が勝手に入るようになって便利なのです。
コメントテーブルの作成
次に記事に対するコメントが入る「コメントテーブル」を作成します。データベースblogを選択している状態で以下のSQL文を発行してください。commentテーブル作成
CREATE TABLE comment (
no SERIAL,
post_no INT,
name TEXT,
content TEXT,
time TIMESTAMP
);
コメントテーブル「comment」にはカラムが5つあります。「no」は「記事番号」です。記事テーブルと同じくSERIAL型にしています。
「post_no」は「コメントが書かれた親記事番号」です。どの記事に対するコメントかを表します。
「name」は「コメントを書いた人の名前」、「content」は「コメント本文」、「time」は「コメントの投稿日時」です。
確認用レコードの挿入
投稿フォームは後で作成しますが、確認用にいくつか、postテーブルとcommentテーブルにレコードを追加してみます。以下のSQL文を発行してください。
INSERT INTO post(no,title,content) VALUES(1,'記事1タイトル','記事1の内容です。');
INSERT INTO post(no,title,content) VALUES(2,'記事2タイトル','記事2の内容です。');
INSERT INTO comment(no,post_no,name,content) VALUES(1,1,'たろう','記事1へのコメントです。');
INSERT INTO comment(no,post_no,name,content) VALUES(2,1,'はなこ','記事1へのコメントです。');
「time」カラムには自動的にレコードを挿入した日時が入ります。各テーブルの表示タブから、2件ずつレコードが追加されていることを確認してください。デザインとプログラムの分離
今回は、ウェブデザイナーにHTMLのデザインを頼んだと仮定して、製作の流れを説明します。「ブログの記事一覧画面」を依頼して、デザイナーは以下のようなHTMLとCSSを作成したとします。この章で作成するファイルは全て「blog」フォルダを作ってそこに入れてください。blog/design.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Special Blog</title>
<link rel="stylesheet" href="blog.css">
</head>
<body>
<h1>Special Blog</h1>
<div class="post">
<h2>記事1のタイトルです</h2>
<p>
記事1の本文です。<br>
記事1の本文です。<br>
</p>
<div class="comment">
<h3>通りすがり1</h3>
<p>
記事1へのコメントです。<br>
記事1へのコメントです。<br>
</p>
</div>
<div class="comment">
<h3>通りすがり2</h3>
<p>
記事1へのコメントです。
</p>
</div>
<p class="commment_link">
投稿日:2011/6/7
<a href="#">コメント</a>
</p>
</div>
</body>
</html>
blog/blog.css
body {
background-color:#f77;
font-size: 14px;
font-family: Helvetica,Arial,sans-serif;
}
a:link, a:visited {color: #a00;}
a:hover {color: #fca;}
p {
margin: 0;
padding: 0;
}
h1 {
margin: 10px 0;
padding: 0;
color: white;
font-size: 32px;
text-align: center;
}
h2 {
margin: 0 -20px 10px -20px;
padding: 5px 10px;
background-color: #379;
color: white;
font-size: 18px;
}
h3 {
margin: 0 -15px 10px -15px;
padding: 5px 10px;
background-color: #a55;
color: white;
font-size: 14px;
}
.post {
width: 500px;
margin: 0 auto 15px auto;
padding: 0 20px 20px 20px;
background-color: #fb0;
}
.comment {
margin: 10px 0;
padding: 0 15px 15px 15px;
background-color: #da7;
}
.commment_link {
text-align: right;
}
CSS の解説は省略します。design.htmlを開くと以下のようになります。これは静的なHTMLなので、プログラマはこのデザインを壊さないようにプログラムを組み込む作業をしなければなりません。
さらに、デザインの修正が入る場合も十分考えられますので、なるべくHTMLにPHPのコードを埋め込まないように、「デザインとプログラムの分離」を考えて行きましょう。
記事一覧
デザインとプログラムの分離を考えて作成された、記事一覧表示のプログラムが以下の2つです。t_index.phpは、デザイナから渡されたdesign.htmlのデザインはそのままにプログラムを組み込んで改変したものです。見比べてみてください。blog/index.php
<?php
$pdo = new PDO("mysql:dbname=blog", "root");
$st = $pdo->query("SELECT * FROM post ORDER BY no DESC");
$posts = $st->fetchAll();
for ($i = 0; $i < count($posts); $i++) {
$st = $pdo->query("SELECT * FROM comment WHERE post_no={$posts[$i]['no']} ORDER BY no DESC");
$posts[$i]['comments'] = $st->fetchAll();
}
require 't_index.php';
?>
blog/t_index.php
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Special Blog</title>
<link rel="stylesheet" href="blog.css">
</head>
<body>
<h1>Special Blog</h1>
<?php foreach ($posts as $post) { ?>
<div class="post">
<h2><?php echo $post['title'] ?></h2>
<p><?php echo nl2br($post['content']) ?></p>
<?php foreach ($post['comments'] as $comment) { ?>
<div class="comment">
<h3><?php echo $comment['name'] ?></h3>
<p><?php echo nl2br($comment['content']) ?></p>
</div>
<?php } ?>
<p class="commment_link">
投稿日:<?php echo $post['time'] ?>
<a href="comment.php?no=<?php echo $post['no'] ?>">コメント</a>
</p>
</div>
<?php } ?>
</body>
</html>
index.phpの解説
$pdo = new PDO("mysql:dbname=blog", "root");
上記で作成したデータベース「blog」に接続しています。
$st = $pdo->query("SELECT * FROM post ORDER BY no DESC");
記事一覧を取得するSQL文を発行しています。「ORDER BY no DESC」で記事番号の大きい順、すなわち新しい記事から順番に並び替えられます。
$posts = $st->fetchAll();
fetchAllメソッドは、全てのレコードを配列として返します。$posts[0]に最も新しい記事、$posts[1]に次の記事…と順番に入ります。
for ($i = 0; $i < count($posts); $i++) {
$st = $pdo->query("SELECT * FROM comment WHERE post_no={$posts[$i]['no']} ORDER BY no DESC");
$posts[$i]['comments'] = $st->fetchAll();
}
count($posts)は全記事数を返します。記事をひとつずつループし、記事に対するコメント一覧を取得するSQL文を発行しています。「post_no」にはコメントを付けた元記事番号が入ります。
さらに記事配列$postsに新しい要素「comments」を作って、そこにコメント一覧を格納しています。
ここまでのプログラムで、$posts配列に、記事とコメント一覧表示に必要な情報が全て入ります。
require 't_index.php';
記事とコメント一覧を表示するプログラムは、require文で別ファイル(t_index.php)に分離しています。t_index.phpの解説
まず元のdesign.htmlから各記事をまとめている部分を探すと、
<div class="post">
・・・
</div>
だと分かります。そこで、その上下を
<?php foreach ($posts as $post) { ?>
・・・
<?php } ?>
で囲み、foreach文で記事数だけループさせるようにします。ループの内部では、各記事のデータが「$post」に入ります。
<h2><?php echo $post['title'] ?></h2>
<p><?php echo nl2br($post['content']) ?></p>
記事のタイトルと本文を出力しています。本文は複数行入る可能性があるので、nl2br関数で改行をbrタグに変換しています。次にコメントですが、元のdesign.htmlからコメント部分を探すと、
<div class="comment">
・・・
</div>
だと分かるので、その上下を
<?php foreach ($post['comments'] as $comment) { ?>
・・・
<?php } ?>
このループで囲んでいます。ループの内部では各コメントのデータが「$comment」に入ります。
<h3><?php echo $comment['name'] ?></h3>
<p><?php echo nl2br($comment['content']) ?></p>
コメントを書き込んだ人とコメント本文を出力しています。コメント本文も複数行入る可能性があるのでnl2br関数を通しています。
投稿日:<?php echo $post['time'] ?>
投稿日を出力しています。
<a href="comment.php?no=<?php echo $post['no'] ?>">コメント</a>
記事番号をパラメータにして、コメント書き込みフォームへのリンクを張っています。リンク先はのちほど作成します。記事投稿
次に記事投稿のプログラムを作って行きましょう。こちらもプログラムとデザインを分離して作ります。post.phpがプログラム部分、t_post.phpがデザイン部分です。
blog/post.php
<?php
$error = $title = $content = '';
if (@$_POST['submit']) {
$title = $_POST['title'];
$content = $_POST['content'];
if (!$title) $error .= 'タイトルがありません。<br>';
if (mb_strlen($title) > 80) $error .= 'タイトルが長すぎます。<br>';
if (!$content) $error .= '本文がありません。<br>';
if (!$error) {
$pdo = new PDO("mysql:dbname=blog", "root");
$st = $pdo->query("INSERT INTO post(title,content) VALUES('$title','$content')");
header('Location: index.php');
exit();
}
}
require 't_post.php';
?>
blog/t_post.php
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>記事投稿 | Special Blog</title>
<link rel="stylesheet" href="blog.css">
</head>
<body>
<form method="post" action="post.php">
<div class="post">
<h2>記事投稿</h2>
<p>題名</p>
<p><input type="text" name="title" size="40" value="<?php echo $title ?>"></p>
<p>本文</p>
<p><textarea name="content" rows="8" cols="40"><?php echo $content ?></textarea></p>
<p><input name="submit" type="submit" value="投稿"></p>
<p><?php echo $error ?></p>
</div>
</form>
</body>
</html>
post.phpの解説
プログラムの流れとしては、- 投稿されていないときは投稿フォーム(t_post.php)を表示する。
- 投稿されて、正常に書きこまれたときは、記事一覧(index.php)を表示する。
- 投稿されて、エラーがあるときは、投稿フォーム(t_post.php)に戻りエラーを表示する。
$error = $title = $content = '';
変数の初期化をしています。$errorにエラーメッセージ、$titleに記事タイトル、$contentに記事本文が入ります。
if (@$_POST['submit']) {
投稿ボタンが押されたかチェックしています。
$title = $_POST['title'];
$content = $_POST['content'];
フォームから送られた記事タイトルと本文を変数に格納しています。
if (!$title) $error .= 'タイトルがありません。<br>';
if (mb_strlen($title) > 80) $error .= 'タイトルが長すぎます。<br>';
if (!$content) $error .= '本文がありません。<br>';
フォームの入力にエラーがある場合はエラーメッセージを登録しています。mb_strlenの行は、タイトルの文字数が80文字より多いときエラーにしています。
if (!$error) {
$pdo = new PDO("mysql:dbname=blog", "root");
$st = $pdo->query("INSERT INTO post(title,content) VALUES('$title','$content')");
エラーメッセージが無い場合のみ、INSERT文を発行しています。記事投稿は管理者のみができるという前提なので、セキュリティは考慮していません。
header('Location: index.php');
exit();
記事の書き込みに成功したらindex.phpにジャンプします。header関数はクライアントにデータを送る際のHTTPヘッダを設定するものです。HTTPの機能として「Location: URL」と書くと、ブラウザがヘッダを判断して指定されたURLにジャンプするのです。
require 't_post.php';
デザイン部分であるt_post.phpを取り込んでいます。この行が実行されるのは「最初に呼ばれたとき」と「投稿が失敗したとき」です。t_post.phpの解説
<p><?php echo $error ?></p>
投稿が失敗したときには$errorに格納されたエラーメッセージを表示しています。投稿テスト
題名が無いとき・本文が無いとき・題名が長いときにエラーが出ることと、正常に投稿されたときに一覧画面に反映されることを確認してください。ブログを公開する際には、この記事投稿プログラム(post.php)は他の人がアクセスできない場所に置く必要があります。パスワードをかけるのが一般的です。
コメント投稿
次にコメント投稿のプログラムを作って行きましょう。記事一覧ページから「コメント」リンクが押されたときに呼び出される部分です。こちらもプログラムとデザインを分離して作ります。comment.phpがプログラム部分、t_comment.phpがデザイン部分です。
blog/comment.php
<?php
$post_no = $error = $name = $content = '';
if (@$_POST['submit']) {
$post_no = strip_tags($_POST['post_no']);
$name = strip_tags($_POST['name']);
$content = strip_tags($_POST['content']);
if (!$name) $error .= '名前がありません。<br>';
if (!$content) $error .= 'コメントがありません。<br>';
if (!$error) {
$pdo = new PDO("mysql:dbname=blog", "root");
$st = $pdo->prepare("INSERT INTO comment(post_no,name,content) VALUES(?,?,?)");
$st->execute(array($post_no, $name, $content));
header('Location: index.php');
exit();
}
} else {
$post_no = strip_tags($_GET['no']);
}
require 't_comment.php';
?>
blog/t_comment.php
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>コメント投稿 | Special Blog</title>
<link rel="stylesheet" href="blog.css">
</head>
<body>
<form method="post" action="comment.php">
<div class="post">
<h2>コメント投稿</h2>
<p>お名前</p>
<p><input type="text" name="name" size="40" value="<?php echo $name ?>"></p>
<p>コメント</p>
<p><textarea name="content" rows="8" cols="40"><?php echo $content ?></textarea></p>
<p>
<input type="hidden" name="post_no" value="<?php echo $post_no ?>">
<input name="submit" type="submit" value="投稿">
</p>
<p><?php echo $error ?></p>
</div>
</form>
</body>
</html>
comment.phpの解説
プログラムの流れは基本的に記事投稿と同じです。
$post_no = $error = $name = $content = '';
変数の初期化をしています。$post_noにはコメントの親記事番号、$errorにはエラーメッセージ、$nameには名前、$contentにはコメント本文が入ります。
$post_no = strip_tags($_POST['post_no']);
$name = strip_tags($_POST['name']);
$content = strip_tags($_POST['content']);
フォームからPOSTされた値を①の変数に代入しています。strip_tagsは文字列のタグを除去する関数です。コメントは不特定多数の人から書かれるものなので、セキュリティ対策をしなければいけないのです。
} else {
$post_no = strip_tags($_GET['no']);
}
このelse内は「コメント」リンクを押されたときに実行されます。コメントリンクに付与された親記事番号を受け取って$post_noに代入しています。t_comment.phpの解説
こちらも記事投稿と似ています。ただし、
<input type="hidden" name="post_no" value="<?php echo $post_no ?>">
隠しフィールドで親記事番号を保存している部分が追加されています。こちらには記事一覧画面から「コメント」リンクを押されたときに、GETパラメータで渡された親記事番号が入ります。さらにエラーでフォームに戻ってきた場合も、POSTパラメータで渡された親記事番号が入ります。これでブログとしてのひととおりの機能が完成しました。他に必要な、記事の修正・削除・コメントの削除などの機能は、前章と同じように作ることができるので省略します。