見出し画像

PHPでデータベースをCRUD操作する③

 前回の記事でユーザー登録機能と一覧表示機能を作成しました。今回は更新機能および削除機能、詳細参照機能を作成します。


筆者の開発環境

PC:Apple M1 チップ搭載MacBook Air
OS:macOS Ventura 13.6
MAMP:6.8
PHP:8.2.0

ユーザー編集フォームの作成

UsersControllerにeditメソッドを追加

 UsersController.phpを下記のように編集し、editメソッドを追加してください。
 ルーティングから受け取ったidを使いuserインスタンスを作成しています。userの情報はテンプレートの中で使用しますので引数で渡します。
 編集フォームでもCSRF対策を行います。

<?php

(省略)

class UsersController
{
    (省略)

    /**
     * 登録処理
     *
     * @return void
     */
    public function store(): void
    {
        try {
            $sessionProvider = new NativeSessionProvider();
            $easyCSRF = new EasyCSRF($sessionProvider);
            $easyCSRF->check($_ENV['CSRF_SALT'], $_POST['csrf_token']);

            $params = [];
            foreach ($_POST as $key => $value) {
                $params[$key] = trim($value);
            }

            User::create($params);

            (new Redirector)->to('users');
        } catch(InvalidCsrfTokenException $e) {
            echo $e->getMessage();
        }
    }

    /**
     * 編集フォームを表示
     *
     * @param integer $id
     * @return void
     */
    public function edit(int $id): void
    {
        $user = User::find($id);

        $sessionProvider = new NativeSessionProvider();
        $easyCSRF = new EasyCSRF($sessionProvider);
        $csrfToken = $easyCSRF->generate($_ENV['CSRF_SALT']);

        (new View)->render('users/edit.php', compact('user', 'csrfToken'), ['title' => '編集 | ユーザー管理']);
    }
}

ルーティングの追加

 editメソッドを追加しましたのでルーティングも追加しましょう。bootstrap.phpファイルを下記のように編集してください。

<?php

(省略)

$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
    // ここにルーティング定義を追加していきます
    $r->addRoute('GET', '/users', function () {
        return (new UsersController)->index();
    });

    $r->addRoute('GET', '/users/create', function () {
        return (new UsersController)->create();
    });

    $r->addRoute('POST', '/users', function () {
        return (new UsersController)->store();
    });

    $r->addRoute('GET', '/users/{id:\d+}/edit', function ($vars) {
        return (new UsersController)->edit($vars['id']);
    });
});

編集フォームの作成

下記のコマンドを実行してedit.phpを作成してください。

touch resources/views/users/edit.php

 edit.phpを下記のように編集してください。フォームの飛び先のルーティングは後ほど作成します。

<?php ob_start(); ?>

<form action="/users/<?= $user->id; ?>" method="PATCH" class="mt-5">
    <div class="mb-3">
        <label for="name" class="form-label">名前</label>
        <input type="text" id="name" class="form-control" name="name" maxlength="50" value="<?= $user->name; ?>">
    </div>
    <div class="mb-3">
        <label for="email" class="form-label">メールアドレス</label>
        <input type="text" id="name" class="form-control" name="email" maxlength="100" value="<?= $user->email; ?>">
    </div>
    <input type="hidden" name="csrf_token" value="<?= $csrfToken; ?>">
    <button type="submit" class="btn btn-primary">更新</button>
</form>

<?php $content = ob_get_clean(); ?>

ユーザー更新処理の作成

Userモデルにupdateメソッドを追加

User.phpを下記のように編集し、updateメソッドを追加してください。

<?php

namespace App\Models;

use App\Utilities\DatabaseConnector;
use PDO;

class User
{
    (省略)

    /**
     * 登録処理
     *
     * @param array $params
     * @return self
     */
    public static function create(array $params): self
    {
        $dbh = DatabaseConnector::connect();
        $sql = 'INSERT INTO users (name, email, created_at, updated_at) VALUES(:name, :email, now(), now())';
        $stmt = $dbh->prepare($sql);
        $stmt->bindValue(':name', $params['name'], PDO::PARAM_STR);
        $stmt->bindValue(':email', $params['email'], PDO::PARAM_STR);
        $stmt->execute();

        return self::find($dbh->lastInsertId());
    }

    /**
     * 更新処理
     *
     * @param array $params
     * @return boolean
     */
    public function update(array $params): bool
    {
        $dbh = DatabaseConnector::connect();
        $sql = 'UPDATE users SET name = :name, email = :email, updated_at = now() WHERE id = :id';
        $stmt = $dbh->prepare($sql);
        $stmt->bindValue(':name', $params['name'], PDO::PARAM_STR);
        $stmt->bindValue(':email', $params['email'], PDO::PARAM_STR);
        $stmt->bindValue(':id', $this->id, PDO::PARAM_INT);

        return $stmt->execute();
    }
}

UsersControllerにupdateメソッドを追加

 UsersController.phpを下記のように編集し、updateメソッドを追加してください。
 CSRFトークンをチェックしています。
 フォームからPOSTされた値は$_POSTという特別な変数の中に入っています。foreachで1件ずつ取り出しながら前後の空白を除去して$params変数に詰め直しています。
 $params変数を先ほど作成したUserクラスのupdate()メソッドに渡しています。
 更新に成功したら一覧画面にリダイレクトします。

<?php

(省略)

class UsersController
{
    (省略)

    /**
     * 編集フォームを表示
     *
     * @param integer $id
     * @return void
     */
    public function edit(int $id): void
    {
        $user = User::find($id);

        $sessionProvider = new NativeSessionProvider();
        $easyCSRF = new EasyCSRF($sessionProvider);
        $csrfToken = $easyCSRF->generate($_ENV['CSRF_SALT']);

        (new View)->render('users/edit.php', compact('user', 'csrfToken'), ['title' => '編集 | ユーザー管理']);
    }

    /**
     * 更新処理
     *
     * @param integer $id
     * @return void
     */
    public function update(int $id): void
    {
        $user = User::find($id);

        try {
            $sessionProvider = new NativeSessionProvider();
            $easyCSRF = new EasyCSRF($sessionProvider);
            $easyCSRF->check($_ENV['CSRF_SALT'], $_POST['csrf_token']);

            $params = [];
            foreach ($_POST as $key => $value) {
                $params[$key] = trim($value);
            }

            $user->update($params);

            (new Redirector)->to('users');
        } catch(InvalidCsrfTokenException $e) {
            echo $e->getMessage();
        }
    }
}

ルーティングの追加

 updateメソッドを追加しましたのでルーティングも追加しましょう。bootstrap.phpファイルを下記のように編集してください。

<?php

(省略)

$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
    // ここにルーティング定義を追加していきます
    $r->addRoute('GET', '/users', function () {
        return (new UsersController)->index();
    });

    $r->addRoute('GET', '/users/create', function () {
        return (new UsersController)->create();
    });

    $r->addRoute('POST', '/users', function () {
        return (new UsersController)->store();
    });

    $r->addRoute('GET', '/users/{id:\d+}/edit', function ($vars) {
        return (new UsersController)->edit($vars['id']);
    });

    $r->addRoute('POST', '/users/{id:\d+}', function ($vars) {
        return (new UsersController)->update($vars['id']);
    });
});

編集画面へのリンクを作成

一覧画面に編集画面へのリンクをつけましょう。app/resources/views/users/index.phpを下記のように編集して、テーブルに1列増やしてください。

<?php ob_start(); ?>

<table class="table table-bordered mt-5">
    <thead>
        <tr>
            <th>ID</th>
            <th>名前</th>
            <th>メールアドレス</th>
            <th>編集</th>
        </tr>
    </thead>
    <tbody>
        <?php foreach ($users as $user) : ?>
            <tr>
                <td><?= $user->id; ?></td>
                <td><?= $this->h($user->name); ?></td>
                <td><?= $this->h($user->email); ?></td>
                <td><a href="/users/<?= $user->id; ?>/edit" class="btn btn-primary">編集</a></td>
            </tr>
        <?php endforeach; ?>
    </tbody>
</table>

<?php $content = ob_get_clean(); ?>
編集リンクの追加

 編集できたら、一覧画面(/users)にアクセスして編集画面へのリンクをクリックしてください。データが更新できることを確認してください。

編集画面

ユーザー削除処理の作成

Userモデルにdestroyメソッドを追加

User.phpを下記のように編集し、destroyメソッドを追加してください。

<?php

namespace App\Models;

use App\Utilities\DatabaseConnector;
use PDO;

class User
{
    (省略)

    /**
     * 更新処理
     *
     * @param array $params
     * @return boolean
     */
    public function update(array $params): bool
    {
        $dbh = DatabaseConnector::connect();
        $sql = 'UPDATE users SET name = :name, email = :email, updated_at = now() WHERE id = :id';
        $stmt = $dbh->prepare($sql);
        $stmt->bindValue(':name', $params['name'], PDO::PARAM_STR);
        $stmt->bindValue(':email', $params['email'], PDO::PARAM_STR);
        $stmt->bindValue(':id', $this->id, PDO::PARAM_INT);

        return $stmt->execute();
    }

    /**
     * 削除処理
     *
     * @return boolean
     */
    public function destroy(): bool
    {
        $dbh = DatabaseConnector::connect();
        $sql = 'DELETE FROM users WHERE id = :id';
        $stmt = $dbh->prepare($sql);
        $stmt->bindValue(':id', $this->id, PDO::PARAM_INT);
        
        return $stmt->execute();
    }
}

UsersControllerにdestroyメソッドを追加

 UsersController.phpを下記のように編集し、destroyメソッドを追加してください。
 CSRFトークンをチェックしています。削除に成功したら一覧画面にリダイレクトします。

<?php

namespace App\Controllers;

use App\Models\User;
use App\Utilities\Redirector;
use App\Utilities\View;
use EasyCSRF\EasyCSRF;
use EasyCSRF\Exceptions\InvalidCsrfTokenException;
use EasyCSRF\NativeSessionProvider;

class UsersController
{
    (省略)

    /**
     * 更新処理
     *
     * @param integer $id
     * @return void
     */
    public function update(int $id): void
    {
        $user = User::find($id);

        try {
            $sessionProvider = new NativeSessionProvider();
            $easyCSRF = new EasyCSRF($sessionProvider);
            $easyCSRF->check($_ENV['CSRF_SALT'], $_POST['csrf_token']);

            $params = [];
            foreach ($_POST as $key => $value) {
                $params[$key] = trim($value);
            }

            $user->update($params);

            (new Redirector)->to('users');
        } catch(InvalidCsrfTokenException $e) {
            echo $e->getMessage();
        }
    }

    /**
     * 削除処理
     *
     * @param integer $id
     * @return void
     */
    public function destroy(int $id): void
    {
        $user = User::find($id);

        try {
            $sessionProvider = new NativeSessionProvider();
            $easyCSRF = new EasyCSRF($sessionProvider);
            $easyCSRF->check($_ENV['CSRF_SALT'], $_POST['csrf_token']);

            $user->destroy();

            (new Redirector)->to('users');
        } catch(InvalidCsrfTokenException $e) {
            echo $e->getMessage();
        }
    }
}

ルーティングの追加

 destroyメソッドを追加しましたのでルーティングも追加しましょう。bootstrap.phpファイルを下記のように編集してください。

<?php

(省略)

$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
    // ここにルーティング定義を追加していきます
    $r->addRoute('GET', '/users', function () {
        return (new UsersController)->index();
    });

    $r->addRoute('GET', '/users/create', function () {
        return (new UsersController)->create();
    });

    $r->addRoute('POST', '/users', function () {
        return (new UsersController)->store();
    });

    $r->addRoute('GET', '/users/{id:\d+}/edit', function ($vars) {
        return (new UsersController)->edit($vars['id']);
    });

    $r->addRoute('POST', '/users/{id:\d+}', function ($vars) {
        return (new UsersController)->update($vars['id']);
    });

    $r->addRoute('POST', '/users/{id:\d+}/delete', function ($vars) {
        return (new UsersController)->destroy($vars['id']);
    });
});

削除フォームの作成

 フォームに埋め込むCSRFトークンを生成しておきます。UsersControllerのindexメソッドを下記のように編集してください。

    /**
     * 一覧表示
     *
     * @return void
     */
    public function index(): void
    {
        $users = User::all();

        $sessionProvider = new NativeSessionProvider();
        $easyCSRF = new EasyCSRF($sessionProvider);
        $csrfToken = $easyCSRF->generate($_ENV['CSRF_SALT']);

        (new View)->render('users/index.php', compact('users', 'csrfToken'), ['title' => '一覧表示 | ユーザー管理']);
    }

 一覧画面に削除フォームを作成します。app/resources/views/users/index.phpを下記のように編集して、テーブルに1列増やしてください。

<?php ob_start(); ?>

<table class="table table-bordered mt-5">
    <thead>
        <tr>
            <th>ID</th>
            <th>名前</th>
            <th>メールアドレス</th>
            <th>編集</th>
            <th>削除</th>
        </tr>
    </thead>
    <tbody>
        <?php foreach ($users as $user) : ?>
            <tr>
                <td><?= $user->id; ?></td>
                <td><?= $this->h($user->name); ?></td>
                <td><?= $this->h($user->email); ?></td>
                <td><a href="/users/<?= $user->id; ?>/edit" class="btn btn-primary">編集</a></td>
                <td>
                    <form action="/users/<?= $user->id; ?>/delete" method="POST">
                        <input type="hidden" name="csrf_token" value="<?= $csrfToken; ?>">
                        <button class="btn btn-danger">削除</button>
                    </form>
                </td>
            </tr>
        <?php endforeach; ?>
    </tbody>
</table>

<?php $content = ob_get_clean(); ?>
削除フォームの追加

 編集できたら、一覧画面(/users)にアクセスして削除ボタンをクリックしてください。データが削除できることを確認してください。

ユーザー詳細参照機能の作成

UsersControllerにshowメソッドを追加

 UsersController.phpを下記のように編集し、showメソッドを追加してください。

<?php

(省略)

class UsersController
{
    (省略)

    /**
     * 登録処理
     *
     * @return void
     */
    public function store(): void
    {
        try {
            $sessionProvider = new NativeSessionProvider();
            $easyCSRF = new EasyCSRF($sessionProvider);
            $easyCSRF->check($_ENV['CSRF_SALT'], $_POST['csrf_token']);

            $params = [];
            foreach ($_POST as $key => $value) {
                $params[$key] = trim($value);
            }

            User::create($params);

            (new Redirector)->to('users');
        } catch(InvalidCsrfTokenException $e) {
            echo $e->getMessage();
        }
    }

    /**
     * 詳細参照
     *
     * @param integer $id
     * @return void
     */
    public function show(int $id): void
    {
        $user = User::find($id);

        (new View)->render('users/show.php', compact('user'), ['title' => '詳細 | ユーザー管理']);
    }

    /**
     * 編集フォームを表示
     *
     * @param integer $id
     * @return void
     */
    public function edit(int $id): void
    {
        $user = User::find($id);

        $sessionProvider = new NativeSessionProvider();
        $easyCSRF = new EasyCSRF($sessionProvider);
        $csrfToken = $easyCSRF->generate($_ENV['CSRF_SALT']);

        (new View)->render('users/edit.php', compact('user', 'csrfToken'), ['title' => '編集 | ユーザー管理']);
    }

    (省略)
}

ルーティングの追加

 showメソッドを追加しましたのでルーティングも追加しましょう。bootstrap.phpファイルを下記のように編集してください。

<?php

(省略)

$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
    // ここにルーティング定義を追加していきます
    $r->addRoute('GET', '/users', function () {
        return (new UsersController)->index();
    });

    $r->addRoute('GET', '/users/create', function () {
        return (new UsersController)->create();
    });

    $r->addRoute('POST', '/users', function () {
        return (new UsersController)->store();
    });

    $r->addRoute('GET', '/users/{id:\d+}', function ($vars) {
        return (new UsersController)->show($vars['id']);
    });

    $r->addRoute('GET', '/users/{id:\d+}/edit', function ($vars) {
        return (new UsersController)->edit($vars['id']);
    });

    $r->addRoute('POST', '/users/{id:\d+}', function ($vars) {
        return (new UsersController)->update($vars['id']);
    });

    $r->addRoute('POST', '/users/{id:\d+}/delete', function ($vars) {
        return (new UsersController)->destroy($vars['id']);
    });
});

詳細画面の作成

下記のコマンドを実行してshow.phpを作成してください。

touch resources/views/users/show.php

 show.phpを下記のように編集してください。ユーザーの入力値は必ずXSS対策をしてください。

<?php ob_start(); ?>

<p class="mt-5">ID: <?= $user->id; ?></p>
<p>名前: <?= $this->h($user->name); ?></p>
<p>メールアドレス: <?= $this->h($user->email); ?></p>
<p>作成日時: <?= $user->created_at; ?></p>
<p>更新日時: <?= $user->updated_at; ?></p>

<?php $content = ob_get_clean(); ?>

詳細画面へのリンクを追加

 一覧画面に詳細画面へのリンクをつけましょう。app/resources/views/users/index.phpを下記のように編集して、名前をリンカブルにします。

<?php ob_start(); ?>

<table class="table table-bordered mt-5">
    <thead>
        <tr>
            <th>ID</th>
            <th>名前</th>
            <th>メールアドレス</th>
            <th>編集</th>
            <th>削除</th>
        </tr>
    </thead>
    <tbody>
        <?php foreach ($users as $user) : ?>
            <tr>
                <td><?= $user->id; ?></td>
                <td><a href="/users/<?= $user->id; ?>"><?= $this->h($user->name); ?></a></td>
                <td><?= $this->h($user->email); ?></td>
                <td><a href="/users/<?= $user->id; ?>/edit" class="btn btn-primary">編集</a></td>
                <td>
                    <form action="/users/<?= $user->id; ?>/delete" method="POST">
                        <input type="hidden" name="csrf_token" value="<?= $csrfToken; ?>">
                        <button class="btn btn-danger">削除</button>
                    </form>
                </td>
            </tr>
        <?php endforeach; ?>
    </tbody>
</table>

<?php $content = ob_get_clean(); ?>
詳細画面リンク

 編集できたら、一覧画面(/users)にアクセスして詳細画面へのリンクをクリックしてください。全てのカラムデータが参照できることを確認してください。

詳細画面

 以上でCRUD操作は完成です。次回はシステムをもう少し使いやすくするために改良作業をします。おつかれさまでした。

PHP/Laravelのシステム開発は株式会社パパグラムへぜひご相談ください。

いいなと思ったら応援しよう!