假设一个表中有五个字段:price、cost、handling、shipping、profit
字段shippingprofit有变元,因为它们是基于计算的。

public function setShippingAttribute($value) {
    $this->attributes['shipping'] = $this->attributes['handling'] + $value;
}

public function setProfitAttribute($value) {
    $this->attributes['profit'] = $this->attributes['price'] - $this->attributes['shipping'] - $this->attributes['cost'];
}

我的问题有两个方面,我正试图为他们找出最优雅的解决方案:
shipping属性必须在保存期间首先调用它的mutator,这样profit属性就正确了。
每次pricecosthandling属性发生更改时,我都需要profit和/或shipping属性也进行更新(同样,按正确的顺序)。
我如何才能有效地解决这两个问题与我的变异?
注:这是我对这个特定模型的计算的简化版本。事实上,在另外20个例子中,有两个例子有变异,所以我需要做任何事情来针对许多相互依赖的计算进行扩展。

最佳答案

为了解决这个问题,并尽可能保持所有的自我维护,我必须在这个模型上创建一个比较复杂的解决方案。下面是基于原始帖子的简化版本,供感兴趣的人使用,但可以很容易地进行扩展。
对于每个mutator,我都创建了访问器,以确保每次对数据的新请求都进行计算:

public function getShippingAttribute($value) {
    return $this->getAttribute('handling') + $value;
}

public function getProfitAttribute($value) {
    return $this->getAttribute('price') - $this->getAttribute('shipping') - $this->getAttribute('cost');
}

访问器使用getAttribute方法而不是直接的属性访问,以确保自动调用幕后的任何其他变异器或关系。
然后使用mutator来确保数据缓存在数据库中,以便将来对它们进行任何查询(这与仅仅附加数据不同):
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 = [];
    });
}

它的结构方式,在上面的setAttribute方法中对saving的每次调用都应该级联向下触发每个其他相关的调制器。
如果我注意到有任何更新不正常,逻辑可能需要进行一些调整,但到目前为止,它完成了任务,而且如果我需要添加更多的带变异的字段,它是可伸缩的。

09-25 18:05