こんにちは、WEBデザイナーのねこふらいです。

肌寒くなり、いよいよ冬本番。コタツとミカンが恋しい季節になってきましたね。


さて、唐突ですがWordPressって便利ですよね! サイト運営が簡単に行えて、プラグインを使えば機能を拡張できるというのも素晴らしい。ブログ機能もあり、訪問者がコメントを投稿できるというのもグッド。今やWordPressは市場の6割近くを占めるCMSとなりました。


そこでふと思ったのですが、WordPressのようなコメント機能を作ってみたい!

コメントを受け付けたら管理画面で承認したり、削除したり、返信もできたら最高ですね。


と、いうわけで今回から数回に分けて『WordPressのようなコメント管理システムを作ってみよう』と思います!

対象者
  • PHPの基礎がわかる人
  • phpMyAdminの使い方がわかる人

これから作っていくもの

はい。改めまして今回から数回に分けて、簡易コメント管理システム『ふらこめ(necofly comment system)』なるものを作っていこうと思います。

WordPressのコメント管理機能をイメージしてくれるとありがたいです。

『ふらこめ』で実装する機能は以下の通り。

ふらこめで実装する機能
  • コメントの受付
  • 管理画面でのコメント管理
  • コメントの承認・削除・編集・返信機能
  • 管理画面へのログイン機能
  • 新規管理アカウント取得機能

ざっとこんな感じです!

コメント欄からコメントを受け付けられるのはもちろんのこと、コメントの管理画面も欲しいところですね。

ただ見る人全員が好き勝手管理できたら大変なので、ログイン機能も設置。ついでに新規管理アカウント取得(サインアップ)機能も作ってしまいましょう。

最終的には下記のようなファイル構造になります。

hura_come
├── admin
│   ├── dashboard.php
│   ├── delete.php
│   ├── display.php
│   ├── login.php
│   ├── logout.php
│   ├── reply.php
│   ├── signup.php
│   └── update.php
├── css
│   └── style.css
├── db_info
│   └── db_info.php
├── images
│   └── logo.jpg
└── save_comment.php

では早速作っていきましょう!

今回作るもの – 『コメント投稿機能』

ねこふらい
記念すべき1回目では『コメント投稿機能』を作っていくよ。
メダ太郎
生徒役1のメダ太郎と
めだ吉
生徒役2のめだ吉です。二人合わせてめだかーずです。よろしく。


今回作っていくのは『コメント機能』です。

フォームにコメントを入力して送信ボタンを押すと、入力した値をデータベースに保存するプログラムです。

第1回目ということで、データベースの作成から見ていきましょう。

phpMyAdminでデータベースとテーブルの準備

ねこふらい
プログラムを制作し始める前に、まずは『ふらこめ』のデータベースとテーブルを用意しよう。ちなみに今回はMAMPを使ってローカル環境で開発しているよ。

まずはphpMyAdminにログインして、hurakomeデータベースを作ります。

hurakomeデータベースが作成できたら、次に、投稿されたコメントを格納するcom_tableテーブルを作成します。必要なカラムは6つ。下記の画像を参考に6つのカラムを作成してください。idにAUTO_INCREMENTの設定をするのをお忘れなく!

手動で作成する以外にも、下記のコードを『SQL』に貼り付けて作成することもできます!

CREATE TABLE IF NOT EXISTS `com_table` (
  `id` int(255) NOT NULL,
  `comment` varchar(1000) COLLATE utf8_unicode_ci DEFAULT NULL,
  `name` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL,
  `email` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
  `display` tinyint(1) DEFAULT NULL,
  `reply` varchar(1000) COLLATE utf8_unicode_ci DEFAULT NULL
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

ALTER TABLE `com_table`
  ADD PRIMARY KEY (`id`);

ALTER TABLE `com_table`
  MODIFY `id` int(255) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=7;
ねこふらい
各カラムの役割はプログラムを作成する中で説明していきます。
めだ吉
できました!
ねこふらい
オッケー。ではphpMyAdminの操作はこれで終了。これから実際にプログラムを書いていきましょう!
メダ太郎
ドキドキするぜ!

db_info.phpの作成

全体のコード

今回のプログラムではデータベースへアクセスすることになるので、プログラムを書き始める前に、接続に必要な情報をこのファイルにまとめておきます。

<?php

$host = 'localhost';
$user = 'root';
$pass = 'root';
$db_name = 'hurakome';
めだ吉
このファイルでは何をしているんですか?
ねこふらい
データベースに接続する際に必要となる情報を、変数にまとめているんだ。データベースの情報を変数に格納することで、もし情報が変わったとしても簡単に対処できるようになるんだ。

データベース接続時に必要になるのが『ホスト名』『ユーザー名』『パスワード』『データベース名』の4つです。

プログラムを設置する環境によって、これらの情報は変わってしまうので、最初に専用のファイルを準備して変数に格納してしまいましょう。もし環境が変わったとしても、このファイルを修正するだけでいいので管理が非常に楽になります。

ファイルの配置
ねこふらい
db_info.phpが作成できたら、以下のツリー構造のようにディレクトリに配置してください。
hura_come
└──  db_info
    └── db_info.php

db_info.phpの作成はこれで終了です!

save_comment.phpの作成

全体のコード

save_comment.phpを、実際にブラウザで出力するとこのようになります。

コメント・お名前・メールアドレスに必要事項を記入して投稿ボタンを押すと、入力した内容がhurakomeデータベースのcom_tableテーブルに保存されます。


ねこふらい
まずは全体のソースコードを見てみましょう。
<?php

require 'db_info/db_info.php';


//データベース接続
try{
    $dbh = new PDO(
        'mysql:host=' . $host . '; dbname=' . $db_name . '; charset=utf8',
        $user,
        $pass,
        array(
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_EMULATE_PREPARES => false,
        )
    );
}catch(PDOException $e){
    echo '<div class="server_error">現在サーバーエラーが発生しています。</div>';
}


//コメントが入力されたら
if($_SERVER['REQUEST_METHOD'] === 'POST'){

    //メッセージの初期化
    $error = '';
    $success = '';

    //入力値のチェック
    if(empty($_POST['comment']) || empty($_POST['name']) || empty($_POST['email'])){
        $error = '入力されていない項目があります。';
    } elseif(strlen($_POST['comment']) > 1000){
        $error = 'コメントは1000文字以内でご記入ください。';
    } elseif(strlen($_POST['name']) > 15){
        $error = 'お名前は15文字以内でご入力ください。';
    } elseif(strlen($_POST['email']) > 100){
        $error = 'メールアドレスは100文字以内でご入力ください。';
    } else {
        $comment = htmlspecialchars( $_POST['comment'] ,ENT_QUOTES, 'UTF-8');
        $name = htmlspecialchars( $_POST['name'] ,ENT_QUOTES, 'UTF-8');
        $email = htmlspecialchars( $_POST['email'] ,ENT_QUOTES, 'UTF-8');

        try{

            //SQL操作
            $sql = 'INSERT INTO com_table(comment,name,email) VALUE (?,?,?)';
            $data[] = $comment;
            $data[] = $name;
            $data[] = $email;
            $stmt = $dbh->prepare($sql);
            $stmt->execute($data);

            //接続解除
            $dbh = null;

            $success = 'コメントを投稿しました。承認をお待ちください。';

        }catch(PDOException $e){
            echo '<div class="server_error">現在サーバーエラーが発生しています。</div>';
        }

    }

}

?>



<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>コメント入力ページ</title>
    <meta name="viewport" content="width=1000">
    <meta name="robots" content="noindex,nofollow">
    <link href="https://use.fontawesome.com/releases/v5.0.6/css/all.css" rel="stylesheet">
    <link rel="stylesheet" type="text/css" href="css/style.css">
</head>

<body>

    <section class="wrap ww">
        <img src="images/logo.jpg" class="ic">

        <?php

        if(isset($error)){
            echo '<ul class="error"><li>' . $error . '</li></ul>';
        }

         if(isset($success)){
            echo '<ul class="success"><li>' . $success . '</li></ul>';
        }

        ?>

        <form action="save_comment.php" method="post" class="comment_form">
            <label>コメント</label>
            <textarea name="comment"></textarea>
            <label>お名前</label>
            <input type="text" name="name">
            <label>メールアドレス</label>
            <input type="email" name="email">
            <input type="submit" name="submit" value="投稿">
        </form>

    </section>

</body>

</html>

save_comment.phpは下記のように配置してください。

hura_come
├── db_info
│   └── db_info.php
└── save_comment.php

ではコードを細分化して詳しく説明していきます。

HTMLの用意
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>コメント入力ページ</title>
    <meta name="viewport" content="width=1000">
    <meta name="robots" content="noindex,nofollow">
    <link href="https://use.fontawesome.com/releases/v5.0.6/css/all.css" rel="stylesheet">
    <link rel="stylesheet" type="text/css" href="css/style.css">
</head>

<body>

    <section class="wrap ww">
        <img src="images/logo.jpg" class="ic">

        <?php

        if(isset($error)){
            echo '<ul class="error"><li>' . $error . '</li></ul>';
        }

         if(isset($success)){
            echo '<ul class="success"><li>' . $success . '</li></ul>';
        }

        ?>

        <form action="save_comment.php" method="post" class="comment_form">
            <label>コメント</label>
            <textarea name="comment"></textarea>
            <label>お名前</label>
            <input type="text" name="name">
            <label>メールアドレス</label>
            <input type="email" name="email">
            <input type="submit" name="submit" value="投稿">
        </form>

    </section>

</body>

</html>

まずはHTMLを準備します。

色々書いていますが、重要なのはform部分です。

<form action="save_comment.php" method="post" class="comment_form">
      <label>コメント</label>
      <textarea name="comment"></textarea>
      <label>お名前</label>
      <input type="text" name="name">
      <label>メールアドレス</label>
      <input type="email" name="email">
      <input type="submit" name="submit" value="投稿">
</form>

同一ファイル内で処理を行うので、データの受け渡し先はsave_comment.phpです。method属性にはpostを指定しましょう。

コメント入力欄にはcomment、名前の入力欄にはname、メールアドレスの入力欄にはemailと、name属性を与えます。

db_info.phpの呼び出し
ねこふらい
HTMLが書けたら、次はPHPです。
require 'db_info/db_info.php';

データベースに接続するためには、先ほど作成したdb_info.phpを呼び出す必要があります。

ここではrequireを使って呼び出しています。


ねこふらい
ファイルを呼び出すときはパスに気をつけないといけないね。パスを間違ってしまうとファイルが呼び出せなくなるから、エラーが発生してしまうんだ。
メダ太郎
ファイル構造には注意を払わなくちゃいけないってことだね。
データベースへの接続
try{
    $dbh = new PDO(
        'mysql:host=' . $host . '; dbname=' . $db_name . '; charset=utf8',
        $user,
        $pass,
        array(
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_EMULATE_PREPARES => false,
        )
    );
}catch(PDOException $e){
    echo '<div class="server_error">現在サーバーエラーが発生しています。</div>';
}

次にデータベースへの接続を行います。

データベースに接続するときはtry~catch文を使って、例外処理も定義しておきましょう。

ここではPODクラスを使用してデータベースへ接続しています。よく見るとdb_info.phpで定義した変数がありますね。ホスト名・データベース名・ユーザー名・パスワードは、このようにデータベースに接続する時に使われます。


ねこふらい
もし接続がうまくいかなかったら、例外処理としてエラーメッセージを表示させてあげよう。
メダ太郎
tryが通常処理で、エラーが出たらcatch部分の処理が実行されるんだな!
投稿ボタンが押されたら処理を開始
if($_SERVER['REQUEST_METHOD'] === 'POST'){

    //メッセージの初期化
    $error = '';
    $success = '';

if文を使い、POSTでデータが送られてきたら(つまりフォームのボタンが押されたら)処理を実行します。

まずは$errorと$successの2つの変数を準備します。


めだ吉
この変数は何に使うんですか?
ねこふらい
$errorはエラーメッセージ、$successは『投稿が完了しました』という処理完了メッセージを格納する変数だよ。詳しくは次の項目で説明するよ。
入力された値をチェック
if(empty($_POST['comment']) || empty($_POST['name']) || empty($_POST['email'])){
    $error = '入力されていない項目があります。';
} elseif(strlen($_POST['comment']) > 1000){
    $error = 'コメントは1000文字以内でご記入ください。';
} elseif(strlen($_POST['name']) > 15){
    $error = 'お名前は15文字以内でご入力ください。';
} elseif(strlen($_POST['email']) > 100){
    $error = 'メールアドレスは100文字以内でご入力ください。';
} else {
    $comment = htmlspecialchars( $_POST['comment'] ,ENT_QUOTES, 'UTF-8');
    $name = htmlspecialchars( $_POST['name'] ,ENT_QUOTES, 'UTF-8');
    $email = htmlspecialchars( $_POST['email'] ,ENT_QUOTES, 'UTF-8');

投稿ボタンが押されたからといって、何でもかんでもデータベースに保存するわけにはいきません。

例えば、フォームに何も記述しないまま投稿ボタンを押してしまったら、NULLがデータベースに保存されてしまいます。

なので、入力された値がデータベースに保存していいものかどうか、まずはチェックしなければなりません。

入力値のチェック
  • コメント・名前・メースアドレスのどれか1つでも未入力のものがあればエラーメッセージを表示
  • コメントが1000文字を超えたらエラー処理
  • 名前が15文字を超えたらエラー処理
  • メースアドレスが100文字以上ならエラー処理
ねこふらい
このチェックに引っかかった場合、先ほど定義した$error変数にエラーメッセージが格納されるんだ。ここで先ほどのHTMLの一文を見てみよう。
<?php

    if(isset($error)){
        echo '<ul class="error"><li>' . $error . '</li></ul>';
    }

    if(isset($success)){
        echo '<ul class="success"><li>' . $success . '</li></ul>';
    }

?>

HTMLの中に上記のPHPが埋め込まれています。

これは「もし変数errorに何かしらの値があれば、変数errorを出力する」「もし変数successに何かしらの値があれば、変数successを出力する」というものです。入力値に誤りがあれば、それに対応したエラーメッセージがここに表示されます。

そして上記の条件に当てはまらなかった、つまりエラーが発生しなかった場合は、入力された値をhtmlspecialcharsでエスケープ処理し、それぞれ変数に格納します。

SQL文でデータを保存
try{

    //SQL操作
    $sql = 'INSERT INTO com_table(comment,name,email) VALUE (?,?,?)';
    $data[] = $comment;
    $data[] = $name;
    $data[] = $email;
    $stmt = $dbh->prepare($sql);
    $stmt->execute($data);

    //接続解除
    $dbh = null;

    $success = 'コメントを投稿しました。承認をお待ちください。';

}catch(PDOException $e){
    echo '<div class="server_error">現在サーバーエラーが発生しています。</div>';
}

入力した値に問題がなければ、SQL文を使ってデータを保存します。この時もtry~catch文を使って、例外処理も定義しておきましょう。


ねこふらい
INSERTは対象のテーブルにデータを追加する構文だよ。

INSERTで各種データを追加できたら、$successにメッセージを代入して、処理は終了です。

$successに値が入ったため、変数errorのように成功メッセージが表示されます。

以上でsave_comment.phpの作成は完了です。

スタイルシートで外観を調整

おまけですが、外観の見栄えをよくするために、CSSのコードも貼り付けておきます(スタイルの解説は割愛)。

よければお使いください。

@charset "UTF-8";

html,
body {
    margin: 0;
    padding: 0;
    -webkit-text-size-adjust: 100%;
    font-family: apple-system, BlinkMacSystemFont, "Helvetica Neue", "Segoe UI", "Noto Sans Japanese", "ヒラギノ角ゴ ProN W3", Meiryo, sans-serif;
}

body {
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    overflow-x: hidden;
    background: #5433FF;
    background: -webkit-linear-gradient(to right, #A5FECB, #20BDFF, #5433FF);
    background: linear-gradient(to right, #A5FECB, #20BDFF, #5433FF);
}

body::-webkit-scrollbar {
    display: none;
}

p {
    font-size: 18px;
    line-height: 1.5;
    font-family: apple-system, BlinkMacSystemFont, "Helvetica Neue", "Segoe UI", "Noto Sans Japanese", "ヒラギノ角ゴ ProN W3", Meiryo, sans-serif;
    color: #333;
}

img {
    max-width: 100%;
    height: auto;
}

a {
    outline: none;
    text-decoration: none;
}

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

table {
    border-collapse: collapse;
    border-spacing: 0;
}

input,
textarea {
    display: block;
    margin: auto;
}

.wrap {
    width: 950px;
    margin: auto;
    padding: 30px 0;
    position: relative;
}

.login{
    height: 100vh;
    padding: 0;
}

.ww {
    background: #fff;
}

.ic {
    display: block;
    margin: auto;
}


/* comment_form
******************************************************************************/

.comment_form {
    width: 80%;
    margin: 50px auto;
}

.comment_form input[type="text"],
.comment_form input[type="email"],
.comment_form textarea {
    width: 90%;
    background: #eeeeee;
    font-size: 16px;
    border: solid 1px #999;
    border-radius: 5px;
    padding: 2%;
    color: #333;
    font-weight: bold;
}

.comment_form textarea {
    height: 100px;
}

.comment_form label {
    display: block;
    width: 92%;
    margin: 20px auto 5px;
    padding: 0;
    font-weight: bold;
    color: #333;
    font-size: 14px;
}

.comment_form input[type="submit"] {
    background: #5433FF;
    background: -webkit-linear-gradient(to right, #cea5fe, #5433FF);
    background: linear-gradient(to right, #cea5fe, #5433FF);
    color: #fff;
    font-size: 18px;
    margin: 20px auto;
    padding: 5px 10px;
    width: 100px;
    border-radius: 50px;
    font-weight: 900;
}

.comment_form input[type="submit"]:hover {
    opacity: 0.8;
}


/*error*/

.error li {
    width: 75%;
    margin: 20px auto;
    color: #ff0d4b;
    font-weight: bold;
}

.server_error {
    background: #000;
    padding: 20px 0;
    text-align: center;
    font-size: 20px;
    color: #ffff00;
    font-weight: 900;
}

/*success*/

.success li {
    width: 75%;
    margin: 20px auto;
    color: #4e0dff;
    font-weight: bold;
}


/* comment list
******************************************************************************/

h2{
    font-size: 22px;
    text-align: center;
    color: #333;
    position: relative;
    padding: 10px 0;
}

h2:after{
    content: "";
    display: block;
    width: 70px;
    height: 3px;
    border-radius: 50px;
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    margin: auto;
    background: #333;
}

.comment_list{
    width: 90%;
    margin: auto;
}

.comment_list li{
    padding: 10px;
    font-size: 16px;
    color: #333;
}

.comment_list li:nth-child(even){
    background: #eee;
}

.comment_list .name{
    margin: 10px;
    font-size: 12px;
    font-style: italic;
    color: #888;
}

.comment_list li > .reply{
    padding: 10px 20px 10px 10px;
}

.comment_list li > .reply li::before {
    font-family: "Font Awesome 5 Free";
    content: "\f3e5";
    display: inline-block;
    line-height: 1;
    font-weight: bold;
    margin-right: 10px;
}

/* sign up ・ Login
******************************************************************************/

.logo {
    width: 40%;
}

.login_wrap {
    width: 70%;
    margin: auto;
    background: #fff;
    padding: 5% 0%;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translateY(-50%) translateX(-50%);
    -webkit- transform: translateY(-50%) translateX(-50%);
    border-radius: 8px;
}

.login_form {
    width: 85%;
    margin: 0 auto;
}

.login_form input[type="email"],
.login_form input[type="password"] {
    width: 70%;
    font-size: 18px;
    color: #666;
    font-weight: bolder;
    padding: 2% 5%;
    border: solid 1px #999;
    background: #eeeeee;
    border-radius: 50px;
}

.login_form label {
    text-align: center;
    display: block;
    margin: 20px 0 5px;
    font-weight: bold;
    color: #999;
}

.login_form input[type="submit"] {
    background: #5433FF;
    background: -webkit-linear-gradient(to right, #cea5fe, #5433FF);
    background: linear-gradient(to right, #cea5fe, #5433FF);
    color: #fff;
    font-size: 18px;
    margin: 20px auto;
    padding: 5px 10px;
    width: 100px;
    border-radius: 50px;
    font-weight: 900;
}

.login_form input[type="submit"]:hover {
    opacity: 0.8;
}

/* dashboard
******************************************************************************/

.top_bar{
    background: #1a1a1a;
    padding: 10px 20px 10px;
}

.top_bar p{
    color: #999;
    font-size: 16px;
    text-align: right;
}

.logout_button{
    display: inline-block;
    color: #ccc;
    border: solid 1px #ccc;
    padding: 5px;
    font-size: 14px;
    transition: all 0.5s;
}

.logout_button:hover{
    opacity: 0.5;
}

/*table*/

.db_table{
    width: 100%;
    margin: auto;
    background: #fff;
    word-wrap: break-word
}

.db_table thead th{
    background: #333;
    color: #ccc;
    font-size: 18px;
    padding: 1%;
    border: solid 1px #777;
}

.db_table thead th:nth-child(1),
.db_table thead th:nth-child(3),
.db_table thead th:nth-child(4){
    width: 20%;
}

.db_table thead th:nth-child(2){
    width: 40%;
}

.db_table tbody td{
    padding: 1%;
    border: 1px solid #ccc;
    font-size: 14px;
    color: #333;
}

.db_table tbody tr:nth-child(even){
    background: #eee;
}

/*button*/
.go_home{
    display: block;
    text-align: center;
}

style.cssを準備できたら、下記のように配置してください。

hura_come
├── css
│   └── style.css
├── db_info
│   └── db_info.php
└── save_comment.php

実際にコメントを投稿してみよう

ねこふらい
ここまでできたら、実際にコメントを投稿できるか試してみよう!
メダ太郎
投稿できたぜ!
めだ吉
今回入力したコメント・名前・メールアドレスはcom_tableテーブルのcomment・name・emailカラムに保存されるんですね。

コメント投稿フォームで入力した値は、com_tableテーブルのcomment・name・emailカラムに保存されます。idはAUTO_INCREMENTにチェックを入れたので、自動的に番号が割り振られています。

まとめ

今回制作したのは、db_info.phpとsave_comment.phpです。
style.cssもおまけで掲載していますので、よければ使ってください。

ここまで制作したら、下記のようなツリー構造になっていると思います。

hura_come
├── css
│   └── style.css
├── db_info
│   └── db_info.php
└── save_comment.php
ねこふらい
次回は管理画面にアクセスするためのログイン機能を作成します。第2回はこちら


HOME > ふらこめ > WordPressのようなコメント管理システムを作ってみよう – 第1回