Developer

魁!小野の塾 PHPで簡単なプログラムを作ってみようの巻 第9話
2021.01.01
Lv2

魁!小野の塾 PHPで簡単なプログラムを作ってみようの巻 第9話

PHPで簡単なプログラムを作ってみよう 第9話

初心者向け、PHPプログラム構築講座です。
初心者といっても、PHPの勉強を少し行い、LAMP環境が自分で構築できるレベルを対象とします。
まったくの初心者の場合は、わからない部分が出てくると思います。
できるだけ細かく説明は入れていきますが、説明がわからない場合は、PHPやMySQLの初心者講座をご覧ください。

対象のスキルレベル

  • LAMP環境の構築
  • PHP言語が読める
  • HTML, CSS, Javascriptが少しわかる
  • Bootstrapのドキュメントをみて、HTMLが書ける
  • CDNを理解している

構築環境

  • Windows10
  • XAMPP(PHP7.3.2, MariaDB 10.1.38)

削除機能の実装

最後に削除機能を実装していきます。
action で指定している登録部分に、削除のロジックを追加していきます。

削除機能追加(抜粋)
<?php
	// db resource
	$db	= new mysqli('localhost', 'root', '', 'pjin');
	$db->set_charset("utf8");

	// $_POST -> $request
	$request	= [
		'author'	=> $_POST['author'] ?? '',
		'content'	=> $_POST['content'] ?? '',
		'action'	=> $_POST['action'] ?? ''
	];

	// create or delete
	$errors	= [];
	switch($request['action']){
	case 'create':
		// error check
		if(empty($request['author'])){
			$errors['author'][]	= '名前を入力して下さい。';
		}
		if(mb_strlen($request['author']) > 128){
			$errors['author'][]	= '名前は128文字以内で入力して下さい。';
		}
		if(empty($request['content'])){
			$errors['content'][]	= '内容を入力して下さい。';
		}
		if(mb_strlen($request['content']) > 65535){
			$errors['content'][]	= '名前は65,535文字以内で入力して下さい。';
		}

		if(count($errors) > 0){
			// エラーがあれば終了
			break;
		}

		// 登録処理
		$sql	= "insert into board(author, content) values (?, ?)";
		if($stmt = $db->prepare($sql)){
			$stmt->bind_param("ss", $request['author'], $request['content']);
			$stmt->execute();
			$stmt->close();
			// 登録後に空にします。
			$request	= [
				'author'	=> '',
				'content'	=> ''
			];
		}
		break;

	case 'delete';
		// 削除処理
		$sql	= "delete from board where id = ?";
		if($stmt = $db->prepare($sql)){
			$stmt->bind_param("i", $_POST['id']);
			$stmt->execute();
			$stmt->close();
		}
		break;
	}

	// 検索処理
	$rs	= [];
	$sql	= "select * from board order by id desc";
	if($stmt = $db->prepare($sql)){
		$stmt->execute();
		$stmt->bind_result($id, $createdAt, $author, $content);
		while($stmt->fetch()){
			$rs[]	= [
				'id'		=> $id,
				'createdAt'	=> $createdAt,
				'author'	=> $author,
				'content'	=> $content
			];
		}
		$stmt->close();
	}

	// db resource close
	$db->close();
?>

削除するように、boardテーブルの主キーであるidをPOSTで送信し、対応するidの行を削除します。
このプログラムでは、actionがない場合は、検索処理を実施し、actionがある場合は、登録又は削除が行われることを想定しています。
更新がある場合は、このaction部分に追加していくようになります。(※今回は作成しませんが。。)

特に削除する対象をチェックすることは行いません。また、論理削除ではなく、物理削除しています。
論理削除の場合は、is_delete や delete_at などのフラグや日時を入れて制御します。

物理削除の場合は、多重度が上がった場合に、先に他の誰かが削除したとしても、SQLの実行でエラーになることはありません。
多重度を考慮する場合は、更新日時などを参照して、画面に表示した時刻と比較して新しくなっているかを確認し、新しい場合はエラーを出力します。(楽観排他)

例えば、画面出力時刻が、2021/03/23 10:20:03 だったとします。これをHTMLのhiddenで埋めておきます。
更新又は削除の処理を実行した場合に、一緒にサーバへ送付後、対象レコードの更新日時を参照します。
更新日時が、2021/03/23 10:25:28 だった場合は、誰かほかの方が、操作中に更新したことが分かります。
この場合に更新するかはプロジェクトごとに異なりますが、先勝ちの場合はエラーとして処理します。

HTML部分の修正

削除ボタンの周りに、FORM要素を追加し、削除用のパラメータを埋めていきます。

削除機能追加(抜粋)
<?php foreach($rs as $row){ ?>
	<div class="card">
		<div class="card-header">
			<div class="row">
				<div class="col-sm-6">
					<strong><?php echo htmlentities($row['author']) ?></strong>
				</div>
				<div class="col-sm-6 text-right">
					<small><?php echo date('Y-m-d H:i:s ', strtotime($row['createdAt'])) ?></small>
				</div>
			</div>
		</div>
		<div class="card-body">
			<form action="<?php echo $_SERVER['SCRIPT_NAME'] ?>" method="POST">
				<p class="float-right">
					<input type="hidden" name="action" value="delete">
					<input type="hidden" name="id" value="<?php echo htmlentities($row['id']) ?>">
					<button type="submit" class="btn btn-outline-danger btn-sm">
						<i class="far fa-trash-alt"></i> 削除
					</button>
				</p>
			</form>
			<p class="card-text">
				<?php echo nl2br(htmlentities($row['content'])) ?>
			</p>
		</div>
	</div>

	<div class="mt-3"></div>
<?php } ?>

FORM要素を追加し、hiddenを2つ追加します。(action, id)actionは、固定値でdeleteを入れておきます。
idは数字で、特に入力値を入れるわけでもないので、htmlentitiesをつける必要もないのですが、出力する場合は必ずつけると癖をつけると忘れないで済みます。(忘れるとイタイデスカラ)

FORM要素を追加したくない場合は、Javascriptを書く必要があります。その場合はもろもろ変更する必要があったりします。

Javascriptを利用する場合(抜粋)
<form action="<?php echo $_SERVER['SCRIPT_NAME'] ?>" method="POST">
	<input type="hidden" name="action" value="create">
	<input type="hidden" name="id" value="">

[省略]

<div class="card-body">
	<p class="float-right">
		<button class="btn btn-outline-danger btn-sm" data-id="<?php echo htmlentities($row['id']) ?>">
			<i class="far fa-trash-alt"></i> 削除
		</button>
	</p>
	<p class="card-text">
		<?php echo nl2br(htmlentities($row['content'])) ?>
	</p>
</div>

[省略]

<script>
$(function(){
	$('[data-id]').on('click', function(){
		var id = $(this).data('id');
		var $form = $('form:first');
		$form.find('[name=action]').val('delete');
		$form.find('[name=id]').val(id);
		$form.submit();
	});
});
</script>

登録のFORM要素にhiddenを追加。名前はidです。
ボタンを押下した場合に、data-* グローバル属性を定義してあると、情報の取得が簡単に行えます。
scriptの内容ですが、data-id属性が定義されている要素を対象に、クリックした場合、以下を行います。

  1. クリックしたボタンに定義してある、data-idの値を取得(要素.data(‘id’))
  2. FORM要素の最初を取得(登録用フォーム)
  3. actionの値をdeleteに変更
  4. idの値を対象idに変更
  5. FORM要素をサブミットします。

Javascriptで行うか、FORM要素で行うかは趣味の問題なので、どちらでも問題なく動きます。
保守性的には、FORM要素で書いたほうが、初心者の受けは良いです。

最後の小言

ざっくりと進めてまいりましたが、これで完成となります。
内容は色々な情報が入り交じり、難しそうに見えるのですが、作業自体は4時間程度で作成できるかと思います。
変なこだわりがあるとHTMLを触り続けてしまうので、そこは気を付けましょう。(特にアニメーション。。。。)

慣れてくると、色々なCDNを駆使できるようになるので、あとは何度も作ることをお勧めします。
テーブルの項目数やデータ型を増やして、作り直すことで、どこがどのように変更すれば出来るかなどが分かるはずです。

出来るだけ、記事の途中にあるリンクを先をみて、なるべく覚えるようにしてください。
数回記事を読みながら、リンククリックして、各サイトを読むだけで、かなりレベルが上がるかと思います。

実際の業務で利用する画面では、項目数が多いので、エラーチェックも含めて、かなりげんなりすること間違えなしです!
出来るだけ早くなれることをお勧め致します。

今回の簡単な掲示板作成に関しては、これにて終了です。
お疲れ様でした。

魁!小野の塾