データベース入門(ツール編2)

データベース入門(ツール作成)
1つ前の記事でSQL文の紹介をしました。この記事ではツールからSQL文を利用してデータベースを操作していきます。xamppで一緒にインストールされているphpで簡単なブラウザで動くツールを作ります。とりあえず仕様を決める...

簡単なphpでDBをいじるツールをつくりましたが、少し改良してみます。

改良内容

  • 簡単なユーザー登録できるようにする
  • ユーザー毎にデータを管理できるようにする

という内容で行きます。

テーブルの設計

まずテーブルをどうするかを決めます。

まずは、ユーザーを管理するUsersテーブルを作ります。

カラムは

  • ユーザーID
    • SERIALですね。
  • 名前
    • 文字を入れたいのでVARCHARの長さ64文字にして、他の人とかぶらないようにUNIQUEにします。

phpmyadmin-createtable5

文字のUNIQUEは長さが必要なので設定しておきます。(インデックスでUNIQUEを選択するとダイアログが出ます。)

phpmyadmin-setunique

ユーザーの管理用テーブルが出来たので、血圧の方のテーブルとユーザーを紐づけます。

ユーザー1人毎に1つずつテーブルを作るのはあほらしいので、各行にユーザーIDを割り振れるように変更します。

blood_pressuresを選択したら「構造」をタブを開きます。

phpmyadmin-addcolumn

下にカラムを追加する項目があるので実行します。

phpmyadmin-addcolumn2

データ型はBIGINTです。というのも先ほどのUsersテーブルのIDがSERIAL(=実質BIGINT)なのでそれに合わせています。

インデックスの項にはINDEXを入れています。INDEXをつけるとそのカラムを使った検索が高速になります。

別テーブルのカラムと関連付けるという事は、その関連付けたそれぞれのカラムで検索する事になるので、INDEXをつけておいた方がパフォーマンスに優れています。(UNIQUEは自動でINDEXがつきます。)

仮データを用意

今だとユーザーが居ないので、blood_pressuresにある既存のデータが存在しないユーザー(user_id=0)を指す事になってしまいます。

(※SERIAL型は0が設定できないのです…)

というわけでUsersテーブルに1人適当なユーザーを増やしました。

phpmyadmin-firstuser

blood_pressuresのuser_idカラムを1にします。(この適当に増やしたユーザーを指すように)

phpmyadmin-firstuser2

とりあえず手動で順番にユーザーIDを1に変えました。

 

index.phpでユーザーを限定する

今のindex.phpでは日付で限定してテーブルを表示していました。

ここでユーザーIDもURLに付けて限定できるように変更します。

<?php
	$begin = null;
	$end = null;
	$userId = 1;

	// GETで指定された値を取得
	if (isset($_GET['begin'])) {
		$begin = $_GET['begin'];
	} else {
		// 指定が無ければ全件取得できる昔の日にちに
		$begin = '1970-01-01';
	}
	if (isset($_GET['end'])) {
		$end = $_GET['end'];
	} else {
		// 指定が無ければ全件取得できる未来の日にちに
		$end = '3000-01-01';
	}
	
	// ユーザーID
	if (isset($_GET['id'])) {
		$userId = $_GET['id'];
	}

	// データーベースへ接続
	$db = new mysqli('localhost', 'root', '', 'health');
	// SQL文を登録
	$state = $db->prepare('SELECT timing, high, low, pulse FROM blood_pressures WHERE user_id=? AND timing BETWEEN ? AND ?;');
	
	// WHEREの条件に使うbeginとendを登録
	$state->bind_param('iss', $userId, $begin, $end);

// 後略

最初にGETのidを取得する部分を追加し、SELECTのWHERE文を変更しています。

SQLの?が増えたのでbind_paramも変わっています。

if (isset($_GET[‘id’])) { $userId = $_GET[‘id’]; }

GETについているid=の部分を$userID変数に設定しています。

$state = $db->prepare(‘SELECT timing, high, low, pulse FROM blood_pressures WHERE user_id=? AND timing BETWEEN ? AND ?;’);

WHEREの条件にuser_id=?が追加されました。

これで検索条件はuser_idが$userIdと等しくて、かつ(AND)、timingが$beginと$endの間ということになります。

$state->bind_param(‘iss’, $userId, $begin, $end);

整数型の$userIdを増やしたため、’ss’から’iss’にし、$userIdを引数に渡しています。

渡す順番はSELECT文の?の順番と一致させるので、上記のとおりです。

これで試しに

http://localhost/health/?id=1
http://localhost/health/?id=2

とかすると結果が変わります。

ユーザー名を表示する

ついでにユーザー名を表示するようにしましょう。

まずは、DBからデータを取得する前に見た目を作ります。

<?php
	$begin = null;
	$end = null;
	$userId = 1;

	// GETで指定された値を取得
	if (isset($_GET['begin'])) {
		$begin = $_GET['begin'];
	} else {
		// 指定が無ければ全件取得できる昔の日にちに
		$begin = '1970-01-01';
	}
	if (isset($_GET['end'])) {
		$end = $_GET['end'];
	} else {
		// 指定が無ければ全件取得できる未来の日にちに
		$end = '3000-01-01';
	}
	
	// ユーザーID
	if (isset($_GET['id'])) {
		$userId = $_GET['id'];
	}

	// データーベースへ接続
	$db = new mysqli('localhost', 'root', '', 'health');
	// SQL文を登録
	$state = $db->prepare('SELECT timing, high, low, pulse FROM blood_pressures WHERE user_id=? AND timing BETWEEN ? AND ?;');
	
	// WHEREの条件に使うbeginとendを登録
	$state->bind_param('iss', $userId, $begin, $end);
	
	$timing = '';
	$high = 0;
	$low = 0;
	$pulse = 0;
	
	// 引数で指定した変数に値が設定されるように登録する
	$state->bind_result($timing, $high, $low, $pulse);
	
	// ユーザー名の取得
	$userName = 'ユーザー名';

	// 登録したSQL文を実行
	$state->execute();
?>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>閲覧ページ</title>
		<style>
			table {
				border-collapse: collapse;
			}
			
			th, td {
				border: solid 1px #000;
			}
		</style>
	</head>
	<body>
		<div>
			<div>
				<header>
					<h3><?= $userName ?> さんの情報</h3>
				</header>
			</div>
			<table>
				<thead>
					<tr>
						<th>日付</th>
						<th>最高</th>
						<th>最低</th>
						<th>脈拍</th>
					</tr>
				</thead>
				<tbody>
	<?-- 行を順番に取得する -->
<?php while ($state->fetch()) : ?>
					<tr>
						<td><?= $timing ?></td>
						<td><?= $high ?></td>
						<td><?= $low ?></td>
						<td><?= $pulse ?></td>
					</tr>

<?php endwhile; ?>
				</tbody>
			</table>
		</div>
		<div>
			<a href="addform.html">追加フォーム</a>
		</div>
	</body>
</html>
<?php
	// DBへの接続を切断
	$state->close();
	$db->close();
?>

変数$userNameの追加と、それを表示する<h3><?= $userName ?> さんの情報</h3>を追加しました。

では、$userNameにDBの値を設定するようにします。

<?php
	$begin = null;
	$end = null;
	$userId = 1;

	// GETで指定された値を取得
	if (isset($_GET['begin'])) {
		$begin = $_GET['begin'];
	} else {
		// 指定が無ければ全件取得できる昔の日にちに
		$begin = '1970-01-01';
	}
	if (isset($_GET['end'])) {
		$end = $_GET['end'];
	} else {
		// 指定が無ければ全件取得できる未来の日にちに
		$end = '3000-01-01';
	}
	
	// ユーザーID
	if (isset($_GET['id'])) {
		$userId = $_GET['id'];
	}

	// データーベースへ接続
	$db = new mysqli('localhost', 'root', '', 'health');
	// SQL文を登録
	$state = $db->prepare('SELECT timing, high, low, pulse FROM blood_pressures WHERE user_id=? AND timing BETWEEN ? AND ?;');
	
	// WHEREの条件に使うbeginとendを登録
	$state->bind_param('iss', $userId, $begin, $end);
	
	$timing = '';
	$high = 0;
	$low = 0;
	$pulse = 0;
	
	// 引数で指定した変数に値が設定されるように登録する
	$state->bind_result($timing, $high, $low, $pulse);
	
	// ユーザー名の取得
	$userName = 'ユーザー名';
	
	$state2 = $db->prepare('SELECT user_name FROM users WHERE id=?;');
	
	$state2->bind_param('i', $userId);
	$state2->bind_result($userName);
	// ユーザー名取得のSQL文実行
	$state2->execute();
	
	if (!$state2->fetch()) {
		// ユーザー名の取得失敗
		$userName = 'ユーザーはいません';
	}
	$state2->close();

	// 血圧取得SQL文を実行
	$state->execute();
?>

順番や変数名に気を付けてください。

存在しないユーザーIDを指定すると、「ユーザーはいません」が表示されます。

ところが、存在するユーザーIDの場合は??と表示されることがあります。

文字コードを指定する

これはDBとphpでの文字コードの違いによって発生します。

もし、このような文字化けが発生する場合は文字コードの設定を3箇所見直してみてください。

照合順序

phpMyAdminでusersの構造タブを開き、nameカラムをみてください。

照合順序が「utf8_general_ci」になっていれば問題ありません。

phpファイルの文字コード

ファイルを保存するときの文字コードでutf8が指定されているか確認してみてください。

ちなみに、BOMという指定があるかもしれませんがこれはチェックしないでください。

mysqliの設定

これが文字化けする際に一番多いパターンです。mysqliではphpとdbとのやりとりで使う文字コードを指定しなければいけません。

実は他のDB接続ライブラリでも同様の設定が何か用意されていることが多いです。もし文字コードあってるはずなのに、文字化けするという際はドキュメントを確認してみてください。

	// データーベースへ接続
	$db = new mysqli('localhost', 'root', '', 'health');
	$db->set_charset('utf8');

mysqliでは文字コードの指定はset_charsetという関数を利用します。

ユーザー登録ページを作る

仮データでは味気ないので、ユーザーを登録するページを作ります。

まず、登録フォームです。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>ユーザー登録</title>
	</head>
	<body>
		<div>
			<form action="add-user.php" method="POST" accept-charset="utf-8">
				<p>ユーザー名: <input type="text" name="name" /></p>
				<p><input type="submit" value="登録" /></p>
			</form>
		</div>
	</body>
</html>

ファイル名はadduserform.htmlにしておきます。文字コードをutf8として保存するのを忘れずに。

http://localhost/health/adduserform.html

では実際にDBに保存するadd-user.phpを作ります。

<?php
	
	$name = $_POST['name'];
	
	// データーベースへ接続
	$db = new mysqli('localhost', 'root', '', 'health');
	$db->set_charset('utf8');
	
	// insert文登録
	$state = $db->prepare('INSERT INTO users (user_name) VALUES (?);');
	
	// ユーザー名を?に設定
	$state->bind_param('s', $name);
	
	// SQL実行
	$state->execute();

	// 挿入されたユーザーのIDを取得
	$userId = $db->insert_id;
	
	// データベース切断
	$state->close();
	$db->close();
	
	// 血圧閲覧ページへ遷移
	header('Location: index.php?id=' . $userId);
?>

INSERTしています。

$userId = $db->insert_id;で直前に挿入した行のIDを取得しています。

最後に血圧閲覧ページにidを指定して遷移するようにしました。

血圧登録ページでもユーザーを指定できるように変更

現状血圧登録にはユーザーの指定がないので指定できるようにします。

addform.htmlを書き換えます。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>入力フォーム</title>
	</head>
	<body>
		<div>
			<form action="add.php" method="POST" accept-charset="utf-8">
				<p>最高血圧: <input type="text" name="high" />
				<p>最低血圧: <input type="text" name="low" />
				<p>脈拍: <input type="text" name="pulse" />
				<p>ユーザー名: <input type="text" name="user" />
				<p><input type="submit" value="送信" />
			</form>
		</div>
	</body>
</html>

ログインページはめんどくさかったのでフォームに指定するようにしています。

登録処理をしているadd.phpも書き換えます。

<?php
	// データーベースへ接続
	$db = new mysqli('localhost', 'root', '', 'health');
	$db->set_charset('utf8');
	
	// 指定されたユーザー名
	$userName = $_POST['user'];
	$userId = 1;
	
	// ユーザーIDを取得
	$state = $db->prepare('SELECT id FROM users WHERE user_name=?;');
	$state->bind_param('s', $userName);
	$state->bind_result($userId);
	// SQL実行
	$state->execute();

	// 1行取得
	if (!$state->fetch()) {
		// 取得失敗
		header('Location: addform.html');
	}
	$state->close();

	// SQL文を登録
	$state = $db->prepare('INSERT INTO blood_pressures (timing, high, low, pulse, user_id) VALUES (NOW(), ?, ?, ?, ?);');

	// フォームで設定されたデータを変数に一度保存
	$high = $_POST['high'];
	$low = $_POST['low'];
	$pulse = $_POST['pulse'];

	// ?の部分を置き換える値を指定する
	$state->bind_param('iiii', $high, $low, $pulse, $userId);
	
	// SQL文実行
	$state->execute();
	
	// データベースとの接続を切断
	$state->close();
	$db->close();
	
	// index.phpへ遷移
	header('Location: index.php?id=' . $userId);
?>

DBに血圧を登録するやつです。

最初に$state = $db->prepare(‘SELECT id FROM users WHERE user_name=?;’);でユーザーIDを取得しています。

INSERT文では、user_idの指定が増えて

$state = $db->prepare(‘INSERT INTO blood_pressures (timing, high, low, pulse, user_id) VALUES (NOW(), ?, ?, ?, ?);’);

となっています。

bind_paramも$userIdの指定が増えています。$state->bind_param(‘iiii’, $high, $low, $pulse, $userId);

 

最後の遷移先にもuserIdを指定するようにしています。

ログインシステムにするには

記事にするのが面倒だったので割愛していますが、セキュリティを置いておけばそこまで難しくはありません。

ログインページでユーザー名を入れられたらセッション変数にユーザーIDを入れるだけです。(phpの場合は$_SESSIONで設定します。セッション変数は各ブラウザ毎にそれぞれ保存されているデータです。)

WEBツールではなく、Windowsアプリなんかの場合はグローバル変数にユーザーIDを保持する事になるでしょう。

タイトルとURLをコピーしました