お問い合わせフォーム3

今回は、画面を切り替えられるようになったindex.phpの確認画面から送信されたデータを
完了画面でPDOを使用してデータを保存できるようにします。

db_app/form3/study内にindex.phpを作成してください。

データベース接続

まずはデータベース接続の方法を軽く復習しておきましょう。

PDOはPHPからデータベース接続に特化したクラスです。
PDOインスタンスのメソッドを使用することでデータベース上のデータを取得したり、削除することができます。

データベースに接続するには最低でも以下の情報が必要です。

  • データベースの種類(mysql)
  • データベース名(db_app)
  • ホスト名(localhost)
  • ユーザー名(root)
  • パスワード(なし)

上記の情報を整理するとこのようになります。

new PDO("データベース名:dbname=データベース名;host=ホスト名", "ユーザー名", "パスワード");

具体的な値を入れるとこのようになります。

new PDO("mysql:dbname=db_app;host=localhost", "root");

これで接続ができるようになりました。

try-catch構文を使用してデータベースにうまく接続できなかった場合のエラーメッセージを出力するようにしてみましょう。

<?php
try {
  // データベース接続時に文字コード(utf8)を指定することで文字化けを防いでいます
  $dbh = new PDO("mysql:dbname=db_app;host=localhost;charset=utf8", "root");
  echo "接続完了";
} catch (PDOException $e) {
  echo "エラーメッセージ : " . $e->getMessage();
}

プレビューして「接続完了」が表示されたら成功です。

プリペアードステートメント

いきなりプリペアードステートメントと言われてもイメージしづらいと思うので、
まずは日本語に直してみます。

  • prepared = 準備(用意)された
  • statement = 文

直訳すると「あらかじめ準備された文」という意味になります。

データベースから1件ずつデータを取り出すとき同じような命令を何度も行うことがあります。
例えば、idが1, 2, 3のデータを取得するSQLを1回ずつ実行すると以下のようになります。

SELECT * FROM contacts WHERE id = 1;
SELECT * FROM contacts WHERE id = 2;
SELECT * FROM contacts WHERE id = 3;

1, 2, 3のように値が変動している箇所にはプレースホルダを置いておきます。
プレースホルダは:〇〇の形式で記述します。
変動する値がどのような値なのかわかりやすくするために具体的なプレースホルダ名にしておくことが望ましいです。
プレースホルダを用いた文字列をprepareメソッドの引数に渡すことで実行したいSQLのテンプレートが準備できます。

<?php
try {
  // データベース接続時に文字コード(utf8)を指定することで文字化けを防いでいます
  $dbh = new PDO("mysql:dbname=db_app;host=localhost;charset=utf8", "root");

  $sql = "SELECT * FROM contacts WHERE id = :id";

  $stmt = $dbh->prepare($sql);
} catch (PDOException $e) {
  echo "エラーメッセージ : " . $e->getMessage();
}

プレースホルダを置くことで、意図しないSQLが送信されてきてもそのSQLは実行されず、
文字列として処理することができます。
プリペアードステートメントとプレースホルダを使用することでSQLインジェクションを防ぐことができます。

プリペアードステートメントの準備はできましたが、プレースホルダに具体的な値を結びつける処理がまだありませんね。
プレースホルダに具体的な値を結びつけることをバインドバインドするというので覚えておいてください。
バインドするにはbindValueメソッドを使用します。
bindValueメソッドは以下の形式で使用します。

$stmt->bindValue("プレースホルダ", "具体的な値"、データ型);

データ型の部分にはPDOで準備されたデータ型を使用します。
数値はPDO::PARAM_INT、文字列はPDO::PARAM_STRを指定してください。
他のデータ型に興味がある方はこちらをご参照ください。

bindValueメソッドを使用してプレースホルダと結びつけが完了したら、SQLを実行する必要があります。
その時に使用するのがexecuteメソッドです。
executeメソッドはprepareメソッドで準備したSQLを実行するメソッドです。

これまでの流れをPHPで記述すると以下のようになります。

<?php
try {
  // データベース接続時に文字コード(utf8)を指定することで文字化けを防いでいます
  $dbh = new PDO("mysql:dbname=db_app;host=localhost;charset=utf8", "root");

  $sql = "SELECT * FROM contacts WHERE id = :id";

  $stmt = $dbh->prepare($sql);

  $stmt->bindValue(":id", 1, PDO::PARAM_INT);

  $stmt->execute();
} catch (PDOException $e) {
  echo "エラーメッセージ : " . $e->getMessage();
}

今回実行したSQLはSELECT文なので、fetchメソッドもしくはfetchAllメソッドでデータを取得する必要があります。
idが1のデータは複数存在しないのでfetchメソッドを使用します。

<?php
try {
  // データベース接続時に文字コード(utf8)を指定することで文字化けを防いでいます
  $dbh = new PDO("mysql:dbname=db_app;host=localhost;charset=utf8", "root");

  $sql = "SELECT * FROM contacts WHERE id = :id";

  $stmt = $dbh->prepare($sql);

  $stmt->bindValue(":id", 1, PDO::PARAM_INT);

  $stmt->execute();

  $data = $stmt->fetch(PDO::FETCH_ASSOC);

  print_r($data);
} catch (PDOException $e) {
  echo "エラーメッセージ : " . $e->getMessage();
}

contactsテーブルにあるidが1のデータが表示されていれば成功です。

データを保存できるようにしよう

SQLでデータを保存(挿入)する方法は覚えていますか?
Insert文で新規データを追加できましたよね。
contactsテーブルに新しくデータを追加する時に値をセットするカラムはname, email, bodyの3つです。
このことから変動する箇所は3つということが推測できます。
上記を踏まえたプリペアードステートメントは以下のようになります。

<?php
try {
  // データベース接続時に文字コード(utf8)を指定することで文字化けを防いでいます
  $dbh = new PDO("mysql:dbname=db_app;host=localhost;charset=utf8", "root");

  $sql = "INSERT INTO contacts (name, email, body) VALUES (:name, :email, :body)";

  $stmt = $dbh->prepare($sql);
} catch (PDOException $e) {
  echo "エラーメッセージ : " . $e->getMessage();
}

bindValueメソッドを使用して:nameには自分の名前、:emailはtest@test.com、
:bodyには「初めてのプリペアードステートメント」をバインドしてexecuteしてみましょう!

<?php
try {
  // データベース接続時に文字コード(utf8)を指定することで文字化けを防いでいます
  $dbh = new PDO("mysql:dbname=db_app;host=localhost;charset=utf8", "root");

  $sql = "INSERT INTO contacts (name, email, body) VALUES (:name, :email, :body)";

  $stmt = $dbh->prepare($sql);

  $stmt->bindValue(":name", "Bob", PDO::PARAM_STR);
  $stmt->bindValue(":email", "test@test.com", PDO::PARAM_STR);
  $stmt->bindValue(":name", "初めてのプリペアードステートメント", PDO::PARAM_STR);

  $stmt->execute();
} catch (PDOException $e) {
  echo "エラーメッセージ : " . $e->getMessage();
}

プレビューしても変化はありませんが、裏側ではしっかりINSERT文が実行されてデータが追加されています。

MySQLにログインして確認してみましょう。

mysql -u root
USE db_app;
SELECT * FROM contacts;

POSTされたデータを保存できるようにしよう

いよいよお問い合わせフォームアプリも残り僅かになりました。
後少しなので頑張ってください!

まずはindex.phpを以下のように修正してください。

<?php
$state = 0;

if (array_key_exists("state", $_POST)) {
  if ($_POST["state"] === "confirm") {
    $state = 1;
  } elseif ($_POST["state"] === "submit") {
    $state = 2;
  }
} else {
  $state = 0;
}
?>

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

<head>
  <meta charset="UTF-8">
  <title>お問い合わせフォーム</title>
</head>

<body>
  <?php if ($state === 0) : ?>
    <h1>お問い合わせ</h1>
    <form action="index.php" method="POST">
      <table>
        <tr>
          <th>お名前</th>
          <td><input type="text" name="name" /></td>
        </tr>
        <tr>
          <th>メールアドレス</th>
          <td><input type="email" name="email" /></td>
        </tr>
        <tr>
          <th>お問い合わせ内容</th>
          <td><textarea name="body"></textarea></td>
        </tr>
        <tr>
          <td><button name="state" value="confirm">確認画面へ</button></td>
        </tr>
      </table>
    </form>
  <?php elseif ($state === 1) : ?>
    <h1>お問い合わせ内容</h1>
    <form action="index.php" method="POST">
      <table>
        <tr>
          <th>お名前</th>
          <td><input type="text" name="name" value=<?php echo $_POST["name"] ?> readonly /></td>
        </tr>
        <tr>
          <th>メールアドレス</th>
          <td><input type="email" name="email" value=<?php echo $_POST["email"] ?> readonly /></td>
        </tr>
        <tr>
          <th>お問い合わせ内容</th>
          <td><textarea name="body" readonly><?php echo $_POST["body"] ?></textarea></td>
        </tr>
        <tr>
          <td><button onClick="history.back()">戻る</button></td>
          <td><button name="state" value="submit">送信</button></td>
        </tr>
      </table>
    </form>
  <?php elseif ($state === 2) : ?>
    <h1>お問い合わせありがとうございました</h1>
    <table>
      <tr>
        <th>お名前</th>
        <td><?php echo $_POST["name"] ?></td>
      </tr>
      <tr>
        <th>メールアドレス</th>
        <td><?php echo $_POST["email"] ?></td>
      </tr>
      <tr>
        <th>お問い合わせ内容</th>
        <td><?php echo $_POST["body"] ?></td>
      </tr>
    </table>
    <p>上記内容でお問い合わせが完了しました</p>
    <a href="index.php">お問い合わせフォームに戻る</a>
  <?php endif; ?>
</body>

</html>

$stateが2、つまり、完了画面に切り替わるときにデータを追加するような処理を実装していきます。
try-catch構文を使用して、データベースと接続、プレースホルダを含むプリペアーステートメントの準備までしてみましょう。

<?php
$state = 0;

if (array_key_exists("state", $_POST)) {
  if ($_POST["state"] === "confirm") {
    $state = 1;
  } elseif ($_POST["state"] === "submit") {
    $state = 2;

    try {
      $dbh = new PDO("mysql:dbname=db_app;host=localhost;charset=utf8", "root");

      $sql = "INSERT INTO contacts (name, email, body) VALUES (:name, :email, :body)";

      $stmt = $dbh->prepare($sql);
    } catch (PDOException $e) {
      echo "エラーメッセージ : " . $e->getMessage();
    }
  }
} else {
  $state = 0;
}
?>

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

<head>
  <meta charset="UTF-8">
  <title>お問い合わせフォーム</title>
</head>

<body>
  <?php if ($state === 0) : ?>
    <h1>お問い合わせ</h1>
    <form action="index.php" method="POST">
      <table>
        <tr>
          <th>お名前</th>
          <td><input type="text" name="name" /></td>
        </tr>
        <tr>
          <th>メールアドレス</th>
          <td><input type="email" name="email" /></td>
        </tr>
        <tr>
          <th>お問い合わせ内容</th>
          <td><textarea name="body"></textarea></td>
        </tr>
        <tr>
          <td><button name="state" value="confirm">確認画面へ</button></td>
        </tr>
      </table>
    </form>
  <?php elseif ($state === 1) : ?>
    <h1>お問い合わせ内容</h1>
    <form action="index.php" method="POST">
      <table>
        <tr>
          <th>お名前</th>
          <td><input type="text" name="name" value=<?php echo $_POST["name"] ?> readonly /></td>
        </tr>
        <tr>
          <th>メールアドレス</th>
          <td><input type="email" name="email" value=<?php echo $_POST["email"] ?> readonly /></td>
        </tr>
        <tr>
          <th>お問い合わせ内容</th>
          <td><textarea name="body" readonly><?php echo $_POST["body"] ?></textarea></td>
        </tr>
        <tr>
          <td><button onClick="history.back()">戻る</button></td>
          <td><button name="state" value="submit">送信</button></td>
        </tr>
      </table>
    </form>
  <?php elseif ($state === 2) : ?>
    <h1>お問い合わせありがとうございました</h1>
    <table>
      <tr>
        <th>お名前</th>
        <td><?php echo $_POST["name"] ?></td>
      </tr>
      <tr>
        <th>メールアドレス</th>
        <td><?php echo $_POST["email"] ?></td>
      </tr>
      <tr>
        <th>お問い合わせ内容</th>
        <td><?php echo $_POST["body"] ?></td>
      </tr>
    </table>
    <p>上記内容でお問い合わせが完了しました</p>
    <a href="index.php">お問い合わせフォームに戻る</a>
  <?php endif; ?>
</body>

</html>

POSTされたデータを変数に代入し、その変数をプレースホルダとバインドしてからexecuteします。

<?php
$state = 0;

if (array_key_exists("state", $_POST)) {
  if ($_POST["state"] === "confirm") {
    $state = 1;
  } elseif ($_POST["state"] === "submit") {
    $state = 2;

    try {
      $dbh = new PDO("mysql:dbname=db_app;host=localhost;charset=utf8", "root");

      $sql = "INSERT INTO contacts (name, email, body) VALUES (:name, :email, :body)";

      $stmt = $dbh->prepare($sql);

      $name = $_POST["name"];
      $email = $_POST["email"];
      $body = $_POST["body"];

      $stmt->bindValue(":name", $name, PDO::PARAM_STR);
      $stmt->bindValue(":email", $email, PDO::PARAM_STR);
      $stmt->bindValue(":body", $body, PDO::PARAM_STR);

      $stmt->execute();
    } catch (PDOException $e) {
      echo "エラーメッセージ : " . $e->getMessage();
    }
  }
} else {
  $state = 0;
}
?>

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

<head>
  <meta charset="UTF-8">
  <title>お問い合わせフォーム</title>
</head>

<body>
  <?php if ($state === 0) : ?>
    <h1>お問い合わせ</h1>
    <form action="index.php" method="POST">
      <table>
        <tr>
          <th>お名前</th>
          <td><input type="text" name="name" /></td>
        </tr>
        <tr>
          <th>メールアドレス</th>
          <td><input type="email" name="email" /></td>
        </tr>
        <tr>
          <th>お問い合わせ内容</th>
          <td><textarea name="body"></textarea></td>
        </tr>
        <tr>
          <td><button name="state" value="confirm">確認画面へ</button></td>
        </tr>
      </table>
    </form>
  <?php elseif ($state === 1) : ?>
    <h1>お問い合わせ内容</h1>
    <form action="index.php" method="POST">
      <table>
        <tr>
          <th>お名前</th>
          <td><input type="text" name="name" value=<?php echo $_POST["name"] ?> readonly /></td>
        </tr>
        <tr>
          <th>メールアドレス</th>
          <td><input type="email" name="email" value=<?php echo $_POST["email"] ?> readonly /></td>
        </tr>
        <tr>
          <th>お問い合わせ内容</th>
          <td><textarea name="body" readonly><?php echo $_POST["body"] ?></textarea></td>
        </tr>
        <tr>
          <td><button onClick="history.back()">戻る</button></td>
          <td><button name="state" value="submit">送信</button></td>
        </tr>
      </table>
    </form>
  <?php elseif ($state === 2) : ?>
    <h1>お問い合わせありがとうございました</h1>
    <table>
      <tr>
        <th>お名前</th>
        <td><?php echo $_POST["name"] ?></td>
      </tr>
      <tr>
        <th>メールアドレス</th>
        <td><?php echo $_POST["email"] ?></td>
      </tr>
      <tr>
        <th>お問い合わせ内容</th>
        <td><?php echo $_POST["body"] ?></td>
      </tr>
    </table>
    <p>上記内容でお問い合わせが完了しました</p>
    <a href="index.php">お問い合わせフォームに戻る</a>
  <?php endif; ?>
</body>

</html>

プレビューしてお問い合わせフォームからデータを送信してみましょう。

データが追加されたどうかを確認するにはMySQLにログインしてSELECT文を実行しましょう。

mysql -u root
USE db_app;
SELECT * FROM contacts;

データが追加されていればお問い合わせフォームアプリは完成です!
おめでとうございます!

お問い合わせフォーム1つにしてもかなり頭を使って疲れているのではないでしょうか?
今まで学習してきた内容をギュッと詰め込んでいるの疲れるのも無理はないです。

1回作っただけでは理解することができない部分もあるので、必ず復習してください。
どこから理解があやふやになってしまったかを知ることが非常に重要になってきます。
そこさえ分かれば復習の効率が上がり、理解スピードも格段に上がります。
あやふやな箇所を洗い出して復習する方法は今後も役に立つので覚えておきましょう。

長い道のりでしたが、基礎編はこれで以上です!
お疲れ様でした!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です