1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

よくある掲示板を1から積み上げて作る〜関数分割〜

Last updated at Posted at 2020-12-10

進行中のプロジェクトに関する概要はこちら

開発環境

  • CentOS 7
  • Apache 2.4
  • MySQL 8.0
  • PHP 7.4

今回の作業

  • 投稿を削除する機能を追加
  • 同じ処理をまとめる
  • ペジネーション実装
  • いくつかの処理を関数に分割

投稿を削除する機能を追加

現状( index.php )では、
投稿データをSELECTしてくる処理も
テキストをINSERTする処理も
全て1つのファイルのみで行っておりますが、
何でもかんでも同じファイルに書いてしまうと
だんだんコードが読みにくくなってくるため、
投稿と削除について
それぞれPOSTの送り先を変えて
別のファイルで処理を行います。

index.php
<!-- 投稿用のフォーム -->

<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通信かどうかの判定も行います。

insert.php
<?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');
delete.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というファイルに
まとめてしまいましょう。

connect.php
$dsn = 'mysql:dbname=bbs;host=localhost';
$user = 'root';
$pass = '';

$dbh = new PDO($dsn, $user, $pass);

これに伴い、
他のファイルでは
connect.phpinclude するように
書き換えていきます。

index.php
include 'connect.php';

$stmt = $dbh->query('SELECT * FROM posts ORDER BY id DESC');
$posts = $stmt->fetchAll();

他のファイルの書き換えについては割愛いたします。

ペジネーション実装

参考記事 - ページネーション(ページ切り替え)の実装方法いろいろ

まずはCOUNTを使用してpostsテーブルのレコード数を取得し、
これを1ページあたりの投稿表示数で割って端数は切り上げます。
(このプロジェクトでは1ページあたり3つの投稿を表示します)

index.php
$stmt = $dbh->query('SELECT COUNT(*) FROM posts');
$count = $stmt->fetchColumn();
$pages = ceil($count / 3);

ちなみにこの記事を書いてる時点で
マジックナンバーを使っていることに気づいたので
修正しておきます。

これで必要なページ数($pages)が取得できました。
このページ数を元に
ページリンク部分のHTMLを書きます。

index.php
<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にしておきます。

index.php
$page = $_GET['page'] ?? null;

取得したページ番号が
nullかそうでないかで分岐させて
投稿内容を取得してきます。

index.php
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に羅列していきます。

まず、先ほど分けたデータベース接続処理を
関数化してみましょう。

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を返せばよいだけなのでこの記述は不要でした。
後々修正が入ります。

また、この時点ではデータベース情報を引数に取っていますが、
結局同じデータベースしか使わないので、

functions.php
function connect(): PDO
{
	return new PDO(
		'mysql:dbname=bbs;host=localhost',
		'root',
		''
	);
}

後々このようになります。

なお、元々この部分が記述されていた
connect.phpは不要になりましたので
削除いたします。

続いて投稿の操作に関わる4つの処理部分について
関数化してしまいます。

functions.php
/**
 * フォームに入力された内容をテーブルに挿入
 *
 * @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.phpdelete.php

  • データベースに接続する
  • POST通信かどうか判定する
  • 投稿を操作する
  • リダイレクトする

という流れで進んでいきますが、
これらを全て関数に突っ込んでしまうと
何を行う処理なのかが分かりにくいため
「投稿を操作する」部分だけを関数化しています。

以上の関数を用いて書き換えたのがこちらです
github.com/sait0928/bbs
今回の振り返りはここまでとなります。

あとがき

自分で書いたコードを
ちゃんと説明できるか確認するのも
理解が深まる作業になって楽しいですね。

私の振り返り用(というか就活用)で投稿していて、
ある程度分かっていること前提で
話を進めてしまっているところがあるので、
そのうち細かいところにフォーカスして解説する記事も
後から足していけたらなあ、と思っていたりします。
実現できなかったらすみません。笑

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?