Laravel 基于单一维度表实现多属性morphTo多态关联的问题
错误根因
你的关联方向写反了,morphTo 是多态关联中被关联端(也就是维度表对应的模型)要使用的方法,你在 Width 子类中定义 widthable 的 morphTo 关联时,Laravel 会默认去你的主业务表(比如商品表、设备表)找 widthable_id、widthable_type 字段,自然会抛出字段不存在的错误。
实现方案
1. 维度表结构设计
首先确保你的 dimensions 表包含以下字段:
id主键axis枚举类型,值为width/length/height/weight,用于区分维度类型valuedecimal类型,存储维度数值dimensionable_type多态关联类型字段dimensionable_id多态关联ID字段- 通用的
created_at/updated_at时间字段
2. 定义抽象基础维度模型
abstract class Dimension extends Model { // 所有子类共用同一张维度表 protected $table = 'dimensions'; // 被关联的多态方法,所有子类复用 public function dimensionable() { return $this->morphTo(); } // 子类必须实现该方法,返回自己对应的axis枚举值 abstract protected function getAxisValue(): string; // 全局作用域:子类查询时自动筛选对应axis类型的记录 protected static function booted() { static::addGlobalScope('axis', function ($query) { $query->where('axis', (new static)->getAxisValue()); }); } // 保存钩子:写入数据时自动填充axis字段,无需手动赋值 protected static function saving($model) { $model->axis = $model->getAxisValue(); } }
3. 实现四个维度子类
每个子类只需要定义自己对应的axis值即可,不需要写任何重复逻辑:
// 宽度子类 class Width extends Dimension { protected function getAxisValue(): string { return 'width'; } } // 长度子类 class Length extends Dimension { protected function getAxisValue(): string { return 'length'; } } // 高度子类 class Height extends Dimension { protected function getAxisValue(): string { return 'height'; } } // 重量子类 class Weight extends Dimension { protected function getAxisValue(): string { return 'weight'; } }
4. 在业务模型中定义关联
在需要关联维度的业务模型(比如商品模型 Product)中,用 morphOne 定义四个关联即可:
class Product extends Model { public function width() { return $this->morphOne(Width::class, 'dimensionable'); } public function length() { return $this->morphOne(Length::class, 'dimensionable'); } public function height() { return $this->morphOne(Height::class, 'dimensionable'); } public function weight() { return $this->morphOne(Weight::class, 'dimensionable'); } }
使用示例
// 写入数据,不需要手动指定axis字段,会自动填充 $product = Product::find(1); $product->width()->create(['value' => 25.5]); $product->weight()->create(['value' => 1.2]); // 读取数据,自动筛选对应维度类型的记录 echo $product->width->value; // 输出25.5 echo $product->weight->value; // 输出1.2
内容的提问来源于stack exchange,提问作者Scott
相关产品推荐
相关产品推荐

