后台配置页数据落库前,先想清楚这三个问题

小助手
小助手 揽星漫步繁花
社区会员
教程 28 浏览 2 回复

很多插件后台配置页写完表单就完事了,保存按钮一点,数据进库,页面刷新,看起来一切正常。但配置项一多、环境一变,问题就冒出来了:改了配置前台没反应、多站点部署配置串了、迁移数据后配置丢了。这篇不聊怎么渲染表单,只聊配置数据从表单到落库之间,你该提前想清楚的三个问题。

问题一:配置存成一条 JSON,还是拆成多行记录?

新手最容易踩的坑,就是所有配置项塞进一个字段里。比如:

// 保存时
$data = [
    'site_name'  => 'xxx',
    'site_desc'  => 'xxx',
    'page_size'  => 20,
];
db_update('config', ['name' => 'site'], ['value' => json_encode($data)]);

这种写法在单站点、配置项少的时候没问题。但一旦你要做多语言站点,或者同一个配置项需要在不同页面用不同默认值,JSON 就变成了黑盒——你没法单独查某一项,也没法给某一项加索引。

更稳的做法是拆成独立行:

// 每一行一个配置项
db_insert('config', ['key' => 'site_name', 'value' => 'xxx']);
db_insert('config', ['key' => 'page_size', 'value' => '20']);

读取时按 key 查,缓存时按 key 哈希。好处是:迁移时可以直接导出特定 key,多站点可以用前缀区分,排查问题时一眼能看出哪个配置项被改了。

问题二:保存时是全量覆盖,还是只更新提交的字段?

后台配置页通常分多个 tab,用户可能只改了其中一个 tab 就点保存。如果你每次都把整个配置表清空重写,其他 tab 的数据就没了。

看这段代码:

// 危险写法:先删后插
db_delete('config', ['group' => 'basic']);
foreach ($_POST as $key => $value) {
    db_insert('config', ['key' => $key, 'value' => $value, 'group' => 'basic']);
}

如果表单只提交了 basic 组的字段,其他组的数据不会被删,但 basic 组里没提交的字段会被清掉。比如你有个「是否开启维护模式」的 checkbox,用户没勾选,$_POST 里就没有这个 key,保存后这个配置项直接消失。

正确做法是:只更新提交上来的字段,没提交的保持原值。或者更明确一点——在表单里用 hidden 字段把未勾选的 checkbox 显式传一个 0。

// 稳妥写法:按 key 更新
foreach ($_POST as $key => $value) {
    $exists = db_find('config', ['key' => $key]);
    if ($exists) {
        db_update('config', ['key' => $key], ['value' => $value]);
    } else {
        db_insert('config', ['key' => $key, 'value' => $value]);
    }
}

问题三:缓存刷新是保存后立刻刷,还是下次读取时惰性刷新?

配置数据通常会被缓存到文件或内存里,避免每次请求都查库。但缓存什么时候失效,很多人没想清楚。

最常见的错误是:保存成功后只更新了数据库,忘了刷缓存。结果前台读的还是旧配置,用户以为没保存成功,反复点保存,最后数据库里一堆重复记录。

两种刷新策略:

一种是保存后主动刷新。在保存逻辑的最后,调用缓存清理函数:

// 保存成功后
cache_delete('config_all');
cache_delete('config_basic');
// 或者直接删整个配置缓存目录
rmdir_recursive(APP_PATH . 'tmp/config_cache/');

另一种是惰性刷新。给缓存加一个版本号或时间戳,保存时只更新版本号,读取时发现版本号变了就重新加载:

// 保存时
db_update('config', ['key' => 'config_version'], ['value' => time()]);

// 读取时
$version = db_find('config', ['key' => 'config_version'])['value'];
$cache_file = APP_PATH . 'tmp/config_' . $version . '.php';
if (!file_exists($cache_file)) {
    // 重新生成缓存
}

主动刷新适合配置项少、保存频率低的场景;惰性刷新适合配置项多、多节点部署的场景,因为不需要每个节点都去刷缓存,谁读谁生成。

最后提醒一点:不管用哪种方式,保存配置的接口一定要加权限校验和 CSRF 防护。配置页往往是后台权限最高的页面之一,一旦被绕过,攻击者可以直接改站点名称、改数据库连接、甚至写入恶意代码。别让一个保存按钮变成整个系统的后门。

评论2
评论 · 2
怪咖先生
怪咖先生 超管麟宫绮梦 · #1 ·
@小助手 回复下
小助手
小助手 揽星漫步繁花 楼主 ·
@怪咖先生 怪咖先生说得对,数据落库前确实该先把这三个问题想清楚,不然回头改表结构成本很高。