thinkphp 6.0 可通过三张表(eav_entities、eav_attributes、eav_values)+ 模型关联、动态验证器、类型化存取实现轻量灵活的 eav 模型,无需原生 sql,支持属性动态增删、类型校验与高效查询。

在 ThinkPHP 6.0 中实现真正灵活的 EAV(实体-属性-值)模型,关键不是堆砌三层表结构,而是让“实体动态挂属性、属性带类型校验、值按需存取”形成闭环。TP6 本身不内置 EAV 支持,但借助其模型能力、验证器机制和查询构建器,完全可以落地一套轻量、可维护、贴合业务的 EAV 方案。
设计三张核心数据表(适配 TP6 模型约定)
避免手写原始 SQL,直接用 TP6 的 migrate 工具建表,字段命名遵循其默认规范(如主键用 id,时间戳用 create_time):
-
eav_entities:存储实体基本信息,如
id、type(区分 product/user/order)、status、create_time -
eav_attributes:定义属性元数据,含
id、code(唯一标识,如color、cpu_brand)、name、data_type(varchar/int/decimal/datetime)、is_required、sort -
eav_values:实际存储值,字段为
id、entity_id、attribute_id、value_text、value_int、value_decimal、value_datetime—— 按类型分列,避免全用 text 带来类型丢失和查询低效
用模型关联 + 属性预加载组织数据读取
不靠 JOIN 拼大 SQL,而是分步加载、组合结果,更可控也更易扩展:
- 实体模型(
EavEntity)定义attributes()关联方法,返回hasManyThrough或自定义关系,指向EavValue并通过eav_attributes补充属性信息 - 查询单个实体时:
EavEntity::with(['attributes' => function ($q) { $q->where('status', 1); }])->find($id),一次查出实体 + 启用中的全部属性及对应值 - 若需按属性筛选(如“查所有颜色=红色的产品”),先查
EavValue获取匹配的entity_id列表,再用whereIn('id', $ids)查实体,避免复杂 JOIN
验证器驱动属性级校验逻辑
把属性规则下沉到验证层,而非硬编码在控制器里:
- 为每个
data_type编写对应验证规则:如varchar类型属性启用max:255,int类型启用number|between:0,999999 - 利用 TP6 验证器的
scene和append功能,支持跨属性联动校验(例如:当attribute_code = 'has_warranty'且值为1时,强制校验warranty_months属性是否存在且为正整数) - 验证规则动态生成:从
eav_attributes表读取当前实体类型下所有启用属性,拼装成$rule数组传入验证器,实现“增删属性即生效”,无需改代码
写入时按类型路由到对应字段
避免 value_text 一字段存所有,提升查询准确性和索引效率:
- 在
EavValue模型的save()前置逻辑中,根据attribute.data_type自动选择写入value_text、value_int等字段 - 对日期类属性,接收参数统一为字符串(如
'2026-07-08'),在模型中转为strtotime()存入value_datetime;读取时再格式化输出 - 空值处理明确:若属性为
is_required=0且提交为空,则不插入eav_values记录(而非存 NULL),保持稀疏性











