引き続きCRUD処理の開発を進めていきましょう。次はカテゴリー更新処理を実装します。

ここでは以下の手順にしたがってプログラムを作成します。

  1. カテゴリー一覧画面の修正 - index.php
  2. カテゴリー詳細画面の作成 - show.php

1. カテゴリー一覧画面の修正 - index.php

index.php ファイルのHTMLコードにカテゴリー詳細画面へのリンクを追加します。

...省略
<body>
  <h3>Categories - Index</h3>
  <hr>
  <table border="1">
    <tr>
      <th>ID</th>
      <th>TITLE</th>
      <th>SHOW</th>
    </tr>
    <?php foreach ($categories as $category) { ?>
    <tr>
      <td><?= htmlspecialchars($category["id"]) ?></td>
      <td><?= htmlspecialchars($category["title"]) ?></td>
      <td><a href="show.php?id=<?= htmlspecialchars($category['id']) ?>">SHOW</a></td>
    </tr>
    <?php } ?>
  </table>
  <hr>
  <a href="create.php">CREATE</a>
</body>
</html>

ここではテーブルに SHOW 列を追加しています。テーブルにはレコードごとに SHOWリンク が表示されるので、クエリパラメータ ?id= の値部分に各レコードのカテゴリーIDを指定しています。

クリックする SHOW リンクによって、送信されるリクエストパラメータ(クエリパラメータ)が変わるように実装しています。

2. カテゴリー詳細画面の作成 - show.php

続いてカテゴリー詳細画面を表示する show.php ファイルを作成します。

<?php
$id = (string)filter_input(INPUT_GET, "id");
if ($id === "") {
    error_log("Validate: id is required.");
    header("Location: error.php");
    exit();
}
if (filter_var($id, FILTER_VALIDATE_INT) === false) {
    error_log("Validate: id is not int.");
    header("Location: error.php");
    exit();
}

try {
    $dsn = "mysql:host=localhost;dbname=eldb;charset=utf8mb4";
    $username = "root";
    $password = "admin";
    $options = [
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_EMULATE_PREPARES => false
    ];
    $pdo = new PDO($dsn, $username, $password);

    $sql = "select id, title from categories where id = :id";
    $ps = $pdo->prepare($sql);
    $ps->bindValue(":id", $id, PDO::PARAM_INT);
    $ps->execute();
    $category = $ps->fetch();
    if ($category === false) {
        error_log("Invalid id. $id");
        header("Location: error.php");
        exit();
    }
} catch (PDOException $e) {
    error_log("PDOException: " . $e->getMessage());
    header("Location: error.php");
    exit();
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>PHP DB</title>
</head>
<body>
  <h3>Categories - Show</h3>
  <hr>
  ID: <?= htmlspecialchars($category["id"]) ?><br>
  TITLE: <?= htmlspecialchars($category["title"]) ?><br>
  <hr>
  <a href="index.php">BACK</a>
</body>
</html>

作成したプログラムについて見ていきましょう。

show.php ファイルはカテゴリーを作成する画面です。先頭部分でリクエストパラメータ(クエリパラメータ)のカテゴリーID( "id" )が正しく送信されているかを検証しています。

項目名 パラメータ名 入力チェック
カテゴリーID id 必須、整数型
<?php
$id = (string)filter_input(INPUT_GET, "id");
if ($id === "") {
    error_log("Validate: id is required.");
    header("Location: error.php");
    exit();
}
if (filter_var($id, FILTER_VALIDATE_INT) === false) {
    error_log("Validate: id is not int.");
    header("Location: error.php");
    exit();
}

通常、カテゴリー一覧画面の SHOW リンクをクリックした場合は、クエリパラメータにカテゴリーID( "id" )を送信するので上記の入力チェックに違反することはありません。ですがブラウザのアドレスバーからURLを直接指定された場合は、クエリパラメータが存在しないこともありうるので、このような入力チェックを実装しています。

入力チェックとは form タグのような入力フォームのリクエストパラメータだけを対象とするのではありません。a タグのURLに指定するクエリパラメータも入力チェックの対象とします。

入力チェックの実装の後の部分ではPDOインスタンスを生成して、データベースに select 文を実行しています。

try {
    $dsn = "mysql:host=localhost;dbname=eldb;charset=utf8mb4";
    $username = "root";
    $password = "admin";
    $options = [
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_EMULATE_PREPARES => false
    ];
    $pdo = new PDO($dsn, $username, $password);

    $sql = "select id, title from categories where id = :id";
    $ps = $pdo->prepare($sql);
    $ps->bindValue(":id", $id, PDO::PARAM_INT);
    $ps->execute();
    $category = $ps->fetch();
    if ($category === false) {
        error_log("Invalid id. $id");
        header("Location: error.php");
        exit();
    }
} catch (PDOException $e) {
    error_log("PDOException: " . $e->getMessage());
    header("Location: error.php");
    exit();
}

ここではリクエストパラメータとして受け取ったカテゴリーID( $id )をselect文のプレースホルダにバインドしています。たとえば、カテゴリーIDに 1 という値を受け取った場合、以下の select 文が実行されます。

select id, title from categories where id = 1

上記の select 文は categories テーブルから id1 のレコードを抽出します。categories テーブルにおいて id 列は主キーであるため、この select 文の期待するレコードは1件(あるいは0件)です。この実行結果をフェッチして $category 変数に代入しています。

$ps->fetch() メソッドが該当レコードが存在しない場合、戻り値に false を返します。ここでは select 文の実行結果が 0 件だった場合はエラー画面にリダイレクトするように実装しています。

さいごにHTMLの出力部分も確認しておきましょう。

  <h3>Categories - Show</h3>
  <hr>
  ID: <?= htmlspecialchars($category["id"]) ?><br>
  TITLE: <?= htmlspecialchars($category["title"]) ?><br>
  <hr>
  <a href="index.php">BACK</a>

ここではselect 文の実行結果である $category 変数の値を画面に出力しています。

動作確認

それではビルトインWebサーバを起動して、ブラウザから実行結果を確認してみましょう。

コマンドラインからビルトインWebサーバを起動します。

$ php -S localhost:8080

ブラウザから以下のURLにアクセスします。

https://〜.vfs.cloud9.ap-northeast-1.amazonaws.com/categories/index.php

上記のようにカテゴリー一覧画面が表示されることを確認します。表示されたテーブルの中から任意のレコードの SHOW リンクをクリックします。

上記のようにカテゴリー詳細画面に選択したレコードが表示されていることを確認できるでしょう。