プロンプトを修正してみました
外国人の子供に日本語を教えます。 学習カリキュラムの進捗を記録して、申し送りをしたいと思います
まず先生がログインします。 ログインでは先生ID、パスワードで認証してください 生徒を選ぶと前回の履歴や注意事項が表示される 未完のカリキュラムを表示して達成度の入力をします 懸案、注意事項は文字での入力も可能にします 履歴には日付、担当先生名も入れてください 記録はスマホで簡単に選択して登録できるようにしたいです 最後にログアウトします
管理画面では生徒、先生、カリキュラムの登録、更新、削除画面も必要です 管理画面は先生の管理者フラグが設定されているときに表示させます 管理者先生をデータベース作成時に作ってください。 管理者先生は、IDは9999,パスワードは9999、名前は管理者です カリキュラムはexcelに記述され、連番、章、項目、説明があります。excelアップロードで登録できる機能も必要です
プログラムはPHP、データベースはsqliteで作ります CSSはbootstrapを使ってレスポンシブにして下さい
プログラムを作ってください
そうすると結果は大きく変わりました
PHPのファイルはひとつになりシンプルになりました
以下のPHPプログラムは、外国人向け日本語学習カリキュラムの進捗管理システムを実装します。Bootstrapを使用したレスポンシブデザイン、SQLiteデータベース、Excelアップロード機能を含みます。
index.php
composer.json
システムを使用するには以下の手順が必要です:
- PHP環境を用意(PHP 7.4以上推奨)
-
omposer install - SQLiteが使用可能な状態であることを確認
- japanese_learning.dbファイルが書き込み可能なディレクトリに配置されるようにする
- Excelファイルは以下の形式で作成:
- 1列目:連番(ID)
- 2列目:章
- 3列目:項目
- 4列目:説明
- 1行目はヘッダーとして無視されます
-
システムの特徴:
- レスポンシブデザイン(Bootstrap 5.3)
- スマホでの簡単操作を考慮したシンプルなUI
- 管理者機能(生徒・先生管理、カリキュラムアップロード)
- 進捗履歴の表示と新規登録
- 安全なパスワード管理(password_hash使用)
- エラー処理と入力検証
-
管理者(ID: 9999, パスワード: 9999)でログインすると、管理画面が表示されます。
<?php
session_start();
require 'vendor/autoload.php'; // Composerでインストールしたライブラリ読み込み
use PhpOffice\PhpSpreadsheet\IOFactory;
$db = new SQLite3('japanese_learning.db');
// データベース初期化
$db->exec("CREATE TABLE IF NOT EXISTS teachers (
id TEXT PRIMARY KEY,
password TEXT,
name TEXT,
is_admin INTEGER
)");
$db->exec("CREATE TABLE IF NOT EXISTS students (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT
)");
$db->exec("CREATE TABLE IF NOT EXISTS curriculums (
id INTEGER PRIMARY KEY,
chapter TEXT,
item TEXT,
description TEXT
)");
$db->exec("CREATE TABLE IF NOT EXISTS progress (
id INTEGER PRIMARY KEY AUTOINCREMENT,
student_id INTEGER,
curriculum_id INTEGER,
teacher_id TEXT,
date TEXT,
achievement INTEGER,
notes TEXT
)");
// 管理者初期化
$stmt = $db->prepare("INSERT OR IGNORE INTO teachers (id, password, name, is_admin) VALUES (?, ?, ?, ?)");
$stmt->bindValue(1, '9999', SQLITE3_TEXT);
$stmt->bindValue(2, password_hash('9999', PASSWORD_DEFAULT), SQLITE3_TEXT);
$stmt->bindValue(3, '管理者', SQLITE3_TEXT);
$stmt->bindValue(4, 1, SQLITE3_INTEGER);
$stmt->execute();
// ログイン処理
if (isset($_POST['login'])) {
$stmt = $db->prepare("SELECT * FROM teachers WHERE id = ?");
$stmt->bindValue(1, $_POST['teacher_id'], SQLITE3_TEXT);
$result = $stmt->execute()->fetchArray(SQLITE3_ASSOC);
if ($result && password_verify($_POST['password'], $result['password'])) {
$_SESSION['teacher_id'] = $result['id'];
$_SESSION['teacher_name'] = $result['name'];
$_SESSION['is_admin'] = $result['is_admin'];
} else {
$error = "ログインに失敗しました";
}
}
// ログアウト処理
if (isset($_GET['logout'])) {
session_destroy();
header('Location: index.php');
exit;
}
// Excelアップロード処理
if (isset($_FILES['curriculum_file']) && $_SESSION['is_admin']) {
$file = $_FILES['curriculum_file']['tmp_name'];
$spreadsheet = IOFactory::load($file);
$sheet = $spreadsheet->getActiveSheet();
$db->exec("DELETE FROM curriculums"); // 既存データ削除
foreach ($sheet->getRowIterator(2) as $row) {
$data = [];
foreach ($row->getCellIterator() as $cell) {
$data[] = $cell->getValue();
}
if (count($data) >= 4) {
$stmt = $db->prepare("INSERT INTO curriculums (id, chapter, item, description) VALUES (?, ?, ?, ?)");
$stmt->bindValue(1, $data[0], SQLITE3_INTEGER);
$stmt->bindValue(2, $data[1], SQLITE3_TEXT);
$stmt->bindValue(3, $data[2], SQLITE3_TEXT);
$stmt->bindValue(4, $data[3], SQLITE3_TEXT);
$stmt->execute();
}
}
}
// 進捗登録処理
if (isset($_POST['save_progress']) && isset($_SESSION['teacher_id'])) {
$stmt = $db->prepare("INSERT INTO progress (student_id, curriculum_id, teacher_id, date, achievement, notes) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->bindValue(1, $_POST['student_id'], SQLITE3_INTEGER);
$stmt->bindValue(2, $_POST['curriculum_id'], SQLITE3_INTEGER);
$stmt->bindValue(3, $_SESSION['teacher_id'], SQLITE3_TEXT);
$stmt->bindValue(4, date('Y-m-d'), SQLITE3_TEXT);
$stmt->bindValue(5, $_POST['achievement'], SQLITE3_INTEGER);
$stmt->bindValue(6, $_POST['notes'], SQLITE3_TEXT);
$stmt->execute();
}
// 管理者機能:生徒・先生登録
if (isset($_POST['add_student']) && $_SESSION['is_admin']) {
$stmt = $db->prepare("INSERT INTO students (name) VALUES (?)");
$stmt->bindValue(1, $_POST['student_name'], SQLITE3_TEXT);
$stmt->execute();
}
if (isset($_POST['add_teacher']) && $_SESSION['is_admin']) {
$stmt = $db->prepare("INSERT INTO teachers (id, password, name, is_admin) VALUES (?, ?, ?, ?)");
$stmt->bindValue(1, $_POST['teacher_id'], SQLITE3_TEXT);
$stmt->bindValue(2, password_hash($_POST['password'], PASSWORD_DEFAULT), SQLITE3_TEXT);
$stmt->bindValue(3, $_POST['teacher_name'], SQLITE3_TEXT);
$stmt->bindValue(4, isset($_POST['is_admin']) ? 1 : 0, SQLITE3_INTEGER);
$stmt->execute();
}
// 管理者機能:削除
if (isset($_POST['delete_student']) && $_SESSION['is_admin']) {
$stmt = $db->prepare("DELETE FROM students WHERE id = ?");
$stmt->bindValue(1, $_POST['student_id'], SQLITE3_INTEGER);
$stmt->execute();
}
if (isset($_POST['delete_teacher']) && $_SESSION['is_admin']) {
$stmt = $db->prepare("DELETE FROM teachers WHERE id = ?");
$stmt->bindValue(1, $_POST['teacher_id'], SQLITE3_TEXT);
$stmt->execute();
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>日本語学習管理システム</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<?php if (!isset($_SESSION['teacher_id'])): ?>
<!-- ログイン画面 -->
<h2>ログイン</h2>
<?php if (isset($error)): ?>
<div class="alert alert-danger"><?php echo $error; ?></div>
<?php endif; ?>
<form method="post" class="card p-4">
<div class="mb-3">
<label for="teacher_id" class="form-label">先生ID</label>
<input type="text" class="form-control" id="teacher_id" name="teacher_id" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">パスワード</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" name="login" class="btn btn-primary">ログイン</button>
</form>
<?php else: ?>
<!-- メイン画面 -->
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>ようこそ、<?php echo htmlspecialchars($_SESSION['teacher_name']); ?>さん</h2>
<a href="?logout=1" class="btn btn-secondary">ログアウト</a>
</div>
<!-- 生徒選択 -->
<h3>生徒選択</h3>
<form method="get" class="mb-4">
<select name="student_id" class="form-select" onchange="this.form.submit()">
<option value="">生徒を選択</option>
<?php
$students = $db->query("SELECT * FROM students");
while ($student = $students->fetchArray(SQLITE3_ASSOC)) {
$selected = (isset($_GET['student_id']) && $_GET['student_id'] == $student['id']) ? 'selected' : '';
echo "<option value='{$student['id']}' $selected>" . htmlspecialchars($student['name']) . "</option>";
}
?>
</select>
</form>
<?php if (isset($_GET['student_id']) && $_GET['student_id']): ?>
<!-- 履歴表示 -->
<h3>前回の履歴</h3>
<table class="table table-striped">
<thead>
<tr>
<th>日付</th>
<th>担当先生</th>
<th>項目</th>
<th>達成度</th>
<th>備考</th>
</tr>
</thead>
<tbody>
<?php
$stmt = $db->prepare("SELECT p.*, c.item, t.name as teacher_name FROM progress p
JOIN curriculums c ON p.curriculum_id = c?
JOIN teachers t ON p.teacher_id = t.id
WHERE p.student_id = ? ORDER BY p.date DESC");
$stmt->bindValue(1, $_GET['student_id'], SQLITE3_INTEGER);
$result = $stmt->execute();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
echo "<tr>";
echo "<td>" . htmlspecialchars($row['date']) . "</td>";
echo "<td>" . htmlspecialchars($row['teacher_name']) . "</td>";
echo "<td>" . htmlspecialchars($row['item']) . "</td>";
echo "<td>" . htmlspecialchars($row['achievement']) . "%</td>";
echo "<td>" . htmlspecialchars($row['notes']) . "</td>";
echo "</tr>";
}
?>
</tbody>
</table>
<!-- 進捗入力 -->
<h3>進捗入力</h3>
<form method="post" class="card p-4">
<input type="hidden" name="student_id" value="<?php echo $_GET['student_id']; ?>">
<div class="mb-3">
<label for="curriculum_id" class="form-label">カリキュラム項目</label>
<select name="curriculum_id" class="form-select" required>
<option value="">選択してください</option>
<?php
$curriculums = $db->query("SELECT * FROM curriculums");
while ($curriculum = $curriculums->fetchArray(SQLITE3_ASSOC)) {
echo "<option value='{$curriculum['id']}'>" . htmlspecialchars($curriculum['chapter'] . ' - ' . $curriculum['item']) . "</option>";
}
?>
</select>
</div>
<div class="mb-3">
<label for="achievement" class="form-label">達成度 (%)</label>
<input type="number" name="achievement" class="form-control" min="0" max="100" required>
</div>
<div class="mb-3">
<label for="notes" class="form-label">備考</label>
<textarea name="notes" class="form-control"></textarea>
</div>
<button type="submit" name="save_progress" class="btn btn-primary">登録</button>
</form>
<?php endif; ?>
<!-- 管理画面 -->
<?php if ($_SESSION['is_admin']): ?>
<h3 class="mt-5">管理画面</h3>
<!-- 生徒管理 -->
<h4>生徒管理</h4>
<form method="post" class="card p-4 mb-4">
<div class="mb-3">
<label for="student_name" class="form-label">生徒名</label>
<input type="text" name="student_name" class="form-control" required>
</div>
<button type="submit" name="add_student" class="btn btn-primary">生徒追加</button>
</form>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>名前</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php
$students = $db->query("SELECT * FROM students");
while ($student = $students->fetchArray(SQLITE3_ASSOC)) {
echo "<tr>";
echo "<td>" . htmlspecialchars($student['id']) . "</td>";
echo "<td>" . htmlspecialchars($student['name']) . "</td>";
echo "<td>
<form method='post' style='display:inline'>
<input type='hidden' name='student_id' value='{$student['id']}'>
<button type='submit' name='delete_student' class='btn btn-danger btn-sm'>削除</button>
</form>
</td>";
echo "</tr>";
}
?>
</tbody>
</table>
<!-- 先生管理 -->
<h4>先生管理</h4>
<form method="post" class="card p-4 mb-4">
<div class="mb-3">
<label for="teacher_id" class="form-label">先生ID</label>
<input type="text" name="teacher_id" class="form-control" required>
</div>
<div class="mb-3">
<label for="teacher_name" class="form-label">先生名</label>
<input type="text" name="teacher_name" class="form-control" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">パスワード</label>
<input type="password" name="password" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-check-label">
<input type="checkbox" name="is_admin" class="form-check-input"> 管理者権限
</label>
</div>
<button type="submit" name="add_teacher" class="btn btn-primary">先生追加</button>
</form>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>名前</th>
<th>管理者</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php
$teachers = $db->query("SELECT * FROM teachers");
while ($teacher = $teachers->fetchArray(SQLITE3_ASSOC)) {
echo "<tr>";
echo "<td>" . htmlspecialchars($teacher['id']) . "</td>";
echo "<td>" . htmlspecialchars($teacher['name']) . "</td>";
echo "<td>" . ($teacher['is_admin'] ? 'はい' : 'いいえ') . "</td>";
echo "<td>
<form method='post' style='display:inline'>
<input type='hidden' name='teacher_id' value='{$teacher['id']}'>
<button type='submit' name='delete_teacher' class='btn btn-danger btn-sm'>削除</button>
</form>
</td>";
echo "</tr>";
}
?>
</tbody>
</table>
<!-- カリキュラムアップロード -->
<h4>カリキュラムアップロード</h4>
<form method="post" enctype="multipart/form-data" class="card p-4">
<div class="mb-3">
<label for="curriculum_file" class="form-label">Excelファイル</label>
<input type="file" name="curriculum_file" class="form-control" accept=".xlsx,.xls" required>
</div>
<button type="submit" class="btn btn-primary">アップロード</button>
</form>
<?php endif; ?>
<?php endif; ?>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
さあ実行してみるとエラーが
SQLがおかしいようです
ここを見て、私はびっくりしました。
AIがなぜこんなミスをするのだろうか。
どう考えても不思議です。
どうもわたしの技量が試されているとしか思えません。
この程度のことをわからないとプログラムを作る資格がないよといっているみたいです
エラー箇所 JOIN curriculums c ON p.curriculum_id = c?
修正 JOIN curriculums c ON p.curriculum_id = c.id
見た瞬間にわかるエラーです。
きっとわざとだと思います
このエラーを,AIに質問する人がどの程度いるか調査して、出来の悪いプログラマーの率を調べているのではないでしょうか。
$stmt = $db->prepare("SELECT p.*, c.item, t.name as teacher_name FROM progress p
JOIN curriculums c ON p.curriculum_id = c?
JOIN teachers t ON p.teacher_id = t.id
WHERE p.student_id = ? ORDER BY p.date DESC");
$stmt->bindValue(1, $_GET['student_id'], SQLITE3_INTEGER);
とにかくプロンプトを変えると、結果がとてもよくなります
プロンプトさえよければ実際に使えるプログラムができそうです