【PHP】PHPだけでCRUD処理を実装してみる(登録・一覧・更新・削除)

phpアイキャッチ PHP

こんにちは、かつコーチです。

ここまでの3回で、PDOでのMySQL接続・データ取得・プリペアドステートメントによる安全なSQLの書き方を学んできました。

今回はそれらを組み合わせて、Webアプリの基本であるCRUD処理を、PHPだけで一通り実装してみます。

CRUDとは、Create(登録)・Read(一覧・取得)・Update(更新)・Delete(削除)の頭文字を取った言葉で、会員管理・投稿管理など、ほとんどのWebアプリの土台になる処理です。

sample_dbusersテーブルを引き続き使いながら、4つの処理を順番に作っていきましょう。

事前準備:共通の接続処理をまとめる

db.phpとして接続処理を切り出す

4つのファイルすべてで接続処理を書くのは非効率なので、まず共通の接続処理をdb.phpとして独立させます。

<?php
// db.php
function getPdo(): PDO
{
    $dsn = 'mysql:host=localhost;dbname=sample_db;charset=utf8mb4';

    try {
        return new PDO($dsn, 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $e) {
        exit('データベース接続エラー: ' . $e->getMessage());
    }
}
?>

こうしておくことで、この後作る4つのファイルはrequire 'db.php';getPdo()の呼び出しだけで接続を使い回せます。

Create:登録処理を作る

insert.phpでユーザーを新規登録する

まずは新しいユーザーを登録する処理です。

<?php
// insert.php
require 'db.php';

$pdo = getPdo();

$name = $_POST['name'] ?? '';
$email = $_POST['email'] ?? '';

if ($name === '' || $email === '') {
    exit('名前とメールアドレスを入力してください');
}

$sql = 'INSERT INTO users (name, email) VALUES (:name, :email)';
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':name', $name, PDO::PARAM_STR);
$stmt->bindValue(':email', $email, PDO::PARAM_STR);
$stmt->execute();

echo '登録が完了しました(ID: ' . $pdo->lastInsertId() . ')';
?>

前回の記事で学んだプリペアドステートメントを使い、フォームから送られてきた値を安全にINSERTしています。

$pdo->lastInsertId()を使うと、直前にINSERTしたレコードのIDを取得できるので、登録直後にそのIDを使って別の処理(詳細画面への遷移など)につなげることができます。

Read:一覧・詳細を表示する

list.phpで全件を一覧表示する

登録したデータを一覧で見られるようにします。

<?php
// list.php
require 'db.php';

$pdo = getPdo();
$stmt = $pdo->query('SELECT * FROM users ORDER BY id DESC');
$users = $stmt->fetchAll();
?>
<table border="1">
    <tr><th>ID</th><th>名前</th><th>メールアドレス</th></tr>
    <?php foreach ($users as $user): ?>
    <tr>
        <td><?= htmlspecialchars($user['id']) ?></td>
        <td><?= htmlspecialchars($user['name']) ?></td>
        <td><?= htmlspecialchars($user['email']) ?></td>
    </tr>
    <?php endforeach; ?>
</table>

ORDER BY id DESCをつけることで、新しく登録したユーザーが一覧の上に表示されるようにしています。

htmlspecialchars()でデータを出力しているのは、データベースの中身に<script>のようなHTMLタグが紛れ込んでいた場合のXSS(クロスサイトスクリプティング)対策です。

SQLインジェクション対策だけでなく、画面に表示する際のエスケープも忘れないようにしましょう。

Update:更新処理を作る

update.phpで指定したIDのユーザーを更新する

登録済みのユーザー情報を書き換える処理です。

<?php
// update.php
require 'db.php';

$pdo = getPdo();

$id = $_POST['id'] ?? '';
$name = $_POST['name'] ?? '';
$email = $_POST['email'] ?? '';

if ($id === '' || $name === '' || $email === '') {
    exit('IDと名前とメールアドレスを入力してください');
}

$sql = 'UPDATE users SET name = :name, email = :email WHERE id = :id';
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->bindValue(':name', $name, PDO::PARAM_STR);
$stmt->bindValue(':email', $email, PDO::PARAM_STR);
$stmt->execute();

echo '更新が完了しました(' . $stmt->rowCount() . '件)';
?>

WHERE id = :idを忘れると、テーブル内の全レコードが同じ内容に上書きされてしまうので、UPDATE文を書くときはWHERE句を必ず確認する習慣が大切です。

$stmt->rowCount()を使うと、実際に何件のレコードが更新されたかを確認できるので、「更新したはずなのに反映されない」といった場合の切り分けにも役立ちます。

Delete:削除処理を作る

delete.phpで指定したIDのユーザーを削除する

最後に削除処理です。

<?php
// delete.php
require 'db.php';

$pdo = getPdo();

$id = $_POST['id'] ?? '';

if ($id === '') {
    exit('削除するIDを指定してください');
}

$sql = 'DELETE FROM users WHERE id = :id';
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();

echo '削除が完了しました(' . $stmt->rowCount() . '件)';
?>

UPDATE文と同じく、WHERE句を書き忘れると全件が消えてしまう、取り返しのつかない処理です。

つまずきやすいポイント:WHERE句なしのUPDATEで全件が上書きされた

実際にやってしまった事故

これは私が実際にローカル環境でやってしまった、今でも忘れられない失敗です。

❌ Before:WHERE句を書き忘れる

<?php
// 修正前:WHERE句がない
$sql = 'UPDATE users SET name = :name';
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':name', 'テスト太郎', PDO::PARAM_STR);
$stmt->execute();
?>

1人分のユーザー名を変更したかっただけなのに、WHERE id = :idを書き忘れたまま実行してしまい、テーブル内の全ユーザーの名前が「テスト太郎」に統一されてしまいました。

幸い学習用のローカル環境だったので実害はありませんでしたが、これが本番環境の会員テーブルだったらと思うとぞっとします。

✅ After:必ずWHERE句で対象を絞り込む

<?php
// 修正後:WHERE句で対象を1件に絞る
$sql = 'UPDATE users SET name = :name WHERE id = :id';
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':name', 'テスト太郎', PDO::PARAM_STR);
$stmt->bindValue(':id', 3, PDO::PARAM_INT);
$stmt->execute();
?>

この経験以来、私はUPDATEDELETEを書くときは、先に同じ条件でSELECT文を実行し、対象が想定した件数かを確認してから書き換える、というルールを自分に課すようになりました。

$stmt->rowCount()が想定より多い数を返したときは、WHERE句を疑うサインとして覚えておいてください。

まとめ

この記事のポイント

  • CRUDはCreate・Read・Update・Deleteの4つの基本操作の総称
  • 接続処理をdb.phpとして切り出すと、4つのファイルで使い回せる
  • INSERT・UPDATE・DELETEはすべてプリペアドステートメントで安全に書く
  • 一覧表示ではhtmlspecialchars()によるXSS対策も忘れない
  • UPDATE・DELETEのWHERE句の書き忘れは、全件を書き換える重大事故につながる

次に読むべき記事

ここまで、接続・取得・安全なSQL・CRUDと一通り実装してきましたが、正直かなりのコード量になったと感じませんでしたか。

次回は、この「素のPHPでDB操作をするのがなぜこんなに大変なのか」を一度整理してみます。

→ 次の記事:PHPでのDB接続、なぜこれが大変なのか

タイトルとURLをコピーしました