Laravel中MorphToMany关联在OrderProduct测试中异常求助
Laravel MorphToMany关联测试失败排查(OrderProduct模型)
问题背景
在Laravel项目中使用MorphToMany关联将Cost模型与Order、OrderProduct模型建立关联,相关代码如下:
迁移文件
Schema::create('costables', function (Blueprint $table) { $table->unsignedBigInteger('cost_id'); $table->morphs('costable'); $table->foreign('cost_id') ->references('id')->on('costs') ->onUpdate('cascade') ->onDelete('cascade'); });
Order模型(测试通过)
namespace Domain\Webshop\Models; use Domain\Customer\Models\Address; use Domain\Customer\Models\Member; use Domain\Product\Models\Cost; use Domain\Webshop\Database\Factories\OrderFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphToMany; class Order extends Model { protected $fillable = [ 'member_id', 'shipping_address_id', 'billing_address_id', 'status', ]; public static $rules = [ 'member_id' => 'required|integer', 'shipping_address_id' => 'sometimes|integer', 'billing_address_id' => 'nullable|integer', 'status' => 'required|integer', ]; protected $casts = [ 'member_id' => 'integer', 'shipping_address_id' => 'integer', 'billing_address_id' => 'integer', ]; protected $hidden = ['created_at', 'updated_at', 'deleted_at']; protected static function newFactory(): OrderFactory { return OrderFactory::new(); } public function member(): BelongsTo { return $this->belongsTo(Member::class, 'member_id'); } public function shippingAddress(): BelongsTo { return $this->belongsTo(Address::class, 'shipping_address_id'); } public function costs(): MorphToMany { return $this->morphToMany(Cost::class, 'costable'); } }
PHPUnit测试用例(Trait形式)
/** @test */ public function shouldHasCosts() { $item = $this->model::factory() ->hasCosts(1) ->create(); $this->assertInstanceOf( Cost::class, $item->first()->costs()->first(), "It should be defined a relation called costs on $this->model" ); }
测试用例对Order模型测试通过,但对OrderProduct模型测试失败,需排查问题原因。
排查与解决步骤
1. 确认OrderProduct模型已定义costs关联
这是最常见的遗漏点:必须在OrderProduct模型中添加与Order一致的costs关联方法,否则测试时无法找到关联关系:
namespace Domain\Webshop\Models; use Domain\Product\Models\Cost; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\MorphToMany; class OrderProduct extends Model { // ... 其他模型代码 public function costs(): MorphToMany { return $this->morphToMany(Cost::class, 'costable'); } }
2. 检查OrderProductFactory是否支持hasCosts关联
测试中使用了->hasCosts(1)语法,需要确保OrderProductFactory中已定义该关联方法,或者使用Laravel原生的工厂关联语法:
- 方法一:在OrderProductFactory中定义
hasCosts方法
public function hasCosts(int $count) { return $this->has(Cost::factory()->count($count), 'costs'); }
- 方法二:直接修改测试代码使用原生
has方法
$item = $this->model::factory() ->has(Cost::factory()->count(1), 'costs') ->create();
3. 验证costables表的costable_type字段值
Laravel的morphs字段会存储模型的完整类名,需确认OrderProduct的类名(包含命名空间)与costables表中存储的costable_type一致。可以在测试中临时添加打印语句排查:
dd($item->costable_type, get_class($this->model));
4. 确认OrderProduct模型的$table属性正确
如果OrderProduct对应的数据库表名不是Laravel默认的order_products,必须在模型中显式指定$table属性,否则关联时会找不到正确的表:
class OrderProduct extends Model { protected $table = 'custom_order_products_table'; // ... 其他代码 }
5. 检查测试类中$this->model的指向
确保使用该Trait的OrderProduct测试类中,$this->model被正确赋值为OrderProduct::class,避免指向错误的模型类。
内容的提问来源于stack exchange,提问作者Ferenc Bablena
相关产品推荐
相关产品推荐

