以 id 參數判斷模式:有 id = 編輯,無 id = 新增。同一支程式處理顯示表單與接收 POST。
<?php
// ArticleController.php
class ArticleController {
private PDO $pdo;
public function __construct(PDO $pdo) { $this->pdo = $pdo; }
public function createOrEdit(): void {
$id = $_GET['id'] ?? null;
$isEdit = $id !== null;
// 1. 取得現有資料 (編輯模式)
$data = [
'title' => '',
'content' => '',
'cover' => '',
];
if ($isEdit) {
$stmt = $this->pdo->prepare("SELECT * FROM articles WHERE id = ?");
$stmt->execute([$id]);
$data = $stmt->fetch(PDO::FETCH_ASSOC) ?: $data;
}
// 2. 處理 POST
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$this->handlePost($id, $isEdit);
return; // 已重導向
}
// 3. 顯示表單
$this->renderForm($data, $isEdit);
}
private function handlePost(?int $id, bool $isEdit): void {
$title = trim($_POST['title'] ?? '');
$content = trim($_POST['content'] ?? '');
if (!$title || !$content) {
$this->renderForm(['error'=>'標題與內容必填'], $isEdit);
return;
}
// 檔案上傳處理
$coverPath = $data['cover'] ?? ''; // 保留舊圖
if (!empty($_FILES['cover']['name'])) {
$upload = $this->uploadCover($_FILES['cover']);
if ($upload['success']) {
$coverPath = $upload['path'];
// 刪除舊檔 (編輯模式)
if ($isEdit && $data['cover'] && file_exists($data['cover'])) {
@unlink($data['cover']);
}
} else {
$this->renderForm(['error'=>$upload['error']], $isEdit);
return;
}
}
// 寫入資料庫
if ($isEdit) {
$stmt = $this->pdo->prepare("UPDATE articles SET title=?, content=?, cover=? WHERE id=?");
$stmt->execute([$title, $content, $coverPath, $id]);
} else {
$stmt = $this->pdo->prepare("INSERT INTO articles (title, content, cover) VALUES (?, ?, ?)");
$stmt->execute([$title, $content, $coverPath]);
}
header('Location: /articles');
exit;
}
private function uploadCover(array $file): array {
$allow = ['image/jpeg','image/png','image/webp'];
if (!in_array($file['type'], $allow)) return ['success'=>false, 'error'=>'圖片格式不支援'];
if ($file['size'] > 2*1024*1024) return ['success'=>false, 'error'=>'檔案不可超過 2MB'];
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
$name = bin2hex(random_bytes(8)) . ".$ext";
$dir = __DIR__ . '/../public/uploads/covers/' . date('Y/m');
if (!is_dir($dir)) mkdir($dir, 0755, true);
$dest = "$dir/$name";
if (move_uploaded_file($file['tmp_name'], $dest)) {
return ['success'=>true, 'path'=>"/uploads/covers/" . date('Y/m') . "/$name"];
}
return ['success'=>false, 'error'=>'上傳失敗'];
}
private function renderForm(array $data, bool $isEdit): void {
extract($data); // $title, $content, $cover, $error
include __DIR__ . '/../views/article_form.php';
}
}
<form method="post" enctype="multipart/form-data">
<?php if (!empty($error)): ?><div class="alert"><?= h($error) ?></div><?php endif; ?>
<div>
<label>標題</label>
<input name="title" value="<?= h($title) ?>" required />
</div>
<div>
<label>內容</label>
<textarea name="content" required><?= h($content) ?></textarea>
</div>
<div>
<label>封面圖</label>
<?php if ($cover): ?>
<img src="<?= h($cover) ?>" style="max-height:100px" />
<label><input type="checkbox" name="remove_cover" /> 移除現有圖片</label>
<?php endif; ?>
<input type="file" name="cover" accept="image/*" />
</div>
<button type="submit"><?= $isEdit ? '更新' : '發布' ?></button>
<a href="/articles">取消</a>
</form>
| 情境 | 處理方式 |
|---|---|
| 編輯時不換圖 | 保留資料庫原 cover 路徑,不處理 $_FILES |
| 編輯時換新圖 | 上傳新檔 → 刪除舊檔 → 更新路徑 |
| 編輯時清除圖 | 勾選 remove_cover → 刪除檔案 → 存空字串 |
| 新增失敗保留輸入 | POST 後重新 renderForm,帶入 $_POST 值 |
| 防止重複提交 | PRG 模式:POST 成功後 header('Location: ...') 導向 GET |