進行中のプロジェクトに関する概要はこちら
開発環境
- CentOS 7
- Apache 2.4
- MySQL 8.0
- PHP 7.4
今回の作業
- 投稿を削除する機能を追加
- 同じ処理をまとめる
- ペジネーション実装
- いくつかの処理を関数に分割
投稿を削除する機能を追加
現状( index.php )では、
投稿データをSELECT
してくる処理も
テキストをINSERT
する処理も
全て1つのファイルのみで行っておりますが、
何でもかんでも同じファイルに書いてしまうと
だんだんコードが読みにくくなってくるため、
投稿と削除について
それぞれPOSTの送り先を変えて
別のファイルで処理を行います。
<!-- 投稿用のフォーム -->
<div id="form">
<!-- insert.php にPOST通信で text を送る -->
<form action="insert.php" method="POST">
<textarea name="text" id="" cols="50" rows="5"></textarea>
<input type="submit" value="投稿">
</form>
</div>
<!-- 削除用のフォーム -->
<?php foreach($posts as $post) : ?>
<tr>
<td><?php echo $post['id']; ?></td>
<td><?php echo nl2br(htmlspecialchars($post['post'])); ?></td>
<td>
<!-- delete.php にPOST通信で $post['id'] を送る -->
<form action="delete.php" method="POST">
<input type="hidden" name="id" value="<?php echo $post['id']; ?>">
<input type="submit" value="削除">
</form>
</td>
</tr>
<?php endforeach; ?>
そして index.php
に書いていた投稿と削除の処理は
フォームのaction
で指定したそれぞれのファイル内で行います。
ここでPOST通信かどうかの判定も行います。
<?php
$dsn = 'mysql:dbname=bbs;host=localhost';
$user = 'root';
$pass = '';
if($_SERVER['REQUEST_METHOD'] === 'POST') {
$dbh = new PDO($dsn, $user, $pass);
$text = $_POST['text'];
$stmt = $dbh->prepare('INSERT INTO posts (post) VALUES (:post)');
$stmt->bindParam(':post', $text, PDO::PARAM_STR);
$stmt->execute();
}
header('Location: index.php');
<?php
$dsn = 'mysql:dbname=bbs;host=localhost';
$user = 'root';
$pass = '';
if($_SERVER['REQUEST_METHOD'] === 'POST') {
$dbh = new PDO($dsn, $user, $pass);
$id = $_POST['id'];
$stmt = $dbh->prepare('DELETE FROM posts WHERE id=:id');
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
$stmt->execute();
}
header('Location: index.php');
この時点では
header()
によるリダイレクト終了後に
exit;
が記述されておりませんが、
後々修正が入ります。
同じ処理をまとめる
ここで、
複数のファイルで
全く同じ処理が行われている部分があることに
気がつきました。
データベース接続のところですね。
connect.php
というファイルに
まとめてしまいましょう。
$dsn = 'mysql:dbname=bbs;host=localhost';
$user = 'root';
$pass = '';
$dbh = new PDO($dsn, $user, $pass);
これに伴い、
他のファイルでは
connect.php
を include するように
書き換えていきます。
include 'connect.php';
$stmt = $dbh->query('SELECT * FROM posts ORDER BY id DESC');
$posts = $stmt->fetchAll();
他のファイルの書き換えについては割愛いたします。
ペジネーション実装
参考記事 - ページネーション(ページ切り替え)の実装方法いろいろ
まずはCOUNT
を使用してposts
テーブルのレコード数を取得し、
これを1ページあたりの投稿表示数で割って端数は切り上げます。
(このプロジェクトでは1ページあたり3つの投稿を表示します)
$stmt = $dbh->query('SELECT COUNT(*) FROM posts');
$count = $stmt->fetchColumn();
$pages = ceil($count / 3);
ちなみにこの記事を書いてる時点で
マジックナンバーを使っていることに気づいたので
修正しておきます。
これで必要なページ数($pages
)が取得できました。
このページ数を元に
ページリンク部分のHTML
を書きます。
<div id="pagination">
<?php for($i = 1; $i <= $pages; $i++) : ?>
<a href="?page=<?php echo $i; ?>"><?php echo $i; ?></a>
<?php endfor; ?>
</div>
今何ページ目にいるかは
GET
で取得します。
ページが指定されていない場合のデフォルト値は
null
にしておきます。
$page = $_GET['page'] ?? null;
取得したページ番号が
null
かそうでないかで分岐させて
投稿内容を取得してきます。
if($page === null) {
$stmt = $dbh->query('SELECT * FROM posts ORDER BY id DESC LIMIT 3');
$posts = $stmt->fetchAll();
} else {
$start = ($page - 1) * 3;
$stmt = $dbh->prepare('SELECT * FROM posts ORDER BY id DESC LIMIT :start, 3');
$stmt->bindValue(':start', $start, PDO::PARAM_INT);
$stmt->execute();
$posts = $stmt->fetchAll();
}
$start = ($page - 1) * 3;
1ページ目のときは1〜3番目の投稿、
2ページ目のときは4〜6番目の投稿を取ってくるので、
その初期位置をstart
と定義します。
完成したのがこちらです。
index.php
いくつかの処理を関数に分割
現状では全ての処理をベタ書きしているため
パッと見で何をやっているか分かりづらいです。
そこで思いついたところから
どんどん関数にまとめていくことで、
処理のまとまりに名前をつけて
分かりやすいコードにしていこうと思います。
これらの関数は
現時点では特に分類はせず
functions.php
に羅列していきます。
まず、先ほど分けたデータベース接続処理を
関数化してみましょう。
/**
* データベースに接続
*
* @param string $dsn
* @param string $user
* @param string $pass
* @return PDO
*/
function connect(string $dsn, string $user, string $pass): PDO
{
return $dbh = new PDO($dsn, $user, $pass);
}
return
の後に変数dbh
を定義していますが、
ただPDO
を返せばよいだけなのでこの記述は不要でした。
後々修正が入ります。
また、この時点ではデータベース情報を引数に取っていますが、
結局同じデータベースしか使わないので、
function connect(): PDO
{
return new PDO(
'mysql:dbname=bbs;host=localhost',
'root',
''
);
}
後々このようになります。
なお、元々この部分が記述されていた
connect.php
は不要になりましたので
削除いたします。
続いて投稿の操作に関わる4つの処理部分について
関数化してしまいます。
/**
* フォームに入力された内容をテーブルに挿入
*
* @param PDO $dbh
* @param string $text
*/
function insert(PDO $dbh, string $text): void
{
$stmt = $dbh->prepare('INSERT INTO posts (post) VALUES (:post)');
$stmt->bindParam(':post', $text, PDO::PARAM_STR);
$stmt->execute();
}
/**
* レコードを削除
*
* @param PDO $dbh
* @param int $id
*/
function delete(PDO $dbh, int $id): void
{
$stmt = $dbh->prepare('DELETE FROM posts WHERE id=:id');
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
$stmt->execute();
}
/**
* テーブルの内容を出力
*
* @param PDO $dbh
* @param int|null $page
* @return array
*/
function select(PDO $dbh, ?int $page): array
{
if($page === null) {
$stmt = $dbh->query('SELECT * FROM posts ORDER BY id DESC LIMIT 3');
return $posts = $stmt->fetchAll();
} else {
$start = ($page - 1) * 3;
$stmt = $dbh->prepare('SELECT * FROM posts ORDER BY id DESC LIMIT :start, 3');
$stmt->bindParam(':start', $start, PDO::PARAM_INT);
$stmt->execute();
return $posts = $stmt->fetchAll();
}
}
/**
* ページ総数のカウント
*
* @param PDO $dbh
* @return int
*/
function countPages(PDO $dbh): int
{
$stmt = $dbh->query('SELECT COUNT(*) FROM posts');
$count = $stmt->fetchColumn();
return $pages = ceil($count / 3);
}
こちらもreturn
の行で変数を定義してしまっておりますが
不要な記述でしたので後々修正が入ります。
この記事はあくまでコミットログの振り返りなので、
こういったことが度々あります。ご了承ください。
ところで、
insert.php
や delete.php
は
- データベースに接続する
- POST通信かどうか判定する
- 投稿を操作する
- リダイレクトする
という流れで進んでいきますが、
これらを全て関数に突っ込んでしまうと
何を行う処理なのかが分かりにくいため
「投稿を操作する」部分だけを関数化しています。
以上の関数を用いて書き換えたのがこちらです
github.com/sait0928/bbs
今回の振り返りはここまでとなります。
あとがき
自分で書いたコードを
ちゃんと説明できるか確認するのも
理解が深まる作業になって楽しいですね。
私の振り返り用(というか就活用)で投稿していて、
ある程度分かっていること前提で
話を進めてしまっているところがあるので、
そのうち細かいところにフォーカスして解説する記事も
後から足していけたらなあ、と思っていたりします。
実現できなかったらすみません。笑