PHP项目自定义报表如何保存查询模板

wen PHP项目 27

本文目录导读:

PHP项目自定义报表如何保存查询模板

  1. 数据库设计
  2. 后端PHP实现
  3. 前端JavaScript实现
  4. 前端UI交互参考
  5. 高级功能扩展
  6. 安全注意事项
  7. 完整工作流程

在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;
}

安全注意事项

  1. 序列化安全:始终使用 json_encode/json_decode,不要使用 serialize()
  2. 权限校验:加载模板时必须校验用户所有权
  3. SQL注入防护:使用参数化查询
  4. XSS防护:前端显示模板名称时进行HTML转义
  5. 数据验证:保存时验证查询结构的合法性,防止恶意数据

完整工作流程

用户设置查询条件 → 点击“保存模板”
→ 填写模板名称 → 选择是否默认
→ 前端序列化查询条件为JSON → POST请求发送到后端
→ 后端验证并保存到数据库
→ 返回成功提示 → 前端刷新模板列表
用户选择模板 → 选择下拉框 → 触发change事件
→ 请求后端加载对应ID的模板
→ 获取JSON数据 → 恢复到报表查询界面
→ 自动执行查询

这套方案适用于大多数PHP报表系统的模板管理需求,可根据实际业务复杂度进行适当调整。

抱歉,评论功能暂时关闭!