データベース 5日目
※1・2限目は一覧表示の続き。
共通で使用する関数
<?php
// HTMLでのエスケープ処理をする関数
function h($var) {
if (is_array($var)) {
return array_map('h', $var);
} else {
return htmlspecialchars($var, ENT_QUOTES, 'UTF-8');
}
}
// デバック用関数
function v($val) {
echo '<pre>';
var_dump($val);
echo '</pre>';
}
データベースの接続処理を関数化する(P146)
// MySQL(MariaDB)に接続する関数
function connectPractice(){
$dbobj = mysqli_connect('localhost', 'Tanaka', 'Manager')
or die('DBに接続できませんでした');
mysqli_select_db($dbobj, 'practice');
// DB領域の選択:practice領域を選択
mysqli_set_charset($dbobj, 'utf8');
// 文字化け対策
return $dbobj;
}
MariaDBに接続する関数
常に同じ設定でDBへ接続するのであれば、DB接続用関数を定義しておくと便利。
mysqli_connect()関数が返す接続情報オブジェクトを$dbobj変数に管理する。
$dbobj = mysqli_connect('localhost', 'Tanaka', 'Manager')
or die('DBに接続できませんでした');
$dbobj変数が管理する接続情報オブジェクトは関数内にあるため、関数外からは参照できない。returnを使って、関数呼び出し部分に返し、関数外で使用できるようにする。
return $dbobj;
各ページのデータベース接続処理部分で関数を呼び出す
MariaDBに接続する関数を呼び出す場合
【変更前】
$dbobj = mysqli_connect('localhost', 'Tanaka', 'Manager')
or die('DBに接続できませんでした');
mysqli_select_db($dbobj, 'practice');
mysqli_set_charset($dbobj, 'utf8');
【変更後】
$dbobj = connectPractice();
connectPractice()関数の返り値である接続情報オブジェクトを関数外で宣言した$dbobjで受け取り関数外のプログラムで引き続き使用できるようにする。
レコードの登録
まずはシンプルなレコード登録ソースコードで動きを確認する。
<?php
require_once __DIR__ . '/functions.php';
$dbobj = connectPractice();
mysqli_query($dbobj, 'INSERT INTO stationery SET item="分度器", price=240,
stock=6, keyword="事務", maker=2') or die(mysqli_error($dbobj));
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>レコードを登録する</title>
</head>
<body>
<div>
新しい商品を追加しました
</div>
</body>
</html>
DBの接続部分はレコードの一覧表示と同じ。実行するSQL文がSELECT文からINSERT文に変わっている。今回はSQL文を実行した結果をPHP内で利用しないため、変数に管理する必要はない。
新規商品入力フォームを作成
新しい商品の情報を入力するフォームを作成する。traderテーブルからメーカー名を取得してラジオボタンにしている点を確認する。
<?php
$debug = true;
// デバック領域の表示(false)非表示(true)
require_once dirname(__FILE__) . '/functions.php';
// 共通関数を呼び出す
$dbobj = connectPractice();
// いつものDB接続処理を関数で実行
// 戻り値の接続許可証を変数$dbobjに代入
$sql = 'SELECT * FROM trader';
$trSet = mysqli_query($dbobj, $sql) or die(mysqli_error($dbobj));
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link href="style.css" type="text/css" rel="stylesheet">
<title>商品管理システム</title>
</head>
<body>
<?php if ($dbobj) : ?>
<div class="debug">
<p>デバッグ用</p>
<p>$sql : <?php print $sql; ?></p>
</div>
<?php endif; ?>
<div id="container">
<div id="head">
<h1>新規登録</h1>
</div>
<div id="content">
<form action="insert.php" method="post">
<fieldset>
<legend>新しい商品の情報</legend>
<dl>
<dt><label for="item">商品名 <span>※必須</span></label></dt>
<dd><input name="item" type="text" id="item" size="20" maxlength="10"></dd>
<dt><label for="price">価格</label></dt>
<dd><input name="price" type="text" id="price" size="10" maxlength="10">円</dd>
<dt><label for="stock">在庫</label></dt>
<dd><input name="stock" type="text" id="stock" size="10" maxlength="10"></dd>
<dt><label for="keyword">キーワード</label></dt>
<dd><input name="keyword" type="text" id="keyword" size="50" maxlength="255"></dd>
</dl>
</fieldset>
<fieldset>
<legend>メーカーの情報</legend>
<dl>
<dt>メーカー<span>※必須</span></dt>
<dd>
<?php while ($trData = mysqli_fetch_assoc($trSet)) : ?>
<label>
<input name="maker" type="radio" value="<?php echo h($trData['m_id']); ?>">
<?php echo h($trData['company']); ?>
</label>
<?php endwhile; ?>
</dd>
</dl>
</fieldset>
<div class="submit_btn"><input type="submit" value="登録"></div>
</form>
<p><a href="index.php" onclick="return confirm('一覧に戻りますか?')">一覧に戻る</a></p>
<!--#content-->
</div>
<!--#container-->
</div>
</body>
</html>
メーカー用ラジオボタン
DBから取得したtraderテーブルのリソースを使用して作成している。
$sql = 'SELECT * FROM trader';
$trSet = mysqli_query($dbobj, $sql) or die(mysqli_error($dbobj));
取得したリソースのm_idフィールドとcompanyフィールドの値を利用してinputタグを作成している。
<?php while ($trData = mysqli_fetch_assoc($trSet)) : ?>
<label>
<input name="maker" type="radio" value="<?php echo h($trData['m_id']); ?>">
<?php echo h($trData['company']); ?>
</label>
<?php endwhile; ?>
上記のPHP記述から以下のHTMLソースコードが生成される。
<label><input name="maker" type="radio" value="1">ペン工房</label>
<label><input name="maker" type="radio" value="2">小鳥文具</label>
<label><input name="maker" type="radio" value="3">黒木屋</label>
確認機能付の戻るボタン
入力フォームから他のページに移動してしまうと入力中の値がクリアされてしまう。ユーザの不用意な操作で入力した値が消えてしまわないようJavaScriptで確認画面を表示させている。
<a href="index.php" onclick="return confirm('一覧に戻りますか?')">一覧に戻る</a>
新規商品の登録
フォームから受け取った値をDBに登録するページを作成する。必須項目の入力値チェックやSQL文作成の手順を確認する。
<?php
$debug = true;
require_once dirname(__FILE__) . '/functions.php';
// POST形式で受け取った値を変数に代入
// 値を受け取った場合は「ユーザ入力値」「value値」を代入
// 値を受け取れない場合は「NULL」を代入
$item = isset($_POST['item']) ? $_POST['item'] : NULL;
$price = isset($_POST['price']) ? $_POST['price'] : NULL;
$stock = isset($_POST['stock']) ? $_POST['stock'] : NULL;
$keyword = isset($_POST['keyword']) ? $_POST['keyword'] : NULL;
$maker = isset($_POST['maker']) ? $_POST['maker'] : NULL;
// 先頭・末尾のホワイトスペースを削除
$item = trim($item);
$price = trim($price);
$stock = trim($stock);
$keyword = trim($keyword);
$maker = trim($maker);
// v($item);
// 値が届かない場合三項演算子でNULLが代入されるが
// trim関数後は空文字になる
// trim関数実行前であれば
// 「NULL」と「空文字」で
// 「値が届かなかった」か「空文字が届いた」かを判定できる。
// trim関数実行後はどちらも空文字になるので判定できない。
// ▲ ▲ここまでが値の準備▲ ▲
// 処理開始
$sql = ''; //SQL文用変数
$message = ''; //HTMLに表示するメッセージ変数
$btn = ''; //リンク用の変数
if ($item == '' or $maker == '') {
// 必須項目が入力・選択されていない時の処理
// NULLでも空文字でもtrueになる
$message = '必須項目を入力してください';
$btn = '<a href="entry.php" onclick="history.back(); return false;">
フォームに戻る</a>';
// ◆JS有効:onclick属性が動く
// history.back()を使って履歴で戻る
// 履歴で戻るとユーザ入力値が維持される
// ※簡易敵なユーザ入力値維持
// ただし、本当はSESSIONが望ましい
// a要素のonclickイベントに「return false;」
// href属性を使ったページ遷移を停止
// ◆JS無効:onclick属性が動かない
// href属性を使ってページ遷移
// ただし、ユーザ入力値は維持されない
} else {
// 必須項目が入力・選択されている時の処理
// DBにレコードを登録
$dbobj = connectPractice();
// DBに接続して接続許可証を変数$dbobjに代入
$item = mysqli_real_escape_string($dbobj, $item);
// (接続許可証, 対象文字列)
$price = mysqli_real_escape_string($dbobj, mb_convert_kana($price, 'n'));
$stock = mysqli_real_escape_string($dbobj, mb_convert_kana($stock, 'n'));
$keyword = mysqli_real_escape_string($dbobj, $keyword);
$maker = mysqli_real_escape_string($dbobj, mb_convert_kana($maker, 'n'));
// mysqli_real_escape_string関数
// ◆セキュリティ用の関数
// SQL文で意味のある記号を無害化してくれる
// 文字列開始・終了を意味するクォートを悪用した攻撃を防ぐ
// ※クォート文字をエスケープする
// DELETE時に詳細解説予定
// SQLインジェクション攻撃に対応する
// 第1引数:接続許可証(DBに意味のある記号を確認する)
// 第2引数:対象文字列(受け取ったユーザ入力値)
// 戻り値:無害化された文字列
// ※関数実行時には接続許可証が必要になるので
// DB接続後に呼び出す
// DB接続前に呼び出すことはできないので注意
// mb_convert_kana関数
// 全角・半角等を変換する関数
// 第1引数:変換対象の値
// 第2引数:変換方法を意味のある文字列で指定する
// 'n':「全角」数字を「半角」に変換
$sql = sprintf(
'INSERT INTO stationery SET
item="%s", price=%d, stock=%d, keyword="%s", maker=%d',
$item,
$price,
$stock,
$keyword,
$maker
);
// sprintf関数(String print formatted)
// 書式を決めて、文字列を整える関数
// 第1引数:フォーマット対象文字列
// 虫食い部分には「%s」や「%d」を記述する
// 「%s」部分には文字列型の値を挿入
// 「%d」部分には整数型の値を挿入
// 「あああ」や「aaa」等の数値に変換できない
// 数値に変換できない値は「0」に変換
// 第2引数以降:フォーマット文字列の虫食い部分に追加する値
// 型を指定してSQL文を作成することで
// SQLインジェクション攻撃を防ぐ
// ユーザ入力値を使ってSQL文を作成する際は
// 必ずsprintf関数を使用する
// ★文字列連結や変数展開を使用しないこと!
mysqli_query($dbobj, $sql) or die(mysqli_error($dbobj));
$message = '新規登録しました';
$btn = '<a href="index.php">一覧に戻る</a>';
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link href="style.css" type="text/css" rel="stylesheet">
<title>商品管理システム</title>
</head>
<body>
<?php if ($debug) : ?>
<div class="debug">
<p>デバッグ用</p>
<pre>$sql : <?php var_dump($_POST); ?></pre>
</div>
<?php endif; ?>
<div id="container">
<div id="head">
<h1>新規登録</h1>
</div>
<div id="content">
<p><?php echo $message; ?></p>
<p><?php echo $btn; ?></p>
<!--#content-->
</div>
<!--#container-->
</div>
</body>
</html>
必須項目の入力チェック
必須項目の商品名とメーカーが入力されているかチェックし処理を分岐させる。
if($item == '' OR $maker =='') {
【必須項目の商品名、メーカーが入力されなかった時の処理】
} else {
【必須項目が入力された時の処理】
}
必須項目が入力されなかった時
$messageに必須項目入力を促すメッセージを、$btnに入力フォームに戻るためのリンクを格納し、body要素の中で表示させる。
JavaScriptで元のページに戻る
フォームに戻るためのリンクにはhref属性とonclick属性を指定する。href属性で指定したリンク先はJavaScriptが無効の時に使用される。JavaScriptが有効な場合はonclick属性で指定したJavaScriptが実行され、一つ前の履歴に戻るようになっている。履歴を使ってもとのページに戻ることで入力済みの値を保持することができる。
<a href="entry.php" onclick="history.back(); return false;">
フォームに戻る</a>
// ◆JS有効:onclick属性が動く
// history.back()を使って履歴で戻る
// 履歴で戻るとユーザ入力値が維持される
// ※簡易敵なユーザ入力値維持
// ただし、本当はSESSIONが望ましい
// a要素のonclickイベントに「return false;」
// href属性を使ったページ遷移を停止
// ◆JS無効:onclick属性が動かない
// href属性を使ってページ遷移
// ただし、ユーザ入力値は維持されない
今回はJavaScriptを使って入力済み値を保持しているが、確実に値を保持したい場合はPHPのセッション等を使ってロジックを組むことになる。
必須項目が入力された時
必須項目が入力されていた場合のみ、DBに登録する。登録の手順を確認する。
全角・半角を変換する関数
全角文字と半角文字を変換する際に次の関数を使う。日本語のみで使える関数。文字エンコードを指定しなかった場合は内部文字エンコードを使用する。
string mb_convert_kana(string変換文字列[,stringオプション
[,stringエンコード]])
mb_convert_kana関数に「n」オプションを付けることでフォームから受け取った文字を半角数字に変換している。
mb_convert_kana($price, 'n')
// mb_convert_kana関数
// 全角・半角等を変換する関数
// 第1引数:変換対象の値
// 第2引数:変換方法を意味のある文字列で指定する
// 'n':「全角」数字を「半角」に変換
DBを安全に使うために
SQL文中で意味を持つ「”」「’」の特殊文字をエスケープする。
string mysqli_real_escape_string(mysqli接続情報,
stringエスケープされる文字列
エスケープしないでSQL文を実行すると悪意あるユーザにDBを破壊されたり、情報を盗み出されたりする。ユーザの入力した値をSQL文に組み込む場合はこの関数を使うようにする。
$item = mysqli_real_escape_string($dbobj, $item);
$price = mysqli_real_escape_string($dbobj, mb_convert_kana($price, 'n'));
$stock = mysqli_real_escape_string($dbobj, mb_convert_kana($stock, 'n'));
$keyword = mysqli_real_escape_string($dbobj, $keyword);
$maker = mysqli_real_escape_string($dbobj, mb_convert_kana($maker, 'n'));
// mysqli_real_escape_string関数
// ◆セキュリティ用の関数
// SQL文で意味のある記号を無害化してくれる
// 文字列開始・終了を意味するクォートを悪用した攻撃を防ぐ
// ※クォート文字をエスケープする
// DELETE時に詳細解説予定
// SQLインジェクション攻撃に対応する
// 第1引数:接続許可証(DBに意味のある記号を確認する)
// 第2引数:対象文字列(受け取ったユーザ入力値)
// 戻り値:無害化された文字列
// ※関数実行時には接続許可証が必要になるので
// DB接続後に呼び出す
// DB接続前に呼び出すことはできないので注意
書式を整える関数
指定の書式に整えるには次の関数を使う。
sprintif(えすぷりんとえふ)
string sprintf(string変換指定子を含むフォーマット文字列
[,mixed変換指定子に当てはめる値
[,mixed変換指定子に当てはめる値
[,...]]])
フォーマット文字列内にある変換指定子(%sや%d等)に第2引数以降で指定した値を当てはめた文字列を生成する。あてはめる値は変換指定子の内容に基づき生成される。今回のプログラムではSQL文の生成に使われているので確認してみる。
$sql = sprintf('INSERT INTO stationery SET
item="%s", price=%d, stock=%d, keyword="%s", maker=%d',
$item, $price, $stock, $keyword, $maker);
// sprintf関数(String print formatted)
// 書式を決めて、文字列を整える関数
// 第1引数:フォーマット対象文字列
// 虫食い部分には「%s」や「%d」を記述する
// 「%s」部分には文字列型の値を挿入
// 「%d」部分には整数型の値を挿入
// 「あああ」や「aaa」等の数値に変換できない
// 数値に変換できない値は「0」に変換
// 第2引数以降:フォーマット文字列の虫食い部分に追加する値
// 型を指定してSQL文を作成することで
// SQLインジェクション攻撃を防ぐ
// ユーザ入力値を使ってSQL文を作成する際は
// 必ずsprintf関数を使用する
// ★文字列連結や変数展開を使用しないこと!
変換指定子 | 内容 |
---|---|
%s | 文字列として扱う |
%d | 10進数の整数として扱う |
実行結果を表示する
実行結果によって内容を変えたメッセージとリンクを表示させる。
<p><?php echo $message; ?></p>
<p><?php echo $btn; ?></p>
編集用フォームへのリンクを作成する
トップページから編集用フォームへのリンクを作成する。index.phpの該当箇所を下記のように変更する。
【変更前】
<td>編集</td>
【変更後】
<td><a href="change.php?id=<?php echo h($data['id']); ?>">編集</a></td>
リンク先のURL「change.php」の後ろにクエリ文字列を付加して編集用フォームにid番号を送る。このid番号を使って編集するレコードを決める。