数据字典不该放 config/ 目录里,因其属于业务逻辑而非运行时配置;应使用 php 类封装为常量,并提供 label() 方法结合 laravel 本地化机制支持多语言,避免数据库存储导致的性能与维护问题。

数据字典该不该放 config/ 目录里?
不该。Laravel 的 config/ 是为运行时配置服务的,比如数据库连接、缓存驱动这些「环境相关」或「部署后可能变」的值;而数据字典(如订单状态:1→待支付、2→已发货)本质是业务语义映射,属于「代码逻辑的一部分」,硬塞进 config 会导致两个问题:一是修改后需清缓存(php artisan config:clear),二是 IDE 无法跳转、类型提示失效、测试难 mock。
更合理的做法是用 PHP 类封装,例如 StatusDictionary 或按模块拆成 OrderStatus、UserRole 等。
- 把键值对定义为
public const,IDE 可自动补全,调用时写OrderStatus::PAID比写字符串'paid'安全得多 - 加一个
map()静态方法返回完整数组,方便 Blade 或 API 返回字典列表 - 避免用
define()或全局常量——没法命名空间,容易冲突
怎么让数据字典支持多语言描述?
直接在类里写死中文不是好主意。但也不建议每条状态都去查 lang/ 文件——性能差、维护散、翻译和逻辑脱节。
折中方案:字典类保留英文 key 和基础中文 label,再通过 Laravel 的本地化机制做覆盖。
- 在
OrderStatus中定义public const PENDING = 'pending';,同时提供label()方法,默认返回__('order.status.pending') - 对应翻译文件
resources/lang/zh/order.php里写'pending' => '待处理',英文版同理 - 关键点:不要在字典类里用
trans()直接取值,否则单元测试时无法控制 locale;而是延迟到调用方决定是否翻译
DB::table('sys_dict')->get() 这种方式为什么越来越危险?
早期项目爱把字典存在数据库里,图个“后台可配”。但实际中,90% 的字典从上线就没改过,却因此引入了三重负担:每次读要查库(哪怕加了 Redis 缓存)、迁移要写 seed、新增字段还得改 model 和验证规则。
- 如果真需要动态管理(比如运营后台增删标签),单独建一张轻量表 + 对应 Resource 控制器即可,别让核心业务逻辑依赖它
- 所有接口返回字典列表时,务必用
cache()->remember()包一层,TTL 设为数小时,避免高频请求打穿 DB - 特别注意:数据库字典一旦被用于 SQL
WHERE status = ?,就和代码里的OrderStatus::SHIPPED值必须严格一致——这种隐式耦合极易出错,建议用 Eloquent 的casts或访问器强制转换
常量集中维护时,config/app.php 里该放什么?
只放真正跨模块、且与环境强相关的开关型配置,比如 enable_third_party_analytics、default_timezone。别把业务常量(如短信模板 ID、微信公众号 appid)全堆进去。
- 第三方服务凭证类配置,必须走
.env+config(),不能写死在 PHP 类里 - 如果多个配置项逻辑相关(如阿里云 OSS 的
endpoint/bucket/region),建议封装成一个OssConfig类,构造函数接收config('oss')数组,内部做 required 字段校验 - 所有 config 值默认应设为
null或空字符串,避免config('xxx.timeout')返回0导致意外超时
最麻烦的不是怎么存,而是哪天要改一个状态名,得同时搜 config/、lang/、数据库 seed、前端 JS 枚举、Swagger 注释……漏一处,线上就报错。所以一开始就把边界划清楚:PHP 类管语义,config 管行为,数据库只管真需要运行时变更的数据。











