【Webman+MySQL答题系统四】题目管理模块:增删改查接口实战
果子发表于:2026-10-05 20:55:43浏览:1次
本篇目标
实现题库管理:Question 模型 + 列表/新增/修改/删除四个接口,重点处理 JSON 选项字段与服务端校验。

一、模型
app/model/Question.php:
<?php
namespace app\model;
use support\Model;
class Question extends Model
{
protected $table = 'question';
// 允许批量赋值
protected $guarded = [];
// options 存库是 JSON 字符串,取出自动转数组
protected $casts = [
'options' => 'array',
];
}
两个细节:表名单数,与 Illuminate 默认的复数猜测不一致,所以显式声明 $table;$guarded = [] 打开批量赋值,否则 ::create() 会抛 MassAssignmentException。
二、接口规划
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/question/list | 分页列表,可按 type 筛选 |
| POST | /api/question/add | 新增 |
| POST | /api/question/update | 修改 |
| POST | /api/question/delete | 删除(含关联清理) |

三、控制器
app/controller/QuestionController.php:
<?php
namespace app\controller;
use support\Request;
use support\Db;
use app\model\Question;
class QuestionController
{
public function list(Request $request)
{
$page = max(1, (int)$request->get('page', 1));
$limit = min(50, max(1, (int)$request->get('limit', 10)));
$type = (int)$request->get('type', 0);
$query = Question::query();
if ($type) {
$query->where('type', $type);
}
$paginate = $query->orderBy('id', 'desc')
->paginate($limit, ['*'], 'page', $page);
$items = array_map(function ($q) {
return [
'id' => $q->id,
'type' => $q->type,
'title' => $q->title,
'options' => $q->options,
'answer' => $q->answer,
'score' => $q->score,
];
}, $paginate->items());
return success(['total' => $paginate->total(), 'items' => $items]);
}
public function add(Request $request)
{
$type = (int)$request->post('type', 1);
$title = trim((string)$request->post('title'));
$options = $request->post('options'); // 数组: ['A'=>'内容', ...]
$answer = str_replace(' ', '', trim((string)$request->post('answer')));
$score = (float)$request->post('score', 2);
if ($title === '') {
return error('题干不能为空');
}
if (!is_array($options) || count($options) < 2) {
return error('选项至少两个');
}
$keys = array_map('strval', array_keys($options));
foreach (explode(',', $answer) as $k) {
if ($k !== '' && !in_array($k, $keys, true)) {
return error('答案必须在选项范围内');
}
}
if ($answer === '') {
return error('答案不能为空');
}
if ($type === 2 && strpos($answer, ',') === false) {
return error('多选题答案至少两项');
}
$q = Question::create([
'type' => $type,
'title' => $title,
'options' => $options,
'answer' => $answer,
'analysis' => trim((string)$request->post('analysis')),
'score' => $score,
]);
return success(['id' => $q->id], '新增成功');
}
public function update(Request $request)
{
$id = (int)$request->post('id');
$q = Question::find($id);
if (!$q) {
return error('题目不存在', 404);
}
if ($title = trim((string)$request->post('title'))) {
$q->title = $title;
}
if ($options = $request->post('options')) {
$q->options = $options;
}
if ($answer = str_replace(' ', '', trim((string)$request->post('answer')))) {
$q->answer = $answer;
}
if ($request->post('analysis') !== null) {
$q->analysis = (string)$request->post('analysis');
}
if ($request->post('score') !== null) {
$q->score = (float)$request->post('score');
}
$q->save();
return success(['id' => $q->id], '修改成功');
}
public function delete(Request $request)
{
$id = (int)$request->post('id');
$q = Question::find($id);
if (!$q) {
return error('题目不存在', 404);
}
// 已被组卷引用的题先清理关联,放进一个事务
Db::transaction(function () use ($q, $id) {
Db::table('paper_question')->where('question_id', $id)->delete();
$q->delete();
});
return success([], '删除成功');
}
}
options 入库:$casts 声明后,给模型赋数组、save 时自动 json_encode,取出时自动 decode——业务代码里全程当数组用。
四、注册路由
use app\controller\QuestionController;
Route::group('/api/question', function () {
Route::get('/list', [QuestionController::class, 'list']);
Route::post('/add', [QuestionController::class, 'add']);
Route::post('/update', [QuestionController::class, 'update']);
Route::post('/delete', [QuestionController::class, 'delete']);
});
五、验证
curl -X POST http://127.0.0.1:8787/api/question/add \
-d "type=1" -d "title=Webman 的内核是?" \
-d "options[A]=Workerman" -d "options[B]=Swoole" \
-d "answer=A" -d "score=2"
curl "http://127.0.0.1:8787/api/question/list?page=1&limit=10&type=1"
返回 {"code":0,...} 即成功。注意 PHP 的 form 数组语法 options[A]=... 正好能被 $request->post('options') 接成数组。
小结
题库模块完成:模型 casts 处理 JSON、四接口齐活、删除带事务清理关联。下一篇组卷,把题目拼成试卷。
栏目分类全部>

