HTML・PHP・MySQLだけで作る間違い探しゲーム【⑥ランキングページを作る】

# 6. ランキング表示ページ
<details>
<summary><< 前の記事 [【⑤結果表示ページを作る】](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5)</summary>
&emsp;[5. 結果表示ページ](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-結果表示ページ)
&emsp;&emsp;[5-1. 無効なアクセスの拒否](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-1-無効なアクセスの拒否)
&emsp;&emsp;[5-2. 回答時間の算出](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-2-回答時間の算出)
&emsp;&emsp;[5-3. 各変数への格納](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-3-各変数への格納)
&emsp;&emsp;[5-4. 保存する回答時間とカウント数の上限設定](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-4-保存する回答時間とカウント数の上限設定)
&emsp;&emsp;[5-5. 正解／不正解による表示メッセージの分岐](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-5-正解不正解による表示メッセージの分岐)
&emsp;&emsp;[5-6. ランキングへの登録](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-6-ランキングへの登録)
&emsp;&emsp;&emsp;[5-6-1. ランキングテーブルの構成](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-6-1-ランキングテーブルの構成)
&emsp;&emsp;&emsp;[5-6-2. ランキングへの登録](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-6-2-ランキングへの登録)
</details>

最後に、**回答時間ランキング**を表示するページを実装します。

![ランキングページ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598695/edda0b7d-75ea-21bb-2dfa-345aace2b9a6.png)

```php:ranking.php
<?php

// 難易度がPOSTされている場合は変数に格納
if (isset($_POST['show_method'])) {
	$select = $_POST['show_method'];
} else {
	$select = 'all';
}

// db接続
require_once('db_connect.php');

// 選択された表示形式によってSQL文を分岐
$sql = 'SELECT * FROM rankings ';
switch ($select) {
	case 'difficult':
		$sql .= "WHERE difficulty = '難しい（漢字）' ORDER BY time LIMIT 10";
		break;
	case 'easy':
		$sql .= "WHERE difficulty = '易しい（絵文字）' ORDER BY time LIMIT 10";
		break;
	case 'all':
		$sql .= 'ORDER BY time LIMIT 10';
		break;
}
$stmt = $dbh->query($sql);
$players = $stmt->fetchAll();

?>
<!DOCTYPE html>
<html lang="ja">
	<head>
		<meta charset="UTF-8">
		<link rel="stylesheet" href="style.css">
		<title>間違い探し ランキング</title>
	</head>
	<body>
		<h1>回答時間ランキング</h1>
		<form method="POST">
			<select name="show_method" size="1">
				<option value="all" <?php if ($select === 'all') echo 'selected' ?>>全て</option>
				<option value="easy" <?php if ($select === 'easy') echo 'selected' ?>>易しい</option>
				<option value="difficult" <?php if ($select === 'difficult') echo 'selected' ?>>難しい</option>
			</select>
			<input type="submit" value="表示">
		</form>
		<div class="table">
			<table class="s-tbl">
				<thead>
					<tr>
						<th>順位</th>
						<th>名前</th>
						<th>難易度</th>
						<th>回答時間</th>
						<th>リセット回数</th>
					</tr>
				</thead>
				<tbody>
					<?php foreach ($players as $key => $player): ?>
					<tr>
						<td><?= ++$key; ?></td>
						<td><?= $player['name']; ?></td>
						<td><?= $player['difficulty']; ?></td>
						<td><?= $player['time']; ?></td>
						<td><?= $player['reset']; ?></td>
					</tr>
					<?php endforeach; ?>
				</tbody>
			</table>
		</div>
		<p>
			<button type="button" onclick="location.href='start.php'">スタートページへ</button>
		</p>
	</body>
</html>
```
## 6-1. 表示切り替え機能
```php:ranking.php
// 難易度がPOSTされている場合は変数に格納
if (isset($_POST['show_method'])) {
	$select = $_POST['show_method'];
} else {
	$select = 'all';
}

// db接続
require_once('db_connect.php');

// 選択された表示形式によってSQL文を分岐
$sql = 'SELECT * FROM rankings ';
switch ($select) {
	case 'difficult':
		$sql .= "WHERE difficulty = '難しい（漢字）' ORDER BY time LIMIT 10";
		break;
	case 'easy':
		$sql .= "WHERE difficulty = '易しい（絵文字）' ORDER BY time LIMIT 10";
		break;
	case 'all':
		$sql .= 'ORDER BY time LIMIT 10';
		break;
}
$stmt = $dbh->query($sql);
$players = $stmt->fetchAll();
```
後述する`<select>`タグによって難易度を「全て」「易しい」「難しい」の選択肢の中から選べるようになっています。

`if`-`else`文によって選択されていた場合はその`value`の値を、選択されていない場合（=初回訪問時）には`all`を`$select`に格納させています。

そして、DB接続設定ファイルを呼び出し、`$select`内の値によってSQL文が変更されるように`switch`構文を用いています。

どの選択肢においても`SELECT * FROM rankings `は共通しているので、一旦`$sql`にこの文を格納してから、条件分岐に応じたSQL文を結合代入演算子`.=`によって追加しています。

PHPマニュアル「[PHP: 文字列演算子 - Manual](https://www.php.net/manual/ja/language.operators.string.php)」

その後、`query()`メソッドによってSQL文を実行し、返ってきた`PDOStatement`オブジェクトを`$stmt`に格納し、次いで`fetchAll()`メソッドによって結果を`$players`に**配列として**格納しています。

PHPマニュアル「[PHP: PDOStatement::fetchAll - Manual](https://www.php.net/manual/ja/pdostatement.fetchall)」

「**db_connect.php**」でフェッチ形式を`PDO::FETCH_ASSOC`と指定したので、対象テーブルの**カラム名がキーとなった配列**として格納されています。

## 6-2. 入力値の保持 - `<select>`タグ
```php:ranking.php
		<form method="POST">
			<select name="show_method" size="1">
				<option value="all" <?php if ($select === 'all') echo 'selected' ?>>全て</option>
				<option value="easy" <?php if ($select === 'easy') echo 'selected' ?>>易しい</option>
				<option value="difficult" <?php if ($select === 'difficult') echo 'selected' ?>>難しい</option>
			</select>
			<input type="submit" value="表示">
		</form>
```
「**start.php**」の`text`タイプや`radio`タイプの`<input>`タグと同じように、`<select>`タグでも入力値が保持されるようにしています。

これは`$selecte`の値に応じて**`selected`属性**を対象の`<option>`タグ内に`echo`させることで実現できます。

## 6-3. ランキングの表示
### 6-3-1. テーブルの表示
```php:ranking.php
		<div class="table">
			<table class="s-tbl">
				<thead>
					<tr>
						<th>順位</th>
						<th>名前</th>
						<th>難易度</th>
						<th>回答時間</th>
						<th>リセット回数</th>
					</tr>
				</thead>
				<tbody>
					<?php foreach ($players as $key => $player): ?>
					<tr>
						<td><?= ++$key; ?></td>
						<td><?= $player['name']; ?></td>
						<td><?= $player['difficulty']; ?></td>
						<td><?= $player['time']; ?></td>
						<td><?= $player['reset']; ?></td>
					</tr>
					<?php endforeach; ?>
				</tbody>
			</table>
		</div>
```
「**find_the_mistake.php**」と同様に、`foreach`構文によって配列となっている`$players`のデータを`<td>`タグ内で出力しています。

`foreach`構文には、

- `foreach (array_expression as $value)`
- `foreach (array_expression as $key => $value)`

の2種類の構文があり、今回は後者を用いることで配列のインデックス番号`$key`を取得し、**順位**を表現しています。

PHPマニュアル「[PHP: foreach - Manual](https://www.php.net/manual/ja/control-structures.foreach.php)」

順位は`1`から始まりますが、配列のインデックス番号は`0`から始まるので、プレインクリメント（前置加算子）`<?= ++$key; ?>`を用いることで、`+1`させてから出力しています[^12]。

[^12]: **インクリメント**・**デクリメント**にはそれぞれ**プレ（前置）**と**ポスト（後置）**があり、それぞれ2連続の加算子／減算子が前と後に付きます（`$a`への加算なら`++$a`と`$a++`）。これらは「変数返す前に加算／減算する」か「変数を返した後に加算／減算する」かといった違いがあり、今回のコードでポストインクリメント(`$key++`)を用いてしまうと`echo`された後に`+1`されてしまい、意味をなさなくなります。

PHPマニュアル「[PHP: 加算子/減算子 - Manual](https://www.php.net/manual/ja/language.operators.increment.php)」

### 6-3-2. テーブルデザイン
```css:style.css
.table {
  margin-top: 10px;
}
.s-tbl {
  border-collapse: collapse;
}
.s-tbl th, .s-tbl td {
  border: 1px solid #000;
  padding: 0.5em;
}
.s-tbl tr:nth-child(even) {
  background: #eee;
}
.s-tbl tr:hover {
  background: #ffffe0;
}
```
テーブルのCSSに関しては下記の記事から引っ張ってきたものに、少しだけ手を加えました。

ご参考までに。

いつか誰かの役に立つかもしれないweb制作屋の備忘録
「[css tableで背景色を交互に変える方法](https://blog.raizzenet.com/how-to-change-bg-color-for-table/)」

これでようやく完成です！

ここまで読んでくださりありがとうございます。

何かおかしなところや改善できる点がございましたら、コメントいただけるとありがたいです！

<details>
<summary><< 前の記事 [【⑤結果表示ページを作る】](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5)</summary>
&emsp;[5. 結果表示ページ](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-結果表示ページ)
&emsp;&emsp;[5-1. 無効なアクセスの拒否](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-1-無効なアクセスの拒否)
&emsp;&emsp;[5-2. 回答時間の算出](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-2-回答時間の算出)
&emsp;&emsp;[5-3. 各変数への格納](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-3-各変数への格納)
&emsp;&emsp;[5-4. 保存する回答時間とカウント数の上限設定](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-4-保存する回答時間とカウント数の上限設定)
&emsp;&emsp;[5-5. 正解／不正解による表示メッセージの分岐](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-5-正解不正解による表示メッセージの分岐)
&emsp;&emsp;[5-6. ランキングへの登録](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-6-ランキングへの登録)
&emsp;&emsp;&emsp;[5-6-1. ランキングテーブルの構成](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-6-1-ランキングテーブルの構成)
&emsp;&emsp;&emsp;[5-6-2. ランキングへの登録](https://qiita.com/_Taturon_/items/b3eb00e4c3ea52f0b6b5#5-6-2-ランキングへの登録)
</details>