本文目录导读:

在PHP项目中实现自定义报表查询模板的保存,核心思路是将用户的查询条件(筛选字段、排序规则、过滤条件等)序列化存储,并支持按模板名称加载,以下是完整的实现方案:
数据库设计
-- 报表模板表
CREATE TABLE report_templates (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL, -- 所属用户
report_type VARCHAR(50), -- 报表类型(如:order_report, user_report)
template_name VARCHAR(100), -- 模板名称
template_data TEXT, -- 序列化的查询条件JSON
is_default TINYINT DEFAULT 0, -- 是否默认模板
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_user_report (user_id, report_type)
);
template_data 字段示例(JSON格式):
{
"filters": [
{"field": "order_date", "operator": "between", "value": ["2024-01-01", "2024-01-31"]},
{"field": "status", "operator": "in", "value": ["paid", "shipped"]},
{"field": "amount", "operator": ">=", "value": 100}
],
"sort": {"field": "order_date", "order": "DESC"},
"columns": ["order_id", "customer_name", "amount", "status"],
"group_by": ["status"],
"record_limit": 100
}
后端PHP实现
1 保存模板
<?php
// save_template.php
require_once 'db.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$userId = $_SESSION['user_id'];
$reportType = $_POST['report_type'] ?? '';
$templateName = $_POST['template_name'] ?? '';
$templateData = $_POST['template_data'] ?? []; // 前端传来的JSON对象
// 参数验证
if (empty($reportType) || empty($templateName) || empty($templateData)) {
http_response_code(400);
echo json_encode(['error' => '缺少必要参数']);
exit;
}
// 序列化查询条件
$serializedData = json_encode($templateData, JSON_UNESCAPED_UNICODE);
// 检查是否设置为默认模板
$isDefault = isset($_POST['set_default']) && $_POST['set_default'] ? 1 : 0;
// 如果设置为默认,先取消该用户该类型的所有默认
if ($isDefault) {
$stmt = $pdo->prepare("UPDATE report_templates SET is_default = 0 WHERE user_id = ? AND report_type = ?");
$stmt->execute([$userId, $reportType]);
}
// 插入或更新(如果同名则更新)
$stmt = $pdo->prepare("
INSERT INTO report_templates (user_id, report_type, template_name, template_data, is_default)
VALUES (?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
template_data = VALUES(template_data),
is_default = VALUES(is_default),
updated_at = NOW()
");
try {
$stmt->execute([$userId, $reportType, $templateName, $serializedData, $isDefault]);
echo json_encode(['success' => true, 'message' => '模板保存成功']);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => '保存失败: ' . $e->getMessage()]);
}
}
2 加载模板列表
<?php
// get_templates.php
require_once 'db.php';
$userId = $_SESSION['user_id'];
$reportType = $_GET['report_type'] ?? '';
$stmt = $pdo->prepare("SELECT id, template_name, is_default, created_at FROM report_templates
WHERE user_id = ? AND report_type = ?
ORDER BY is_default DESC, created_at DESC");
$stmt->execute([$userId, $reportType]);
$templates = $stmt->fetchAll(PDO::FETCH_ASSOC);
header('Content-Type: application/json');
echo json_encode($templates);
3 加载单个模板
<?php
// load_template.php
require_once 'db.php';
$templateId = $_GET['id'] ?? 0;
$stmt = $pdo->prepare("SELECT template_data FROM report_templates WHERE id = ? AND user_id = ?");
$stmt->execute([$templateId, $_SESSION['user_id']]);
$template = $stmt->fetch(PDO::FETCH_ASSOC);
if ($template) {
$templateData = json_decode($template['template_data'], true);
header('Content-Type: application/json');
echo json_encode($templateData);
} else {
http_response_code(404);
echo json_encode(['error' => '模板不存在']);
}
前端JavaScript实现
1 保存模板
// 假设已有查询条件对象 reportFilters
const reportFilters = {
filters: [
{ field: 'order_date', operator: 'between', value: ['2024-01-01', '2024-01-31'] },
{ field: 'status', operator: 'in', value: ['paid', 'shipped'] }
],
sort: { field: 'order_date', order: 'DESC' },
columns: ['order_id', 'customer_name', 'amount', 'status']
};
function saveTemplate() {
const templateName = prompt('请输入模板名称:');
if (!templateName) return;
const setDefault = confirm('是否设为默认模板?');
fetch('/save_template.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
report_type: 'order_report',
template_name: templateName,
template_data: reportFilters,
set_default: setDefault
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('模板保存成功');
loadTemplateList(); // 刷新模板列表
}
})
.catch(error => console.error('保存失败:', error));
}
2 加载模板列表示例
function loadTemplateList() {
fetch('/get_templates.php?report_type=order_report')
.then(response => response.json())
.then(templates => {
const select = document.getElementById('templateSelect');
select.innerHTML = '<option value="">请选择模板</option>';
templates.forEach(template => {
const option = document.createElement('option');
option.value = template.id;
option.textContent = template.template_name + (template.is_default ? ' (默认)' : '');
select.appendChild(option);
});
});
}
function applyTemplate(templateId) {
fetch(`/load_template.php?id=${templateId}`)
.then(response => response.json())
.then(templateData => {
// 将查询条件应用到报表界面
reportFilters = templateData;
applyFiltersToUI(templateData);
reloadReport();
});
}
前端UI交互参考
<div class="report-toolbar">
<button onclick="saveTemplate()">保存为模板</button>
<select id="templateSelect" onchange="if(this.value) applyTemplate(this.value)">
<option value="">选择模板</option>
</select>
<button onclick="loadTemplateList()">刷新模板列表</button>
</div>
高级功能扩展
1 模板分类与权限
-- 添加模板分类和权限控制 ALTER TABLE report_templates ADD COLUMN category VARCHAR(50) DEFAULT 'personal'; ALTER TABLE report_templates ADD COLUMN is_public TINYINT DEFAULT 0;
2 模板版本管理
-- 模板版本表
CREATE TABLE template_versions (
id INT AUTO_INCREMENT PRIMARY KEY,
template_id INT NOT NULL,
template_data TEXT,
version INT DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
change_log TEXT
);
3 模板分享功能
// 分享模板时生成分享码
function shareTemplate($templateId) {
$shareCode = bin2hex(random_bytes(8));
// 存储分享关系
$stmt = $pdo->prepare("INSERT INTO template_shares (template_id, share_code) VALUES (?, ?)");
$stmt->execute([$templateId, $shareCode]);
return $shareCode;
}
安全注意事项
- 序列化安全:始终使用
json_encode/json_decode,不要使用serialize() - 权限校验:加载模板时必须校验用户所有权
- SQL注入防护:使用参数化查询
- XSS防护:前端显示模板名称时进行HTML转义
- 数据验证:保存时验证查询结构的合法性,防止恶意数据
完整工作流程
用户设置查询条件 → 点击“保存模板”
→ 填写模板名称 → 选择是否默认
→ 前端序列化查询条件为JSON → POST请求发送到后端
→ 后端验证并保存到数据库
→ 返回成功提示 → 前端刷新模板列表
用户选择模板 → 选择下拉框 → 触发change事件
→ 请求后端加载对应ID的模板
→ 获取JSON数据 → 恢复到报表查询界面
→ 自动执行查询
这套方案适用于大多数PHP报表系统的模板管理需求,可根据实际业务复杂度进行适当调整。