thinkphp问卷系统核心是动态表单与数据库聚合统计:通过form_schema表驱动题型渲染,responses表json存答案,mysql直接聚合计算,配合安全过滤、加密存储及缓存优化。

用 ThinkPHP 搭建问卷调查系统,关键不在堆功能,而在于表单结构能随需变化、统计结果能直接反映业务意图。硬编码字段、手动循环算数据,短期能跑通,但加一道题、换一个维度就改代码——这不可持续。
动态表单:靠数据库存结构,不靠PHP写死
所有题目类型(单选、多选、矩阵、评分)都应由配置驱动,而不是在控制器里 if-else 渲染。核心是建一张 form_schema 表:
- 每条记录代表一个题目字段:含 field_name(如 q3_age)、field_type(radio/text/number)、label(“您的年龄”)、options(JSON 存 ["18-25","26-35","36+"])、required、sort_order
- 渲染时查 form_schema,按 sort_order 排序,遍历生成 HTML;前端 name 统一用 field_q3_age,后端用 input('post.field_*') 批量提取
- 提交数据不拆到多张答案表,先存进 responses 表的 json_data 字段(TEXT 类型),内容如 {"q1_name":"张三","q3_age":"26-35"},兼容题型变更和后期扩展
高效统计:让数据库算,别让 PHP 算
万级答卷下,用 PHP array_count_values() 遍历全量数据,响应会卡顿甚至超时。正确做法是把聚合逻辑交给 MySQL:
- 单选题各选项次数:Db::query("SELECT `value`, COUNT(*) as count FROM `answers` WHERE `question_key` = ? GROUP BY `value`", ['q2_satisfaction'])
- 多选题(value 存逗号字符串):MySQL 8.0+ 可用 JSON_TABLE 解析后 GROUP BY;低版本可用 SUBSTRING_INDEX + UNION ALL 拆行再聚合
- 跨题分析(比如“选了‘非常满意’的人中,多少人填了邮箱”):JOIN answers 表两次,分别匹配 q2_satisfaction 和 q4_email,WHERE 加条件后 COUNT(*)
- 时间段统计:whereTime('submit_time', 'between', ['2026-05-01', '2026-05-31']) → 再配合 count() 或 sum()
报表输出:字段明确、格式统一、前端可直用
给 ECharts 或 Chart.js 供数,不是简单 echo json_encode() 就完事:
- 必须用 return json($data),确保 Content-Type 是 application/json;否则前端 fetch 拿不到有效响应
- 聚合查询要用 field() 显式声明字段,例如 field('`option_value` as name, COUNT(*) as value'),name/value 是图表库默认识别的键名
- 空数据要返回 [] 而非 null;时间字段用 date('Y-m-d', strtotime($time)) 格式化,避免前端解析失败
- 大数据量报表加缓存:Db::name('responses')->cache('daily_stat_202605', 3600)->whereTime(...)->select()
安全与演进:从能用到好用
上线前绕不开的几件事:
- XSS 过滤:所有用户输入进库前过 htmlspecialchars() 或 filter_var($input, FILTER_SANITIZE_STRING)
- 敏感字段加密:手机号、邮箱等用 openssl_encrypt() 加密存储,非 password_hash(那是密码专用)
- 跳转逻辑支持:在 form_schema 表加 condition 字段(如 "q1_type == '企业用户'"),提交时服务端解析执行
- 后续升级路径清晰:初期用 JSON 存答案;量大后拆出 answers 表,按 question_key + response_id 建联合索引提升聚合效率
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











