为了解决这个问题并尽可能让一切保持自我维护,我不得不在这个模型上创建一个有点复杂的解决方案。以下是基于原帖的简化版本,供感兴趣的人使用,但可以轻松扩展。
对于每个 mutators,我创建了访问器来确保每次新的数据请求都进行计算:
public function getShippingAttribute($value) {
return $this->getAttribute('handling') + $value;
}
public function getProfitAttribute($value) {
return $this->getAttribute('price') - $this->getAttribute('shipping') - $this->getAttribute('cost');
}
访问器使用getAttribute 方法而不是直接访问属性来确保自动调用幕后的任何其他修改器或关系。
然后使用突变器确保数据缓存在数据库中,以供将来对它们进行任何查询(这与仅附加数据相反):
public function setShippingAttribute($value) {
$this->attributes['shipping'] = $this->getShippingAttribute($value);
}
public function setProfitAttribute($value) {
$this->attributes['profit'] = $this->getProfitAttribute($value);
}
为了确保在发生更改时更新所有依赖字段,我将其添加到模型中:
protected static $mutatorDependants = [
'handling' => [
'shipping'
],
'price' => [
'profit'
],
'cost' => [
'profit'
],
'shipping' => [
'profit'
]
];
protected $mutatorCalled = []; // Added to prevent unnecessary mutator calls.
如果上述任何字段发生更改,则在 saving 事件上自动重新计算其依赖项:
public static function boot()
{
parent::boot();
static::saving(function ($model) {
/** @var \Item $model */
foreach ($model->getDirty() as $key => $value) {
// Ensure mutators that depend on changed fields are also updated.
if (isset(self::$mutatorDependants[$key])) {
foreach (self::$mutatorDependants[$key] as $field) {
if (!in_array($field, $model->mutatorCalled)) {
$model->setAttribute($field, $model->attributes[$field]);
$model->mutatorCalled[] = $field;
}
}
}
}
$model->mutatorCalled = [];
});
}
它的结构方式是,在上述saving 方法中对setAttribute 的每次调用都应该级联触发所有其他相关的mutator。
如果我注意到任何更新乱序,逻辑可能需要稍作调整,但到目前为止,如果我需要添加更多带有 mutators 的字段,它可以完成工作并且是可扩展的。