ここまで学んだ知識(HTML/CSS/JS/PHP/SQL)を総動員して挑む、3つの実践課題を用意しました。
難易度順になっています。ぜひ、答えを見る前に一度自分でコードを書いてみてください!
💻 課題1:【JavaScript】高機能・割り勘電卓
基礎編で作ったカウンターよりも少し複雑な計算ロジックを実装します。
📋 要件定義
- 入力項目:「合計金額」(数値)と「人数」(数値)。
- 機能:「計算する」ボタンを押すと、一人あたりの金額を表示する。
- 余り:割り切れない場合、「余り」も表示する(例:1人 3,300円、余り 100円)。
- 【追加チャレンジ】:100円単位で支払うように切り上げる機能をつける(3,333円 → 3,400円集める)。
💡 ヒント
- 余りの計算には
%を使います。 - 切り上げには
Math.ceil()を使いますが、100円単位にするには工夫が必要です(一度100で割って、切り上げて、100掛ける…?)。
▶︎ 解答コードを見る(クリックで展開)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>割り勘電卓</title>
<style>
body { font-family: sans-serif; padding: 20px; max-width: 400px; margin: 0 auto; }
input { padding: 10px; width: 100%; box-sizing: border-box; margin-bottom: 10px; }
button { width: 100%; padding: 10px; background: #00bcd4; color: white; border: none; font-size: 16px; cursor: pointer; }
#result { background: #f0f0f0; padding: 15px; margin-top: 20px; display: none; }
</style>
</head>
<body>
<h1>💰 割り勘電卓</h1>
<label>合計金額(円)</label>
<input type="number" id="price" placeholder="例:10000">
<label>人数(人)</label>
<input type="number" id="num" placeholder="例:3">
<button id="btn">計算する</button>
<div id="result">
<!-- ここに結果が出る -->
<p>一人あたり: <strong id="per-person" style="font-size:24px">0</strong> 円</p>
<p>余り: <span id="remainder">0</span> 円</p>
</div>
<script>
const btn = document.querySelector("#btn");
const priceInput = document.querySelector("#price");
const numInput = document.querySelector("#num");
const resultBox = document.querySelector("#result");
btn.addEventListener("click", () => {
const price = Number(priceInput.value);
const num = Number(numInput.value);
// バリデーション:0人や空欄を防ぐ
if (price <= 0 || num <= 0) {
alert("正しい数値を入力してください");
return;
}
// === 計算ロジック(100円単位切り上げVer) ===
// 1. まず単純に割る
// 例:10000 ÷ 3 = 3333.333...
// 2. 100円単位にするテクニック
// 100で割る(33.33..) → 切り上げ(34) → 100掛ける(3400)
const payment = Math.ceil((price / num) / 100) * 100;
// 3. 余り(というか、多く集まりすぎる分)を計算
// 支払い総額(3400 * 3) - 元の金額(10000) = 200円余る
const totalPay = payment * num;
const remainder = totalPay - price;
// 画面表示
document.querySelector("#per-person").textContent = payment;
document.querySelector("#remainder").textContent = remainder;
resultBox.style.display = "block";
});
</script>
</body>
</html>
🔐 課題2:【PHP & Session】秘密の合言葉ページ
データベースは使わず、PHPの「セッション」の挙動を完全に理解するための課題です。
📋 要件定義
- ログイン画面(login.php):パスワード入力欄が1つだけ。正しい合言葉(例:secret123)で管理画面へ移動。
- 管理画面(admin.php):URLを直打ちしても、ログインしていなければ入れない(強制送還)。ログアウトボタンで戻る。
💡 ヒント
- パスワードが合っていたら
$_SESSION['is_login'] = true;のようにフラグを立てましょう。 - 管理画面の先頭で
empty($_SESSION['is_login'])をチェックします。
▶︎ 解答コードを見る(クリックで展開)
/* --- ファイル1:login.php --- */
<?php
session_start();
$message = "";
// 送信されたら判定
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pass = $_POST['password'];
// 合言葉はここで設定(本来はDBのハッシュと比較します)
if ($pass === 'secret123') {
// セッションに「ログイン済み」の証拠を残す
$_SESSION['is_login'] = true;
// 管理画面へGo
header("Location: admin.php");
exit;
} else {
$message = "パスワードが違います";
}
}
?>
<!DOCTYPE html>
<html>
<body>
<h1>ログイン</h1>
<p style="color:red"><?= $message ?></p>
<form method="post">
パスワード:<input type="password" name="password">
<button type="submit">入室</button>
</form>
</body>
</html>
/* --- ファイル2:admin.php --- */
<?php
session_start();
// ★セキュリティチェック
// セッションに証拠がない、またはfalseなら追い出す
if (empty($_SESSION['is_login'])) {
header("Location: login.php");
exit;
}
// ログアウト処理
if (isset($_POST['logout'])) {
session_destroy(); // セッション破壊
header("Location: login.php");
exit;
}
?>
<!DOCTYPE html>
<html>
<body>
<h1>秘密の管理画面</h1>
<p>ようこそ、選ばれし者よ。</p>
<form method="post">
<button type="submit" name="logout">ログアウト</button>
</form>
</body>
</html>
📝 課題3:【PHP & DB】Todoリスト(完全版)
データベースを使った、みんなで共有できるTodoリストです。
📋 要件定義
- データベース: DB名
todo_app、テーブル名tasks。 - カラム: id, title, status(0:未完了, 1:完了)。
- 機能: 追加(INSERT)、一覧表示(SELECT)、完了(UPDATE)、削除(DELETE)。
💡 ヒント
- 1つのファイル(index.php)で処理する場合、ボタンの
name属性を変えて分岐させます(例:name=”add”, name=”delete”)。 - 完了・削除ボタンのフォームにも
input type="hidden"でIDを仕込むのを忘れずに。
▶︎ 解答コードを見る(クリックで展開)
/* --- まずphpMyAdminでSQLを実行 --- */
/*
CREATE DATABASE todo_app DEFAULT CHARACTER SET utf8mb4;
USE todo_app;
CREATE TABLE tasks (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255),
status INT DEFAULT 0
);
*/
/* --- ファイル:index.php --- */
<?php
// DB接続
try {
$pdo = new PDO('mysql:dbname=todo_app;host=localhost;charset=utf8mb4', 'root', ''); // MAMPはroot/root
} catch (PDOException $e) {
exit('DBError:'.$e->getMessage());
}
// === POST処理(追加・完了・削除) ===
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 1. 追加ボタンが押された
if (isset($_POST['add'])) {
$title = $_POST['title'];
if (!empty($title)) {
$stmt = $pdo->prepare("INSERT INTO tasks (title, status) VALUES (:title, 0)");
$stmt->bindValue(':title', $title, PDO::PARAM_STR);
$stmt->execute();
}
}
// 2. 完了ボタンが押された(UPDATE)
if (isset($_POST['done_id'])) {
$id = $_POST['done_id'];
$stmt = $pdo->prepare("UPDATE tasks SET status = 1 WHERE id = :id");
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
}
// 3. 削除ボタンが押された(DELETE)
if (isset($_POST['delete_id'])) {
$id = $_POST['delete_id'];
$stmt = $pdo->prepare("DELETE FROM tasks WHERE id = :id");
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
}
}
// === 全データ取得 ===
// 未完了(0)を上に、完了(1)を下に表示したい場合、ORDER BY status ASC, id DESC
$sql = "SELECT * FROM tasks ORDER BY status ASC, id DESC";
$tasks = $pdo->query($sql)->fetchAll();
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>DB版Todoリスト</title>
<style>
.done { text-decoration: line-through; color: gray; }
ul { list-style: none; padding: 0; }
li { border-bottom: 1px solid #ddd; padding: 10px; display: flex; justify-content: space-between; }
form { display: inline; }
</style>
</head>
<body>
<h1>Todoリスト(共有版)</h1>
<!-- 追加フォーム -->
<form method="post">
<input type="text" name="title" required placeholder="新しいタスク">
<button type="submit" name="add">追加</button>
</form>
<ul>
<?php foreach ($tasks as $task): ?>
<li>
<!-- タイトル(statusが1なら .done クラスをつける) -->
<span class="<?= $task['status'] == 1 ? 'done' : '' ?>">
<?= htmlspecialchars($task['title'], ENT_QUOTES, 'UTF-8') ?>
</span>
<div>
<!-- 完了ボタン(未完了の時だけ表示) -->
<?php if ($task['status'] == 0): ?>
<form method="post">
<input type="hidden" name="done_id" value="<?= $task['id'] ?>">
<button type="submit">完了</button>
</form>
<?php else: ?>
[済]
<?php endif; ?>
<!-- 削除ボタン -->
<form method="post" onsubmit="return confirm('削除しますか?');">
<input type="hidden" name="delete_id" value="<?= $task['id'] ?>">
<button type="submit">削除</button>
</form>
</div>
</li>
<?php endforeach; ?>
</ul>
</body>
</html>