本教學專為 PHP 初學者設計,透過一個「人員管理」實例,完整示範資料庫的新增、查詢(含分頁)、編輯、刪除操作。採用程序式寫法搭配簡易資料庫封裝類,邏輯清晰易懂,適合作為入門專案練習。
person_system/ ├── common.php # 資料庫連線與工具類 ├── personlist.php # 主程式:列表、新增、編輯、刪除邏輯 ├── style.css # 樣式表 └── database.sql # 匯入此檔建立資料表
CREATE DATABASE `person_db` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE `person_db`; CREATE TABLE `person` ( `person_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `login` VARCHAR(50) NOT NULL COMMENT '登入帳號', `pws` VARCHAR(100) NOT NULL COMMENT '密碼 (實務請加密儲存)', `question` VARCHAR(100) DEFAULT NULL COMMENT '安全問題', `answer` VARCHAR(100) DEFAULT NULL COMMENT '安全答案', `addtime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間', PRIMARY KEY (`person_id`), UNIQUE KEY `uk_login` (`login`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='人員資料表'; -- 測試資料 INSERT INTO `person` (`login`, `pws`, `question`, `answer`) VALUES ('admin', '123456', '最喜歡的顏色', '藍色'), ('user001', 'abc123', '寵物名字', '小白'), ('demo', 'demo888', '出生城市', '台北');
PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ]; $this->pdo = new PDO($dsn, $user, $pass, $options); } public function query($sql, $params = []) { $stmt = $this->pdo->prepare($sql); $stmt->execute($params); return $stmt; } public function fetchAll($sql, $params = []) { return $this->query($sql, $params)->fetchAll(); } public function fetchOne($sql, $params = []) { return $this->query($sql, $params)->fetch(); } public function execute($sql, $params = []) { $stmt = $this->pdo->prepare($sql); return $stmt->execute($params); } public function lastInsertId() { return $this->pdo->lastInsertId(); } public function rowCount($stmt) { return $stmt->rowCount(); } } // 實例化 (請修改為您的實際設定) $db = new Database('localhost', 'root', '', 'person_db'); // 導向函數 function forward($msg, $type, $url) { echo ""; exit; }
execute($sql, [$login, $pws, $question, $answer]); forward('新增成功', 'href', 'personlist.php'); } catch (PDOException $e) { if ($e->getCode() == 23000) { forward('帳號已存在', 'href', 'personlist.php?action=add'); } forward('新增失敗: ' . $e->getMessage(), 'href', 'personlist.php?action=add'); } } // 3. 刪除 if ($action === 'del') { $id = (int)($_GET['person_id'] ?? 0); $page = (int)($_GET['page'] ?? 1); if ($id > 0) { $db->execute("DELETE FROM person WHERE person_id = ?", [$id]); forward('刪除成功', 'href', "personlist.php?page=$page"); } forward('無效 ID', 'href', "personlist.php?page=$page"); } // 4. 顯示編輯表單 if ($action === 'edit') { $id = (int)($_GET['person_id'] ?? 0); $page = (int)($_GET['page'] ?? 1); if ($id <= 0) forward('無效 ID', 'href', "personlist.php?page=$page"); $row = $db->fetchOne("SELECT * FROM person WHERE person_id = ?", [$id]); if (!$row) forward('資料不存在', 'href', "personlist.php?page=$page"); include 'person_form.php'; exit; } // 5. 處理編輯儲存 if ($action === 'editsave') { $id = (int)($_POST['person_id'] ?? 0); $page = (int)($_POST['page'] ?? 1); $login = trim($_POST['login'] ?? ''); $pws = trim($_POST['pws'] ?? ''); $question = trim($_POST['question'] ?? ''); $answer = trim($_POST['answer'] ?? ''); if ($id <= 0 || $login === '') forward('資料不完整', 'href', "personlist.php?page=$page"); $sql = "UPDATE person SET login=?, pws=?, question=?, answer=? WHERE person_id=?"; $db->execute($sql, [$login, $pws, $question, $answer, $id]); forward('修改成功', 'href', "personlist.php?page=$page"); } // ===== 6. 預設:顯示列表 (含分頁) ===== $total = $db->fetchOne("SELECT COUNT(*) as cnt FROM person")['cnt']; $totalPages = ceil($total / $pageSize); $page = max(1, min($page, $totalPages)); $offset = ($page - 1) * $pageSize; $rows = $db->fetchAll("SELECT * FROM person ORDER BY person_id DESC LIMIT $offset, $pageSize"); // 分頁導航函數 function showpage($current, $pageSize, $totalPages, $total) { if ($totalPages <= 1) return ''; $html = ''; $html .= "共 $total 筆 / 第 $current / $totalPages 頁 "; if ($current > 1) { $html .= '« 上一頁 '; } $start = max(1, $current - 3); $end = min($totalPages, $current + 3); for ($i = $start; $i <= $end; $i++) { if ($i == $current) { $html .= '' . $i . ' '; } else { $html .= '' . $i . ' '; } } if ($current < $totalPages) { $html .= '下一頁 » '; } $html .= ''; return $html; } ?>人員管理系統
= $title ?>
" class="btn">返回列表
/* style.css */ * { box-sizing: border-box; } body { font-family: 'Microsoft JhengHei', sans-serif; background: #f0f2f5; margin: 0; padding: 20px; } .container { max-width: 1000px; margin: 0 auto; background: white; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); overflow: hidden; } h1 { background: #2c3e50; color: white; margin: 0; padding: 20px 30px; } h2 { margin-top: 0; color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; } .toolbar { padding: 20px 30px; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center; } .data-table { width: 100%; border-collapse: collapse; } .data-table th, .data-table td { padding: 12px 15px; text-align: left; border-bottom: 1px solid #eee; } .data-table th { background: #f8f9fa; font-weight: 600; color: #333; white-space: nowrap; } .data-table tr:hover td { background: #f8f9fa; } .data-table .empty { text-align: center; color: #999; padding: 40px; } .actions { white-space: nowrap; } .btn { display: inline-block; padding: 8px 16px; border-radius: 4px; text-decoration: none; font-size: 14px; cursor: pointer; border: none; margin-right: 5px; transition: all 0.2s; } .btn.small { padding: 5px 10px; font-size: 12px; } .btn.primary { background: #3498db; color: white; } .btn.primary:hover { background: #2980b9; } .btn.danger { background: #e74c3c; color: white; } .btn.danger:hover { background: #c0392b; } .btn { background: #95a5a6; color: white; } .btn:hover { background: #7f8c8d; } .form-container { padding: 30px; } .form-group { margin-bottom: 20px; } label { display: block; margin-bottom: 6px; font-weight: 500; } .required { color: #e74c3c; } input[type=text], input[type=password] { width: 100%; max-width: 400px; padding: 10px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; } input:focus { border-color: #3498db; outline: none; box-shadow: 0 0 0 3px rgba(52,152,219,0.2); } small { color: #999; font-size: 12px; } .form-actions { margin-top: 20px; padding-top: 20px; border-top: 1px solid #eee; } .pagination { padding: 20px 30px; text-align: center; border-top: 1px solid #eee; } .pagination a, .pagination span { display: inline-block; padding: 6px 12px; margin: 0 3px; border-radius: 3px; text-decoration: none; } .pagination a { background: #fff; border: 1px solid #ddd; color: #3498db; } .pagination a:hover { background: #3498db; color: white; border-color: #3498db; } .pagination .current { background: #3498db; color: white; border-color: #3498db; } .pagination span:first-child { color: #666; margin-right: 15px; }
| 項目 | 本教學做法 | 生產環境建議 |
|---|---|---|
| SQL 注入防護 | PDO Prepared Statements | 同左,嚴禁字串拼接 SQL |
| XSS 防護 | 輸出時 htmlspecialchars() | 同左,考慮 CSP 標頭 |
| 密碼儲存 | 明文 (教學用) | 必須 使用 password_hash()/password_verify() |
| CSRF 防護 | 無 | 表單加入 CSRF Token |
| 權限控制 | 無 | 登入驗證、角色權限 (RBAC) |
| 輸入驗證 | 基本長度、必填 | 伺服器端完整驗證 + 客戶端輔助 |
| 錯誤處理 | try-catch + 友善訊息 | 記錄詳細日誌、使用者看友善訊息 |
password_hash()。