Portfolio作成 ⑤ 記事投稿機能実装編

目標
① 記事一覧をMySQL化
② 記事詳細をMySQL化
① 記事一覧をMySQL化
ここで一度整理すると、
現在
データファイル(data/posts.php) へ「記事タイトル」と「記事本文」を書き込み、
<?php
$posts = [
[
// 記事のID番号
'id' => 1,
// 記事タイトル
'title' => '最初の記事',
// 記事本文
'content' => 'CMS開発開始'
],
[
// 記事のID番号
'id' => 2,
// 記事タイトル
'title' => 'PHP学習',
// 記事本文
'content' => '配列を使ってみる'
]
];
記事一覧画面(index.php) にてinclude 'data/posts.php';で読み込み、リンク付き記事タイトルとして一覧で表示しています。
<?php
include 'data/posts.php';
include 'header.php';
?>
<h2>記事一覧</h2>
<?php foreach($posts as $post): ?>
<h3>
<a href="post.php?id=<?php echo $post['id']; ?>">
<?php echo $post['title']; ?>
</a>
</h3>
<?php endforeach; ?>
<?php include 'footer.php'; ?>

修正後
「index.php にてinclude 'data/posts.php';で読み込み」の部分を、
「MySQL へ SELECTコマンドで必要情報を呼び出し、記事一覧へ表示」という形式へと変更します。
ここまでの作業が「記事一覧をMySQL化」の目標です。
修正方法
まずは、index.phpを編集しに行きましょう。
/独自ドメイン/public_html/portfolio/index.php
へ移動。
改めてindex.phpの中身はこうなっています。
<?php
include 'data/posts.php';
include 'header.php';
?>
<h2>記事一覧</h2>
// $posts に格納されている投稿データを1件ずつ取り出して処理する
// 1回目のループでは最初の投稿、2回目は次の投稿…というように繰り返される
// 現在処理中の投稿データは $post に代入される
<?php foreach($posts as $post): ?>
<h3>
<!--
投稿詳細ページ(post.php)へのリンクを作成する
?id= の後ろに現在の投稿IDを付与しているため、
クリックすると該当する投稿の詳細ページを表示できる
例:
$post['id'] が 2 の場合
↓
<a href="post.php?id=2">
-->
<a href="post.php?id=<?php echo $post['id']; ?>">
// 現在処理中の投稿データ($post)から
// titleカラムの値(投稿タイトル)を取得して画面に表示する
<?php echo $post['title']; ?>
</a>
</h3>
<?php endforeach; ?>
<?php include 'footer.php'; ?>
include 'data/posts.php'; の部分を削除して、同じ個所に下記コードを書き込んでいきます。
<?php
// config配下の「database.php」を読み込む<データベース接続設定ファイル>
include 'config/database.php';
// 実行するSQL文を定義
// postsテーブルの全てのカラム(*)を取得し、
// idの降順(大きいID=新しい投稿が先)で並べる
$sql = "
SELECT *
FROM posts
ORDER BY id DESC
";
// SQLを実行する
// query()は結果セットを返し、PDOStatementオブジェクトが$stmtに格納される
$stmt = $pdo->query($sql);
// SQLの実行結果を全件取得する
// FETCH_ASSOCを指定することで、
// カラム名をキーとした連想配列として取得する
$posts = $stmt->fetchAll(PDO::FETCH_ASSOC);
include 'header.php';
?>
database.php ($pdoのつながり確認)
<?php
// データベースサーバー名
// XServerでは通常 localhost
$host = 'localhost';
// 接続するデータベース名
$dbname = 'ここにMySQLデータベース名';
// MySQLユーザー名
$username = 'ここにMySQLユーザー名';
// MySQLパスワード
$password = 'ここにMySQLパスワード';
try {
// PDOオブジェクト作成
// PHPとMySQLを接続するための窓口
$pdo = new PDO(
// 接続先情報
"mysql:host=$host;dbname=$dbname;charset=utf8mb4",
// ユーザー名
$username,
// パスワード
$password
);
// エラー発生時に例外を投げる設定
// 開発中は必須
$pdo->setAttribute(
PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION
);
} catch (PDOException $e) {
// 接続失敗時の処理
die(
'データベース接続失敗: ' .
$e->getMessage()
);
}
一旦、index.phpの修正後確認ともう一点修正として、XSS対策の
htmlspecialchars()
の追加。
<?php
include 'config/database.php';
$sql = "
SELECT *
FROM posts
ORDER BY id DESC
";
$stmt = $pdo->query($sql);
$posts = $stmt->fetchAll(PDO::FETCH_ASSOC);
include 'header.php';
?>
<h2>記事一覧</h2>
<?php foreach($posts as $post): ?>
<h3>
<a href="post.php?id=<?php echo $post['id']; ?>">
<?php echo htmlspecialchars($post['title']); ?>
</a>
</h3>
<?php endforeach; ?>
<?php include 'footer.php'; ?>
動作確認 記事一覧画面(index.php)
https://kimimachilab.com/portfolio/
へアクセスして、ログイン。
Portfolio作成 ③ 管理画面編 で新規投稿画面(new-post.php)を作成し、
Portfolio作成 ④ DB接続編 で
タイトル「CMS開発日記」
本文「MySQLへの保存に成功」
を投稿しているため、既にMySQLのpostsテーブルへと保存されているはずですが・・・。

よし!きちんと目標としていた「MySQL へ SELECTコマンドで必要情報を呼び出し、記事一覧へ表示」が達成できました!
② 記事詳細をMySQL化
記事一覧画面(index.php)のMySQL化に続いて、記事詳細画面(post.php)をMySQL化していきます。
では、現状確認から
現在
データファイル(data/posts.php)を読み込み、「記事のID番号」を取得(idが指定されていなければnullを代入)。
配列データから合致したIDの投稿データを探し、保存。
HTMLに合わせ、「記事タイトル」と「記事本文」を表示。
<?php
// データファイル(data/posts.php)を読み込む
// このファイル内で $posts 配列が利用できるようになる
include 'data/posts.php';
// 「記事のID番号」を取得する
// 例: post.php?id=2 → $id に 2 が入る
// idが指定されていない場合は null を代入する
$id = $_GET['id'] ?? null;
// 指定されたIDの投稿を格納するための変数
// 最初は見つかっていないので null
$targetPost = null;
// $posts 配列の投稿データを1件ずつ取り出して処理する
foreach ($posts as $post) {
// 現在の投稿IDとURLから取得したIDが一致するか確認
if ($post['id'] == $id) {
// 一致した投稿データを保存
$targetPost = $post;
// 目的の投稿が見つかったのでループを終了
break;
}
}
?>
<?php
// 共通ヘッダーを読み込む
include 'header.php';
?>
<!-- 「記事タイトル」を表示 -->
<h2><?php echo $targetPost['title']; ?></h2>
<!-- 「記事本文」を表示 -->
<p>
<?php echo $targetPost['content']; ?>
</p>
<?php
// 共通フッターを読み込む
include 'footer.php';
?>

修正後
「データファイル(data/posts.php)を読み込み」の部分を、
「index.phpと同じくconfig配下のデータベース接続設定ファイル(database.php)を読み込む」へ変更。
「配列データから合致したIDの投稿データを探し、保存」の部分を、
MySQL へ SELECTコマンドで呼び出し、WHEREコマンドで必要情報を絞り込む。
記事一覧画面(index.php)の時は失念していた、XSS対策やSQLインジェクション対策も修正。
<?php
// データベース接続ファイルを読み込む
// このファイル内で $pdo(PDOオブジェクト)が作成されている
include 'config/database.php';
// URLパラメータから記事IDを取得する
// 例: post.php?id=3 → $id に 3 が入る
// idが指定されていない場合は null を代入
$id = $_GET['id'] ?? null;
// 指定されたIDの記事を取得するSQL文
// postsテーブルの全てのカラム(*)を取得し、
// :id はプレースホルダ(後で値を埋め込むための目印)
$sql = "
SELECT *
FROM posts
WHERE id = :id
";
// SQLを準備する
// prepare()を使うことでSQLインジェクション対策になる
$stmt = $pdo->prepare($sql);
// プレースホルダ :id に実際の値をセットしてSQLを実行
$stmt->execute([
':id' => $id
]);
// 検索結果を1件取得する
// FETCH_ASSOC を指定して連想配列として受け取る
$post = $stmt->fetch(PDO::FETCH_ASSOC);
// 共通ヘッダーを読み込む
include 'header.php';
?>
<?php if ($post): ?>
<!-- 記事が存在する場合 -->
<h2>
<?php
// 記事タイトルを表示
// htmlspecialchars()でHTMLタグを無効化しXSSを防ぐ
echo htmlspecialchars($post['title']);
?>
</h2>
<p>
<?php
// 記事本文を表示
// htmlspecialchars() → HTMLタグを無効化
// nl2br() → 改行コードを <br> タグに変換
echo nl2br(htmlspecialchars($post['content']));
?>
</p>
<?php else: ?>
<!-- 指定されたIDの記事が見つからない場合 -->
<p>記事が見つかりません。</p>
<?php endif; ?>
<?php
// 共通フッターを読み込む
include 'footer.php';
?>
動作確認 記事詳細画面(post.php)
https://kimimachilab.com/portfolio/
へアクセスして、ログイン。
動作確認 記事一覧画面(index.php)で確認した画面から、
記事タイトルである「CMS開発日記」のリンクを押す。

指定しているHTML通りなら
- ヘッター
- 記事タイトル「CMS開発日記」
- 記事本文「MySQLへの保存に成功」
- フッター
の構成で表示されるはずですが・・・

よし!こちらも想定通りの表示を確認できました!
残作業
今回の作業によって不要となったフォルダ・ファイルを削除しておきましょう。
今回不要となるフォルダ・ファイルは
- data
- posts.php
です。
/独自ドメイン/public_html/portfolio/data/posts.php
posts.phpはdata配下に配置されている構成となっているので、dataフォルダを削除しましょう。
dataフォルダを指定して、

「削除」ボタンを押す。

「完全に削除」ボタンを押す。

削除完了です。
最後に
ここまで読んでいただき、ありがとうございます。
新たな試みとして、コメントアウトの解説文をより詳しく、見分けやすいようにカラーを付けてみました。
読み解きずらくなったよ。や 見分けずらくなったよ。などございましたら、お気軽にコメントをください。
キミマチとしては、その逆のコメントが送っていただけたら非常に嬉しいですし、いただけるように精進いたします。
