本文目录导读:

实现PHP题库管理,本质上是一个CRUD(增删改查) 结合特定业务逻辑(如分类、标签、难度、题型、批量导入导出)的后台管理系统。
下面从数据库设计、核心功能模块、技术选型建议、关键代码示例以及安全与性能几个方面来详细说明。
数据库设计(核心)
题库管理的基础是合理的表结构,推荐使用 MySQL / MariaDB。
核心表设计:
-- 1. 题型表 (可选,也可以用枚举类型)
CREATE TABLE `question_types` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL COMMENT '题型名称 (如: 单选题, 多选题, 判断题, 填空题, 简答题)',
`code` VARCHAR(30) UNIQUE NOT NULL COMMENT '英文标识 (single_choice, multiple_choice...)'
);
-- 2. 分类/科目表 (树形结构)
CREATE TABLE `categories` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL,
`parent_id` INT DEFAULT 0 COMMENT '父级分类ID,0为顶级',
`sort_order` INT DEFAULT 0 COMMENT '排序'
);
-- 3. 题目主表 (最重要的表)
CREATE TABLE `questions` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`type_id` INT NOT NULL COMMENT '题型ID',
`category_id` INT DEFAULT 0 COMMENT '分类ID',
`difficulty` TINYINT DEFAULT 1 COMMENT '难度: 1简单, 2中等, 3困难',
`content` TEXT NOT NULL COMMENT '题目内容 (支持富文本)',
`options` JSON DEFAULT NULL COMMENT '选项 (JSON格式,如 [{"key":"A","value":"选项1"},...])',
`answer` TEXT NOT NULL COMMENT '答案 (单选题存A, 多选题存A,B,C, 判断题存T/F, 填空题存答案文本)',
`analysis` TEXT COMMENT '答案解析',
`status` TINYINT DEFAULT 1 COMMENT '状态: 1启用, 0禁用',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` INT COMMENT '创建人ID',
INDEX `idx_category` (`category_id`),
INDEX `idx_type` (`type_id`),
INDEX `idx_difficulty` (`difficulty`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 4. 标签表 (用于灵活筛选)
CREATE TABLE `tags` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(50) UNIQUE NOT NULL
);
-- 5. 题目-标签关联表 (多对多)
CREATE TABLE `question_tags` (
`question_id` INT NOT NULL,
`tag_id` INT NOT NULL,
PRIMARY KEY (`question_id`, `tag_id`),
FOREIGN KEY (`question_id`) REFERENCES `questions`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`tag_id`) REFERENCES `tags`(`id`) ON DELETE CASCADE
);
设计要点:
options 字段使用 JSON:PHP 7+ 和 MySQL 5.7+ 原生支持 JSON,存储灵活,不同题型(单选、多选、判断)可通过 PHP 逻辑解析。answer字段:统一存字符串,如果是多选题,存"A,B,C";判断题存"T"或"F"。content和analysis使用 TEXT:支持长文本和 HTML(如果要用富文本编辑器)。
核心功能模块
一个完整的题库管理后端通常包含以下模块:
| 功能模块 | 说明 | | :--- | :--- |管理 | 增/删/改/查单个题目,支持按分类、题型、难度、标签筛选。 | | 分类管理 | 无限极分类,支持新增、修改、排序。 | | 标签管理 | 新增、删除标签,统计每个标签下的题目数量。 | | 批量导入 | Excel (PhpSpreadsheet) / CSV / Word 格式导入。 | | 批量导出 | 按条件筛选后导出为 Excel/CSV。 | | 预览与搜索 | 根据 ID、内容关键词、分类等快速定位题目。 | | 版本控制** | 记录题目的修改历史,可以回退(可选)。 |
PHP 技术栈选择建议
你可以根据项目规模选择不同的框架或纯原生实现:
| 类型 | 推荐技术 | 适用场景 |
|---|---|---|
| 轻量级框架 | Laravel (最流行, ORM, Queue, 文件处理) | 大型、多人协作、需要任务队列(如批量导入) |
| ThinkPHP (国内常用, 简单快速) | 中小型项目, 中文文档齐全 | |
| 高性能框架 | Hyperf / Swoole | 高并发在线考试系统 |
| 无框架 | PHP原生 + PDO | 很小的项目或学习用途, 但维护性差 |
推荐:如果你是个人开发或团队不大,Laravel 是最优选,它提供的 Eloquent ORM 能极大简化数据库操作,Laravel Excel 库可以轻松处理导入导出。
关键代码示例 (以 Laravel 为例)
1 题目模型 (app/Models/Question.php)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Question extends Model
{
protected $fillable = [
'type_id', 'category_id', 'difficulty',
'content', 'options', 'answer', 'analysis', 'status'
];
// 将 options 字段自动转为数组 (Laravel 自动处理 JSON)
protected $casts = [
'options' => 'array',
];
// 关联分类
public function category()
{
return $this->belongsTo(Category::class);
}
// 关联标签
public function tags()
{
return $this->belongsToMany(Tag::class, 'question_tags');
}
}
2 题目控制器(核心增删改查示例)
<?php
namespace App\Http\Controllers\Admin;
use App\Models\Question;
use Illuminate\Http\Request;
class QuestionController extends Controller
{
// 1. 列表 (带筛选)
public function index(Request $request)
{
$query = Question::query();
// 按分类筛选
if ($request->filled('category_id')) {
$query->where('category_id', $request->category_id);
}
// 按题型筛选
if ($request->filled('type_id')) {
$query->where('type_id', $request->type_id);
}
// 按难度筛选
if ($request->filled('difficulty')) {
$query->where('difficulty', $request->difficulty);
}
// 关键词搜索 (内容)
if ($request->filled('keyword')) {
$query->where('content', 'like', '%' . $request->keyword . '%');
}
$questions = $query->orderBy('id', 'desc')->paginate(20);
return view('admin.questions.index', compact('questions'));
}
// 2. 新增
public function store(Request $request)
{
$data = $request->validate([
'type_id' => 'required|integer',
'category_id' => 'required|integer',
'difficulty' => 'required|integer|between:1,3',
'content' => 'required|string',
'options' => 'nullable|array', // 如果是选择题,选项为数组
'answer' => 'required|string',
'analysis' => 'nullable|string',
]);
// 将选项转为 JSON
$data['options'] = json_encode($data['options'] ?? []);
$question = Question::create($data);
// 同步标签 (假设 tags 是一个 ID 数组)
if ($request->has('tags')) {
$question->tags()->sync($request->tags);
}
return redirect()->route('admin.questions.index')->with('success', '题目创建成功');
}
// 3. 更新
public function update(Request $request, Question $question)
{
// 验证逻辑与 store 类似...
$question->update($data);
if ($request->has('tags')) {
$question->tags()->sync($request->tags);
}
return redirect()->route('admin.questions.index')->with('success', '题目更新成功');
}
// 4. 删除
public function destroy(Question $question)
{
$question->delete(); // 自动删除关联标签记录 (外键级联)
return response()->json(['success' => true]);
}
}
3 批量导入 Excel (使用 Laravel Excel)
// 控制器方法
public function import(Request $request)
{
$request->validate([
'file' => 'required|file|mimes:xlsx,csv'
]);
// 使用 Maatwebsite\Excel 导入
Excel::import(new QuestionsImport, $request->file('file'));
return redirect()->back()->with('success', '导入成功');
}
// App/Imports/QuestionsImport.php
namespace App\Imports;
use App\Models\Question;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow; // 处理表头
class QuestionsImport implements ToModel, WithHeadingRow
{
public function model(array $row)
{
// 假设 Excel 列: content, type_id, answer, difficulty
// 注意:这里需要处理选项的格式,Excel 里保存为 A;选项内容;B;选项内容...
$options = [];
if (!empty($row['options'])) {
$pairs = explode(';', $row['options']);
foreach ($pairs as $pair) {
$parts = explode(':', $pair); // 如 A:选项内容
$options[] = ['key' => trim($parts[0]), 'value' => trim($parts[1] ?? '')];
}
}
return new Question([
'type_id' => $row['type_id'],
'category_id' => $row['category_id'] ?? 0,
'difficulty' => $row['difficulty'] ?? 1,
'content' => $row['content'],
'options' => json_encode($options),
'answer' => $row['answer'],
'analysis' => $row['analysis'] ?? '',
'status' => 1,
]);
}
}
前端与交互建议
| 功能 | 推荐前端库/工具 |
|---|---|
| 富文本编辑器 | TinyMCE / CKEditor / WangEditor (轻量) |
| 表格展示 | Layui (后端模板) 或 Ant Design Vue (前后端分离) |
| 批量操作 | 复选框 + Ajax 删除/禁用 |
| 分类树选择 | zTree / jsTree (树形下拉) |
安全与性能优化
- SQL 注入:使用 ORM(如 Laravel Eloquent)或 PDO 预处理,绝对不要拼接 SQL。
- XSS 防护:用户输入的题目内容(尤其是富文本)在显示时必须经过转义。
- Laravel 中:
{{ $question->content }}会自动转义。 - 如果需要输出 HTML(如富文本预览),使用
{!! $question->content !!},但必须确认内容来源是可信的(如管理员录入),否则有 XSS 风险。
- Laravel 中:
- 批量导入优化:Excel 数据量过大时,使用 队列(Queue) 异步处理,避免请求超时。
- 搜索结果缓存:如果题目数量很大(10万+),使用 Elasticsearch 或 Redis 缓存搜索结果。
- 数据库索引:确保
category_id、type_id、difficulty字段已建索引。 - 权限控制:区分管理员、教师、出题人角色,使用 Laravel Permission 或类似 RBAC 库。
实现 PHP 题库管理并不复杂,核心是:
- 设计好 JSON 选项字段,让不同题型共用一张表。
- 使用成熟的框架(Laravel/ThinkPHP)减少重复代码。
- 前端集成富文本编辑器处理题干内容。
- 重点做好导入导出功能,这往往是实际使用中最频繁的操作。