在保存时以正确的顺序更新Laravel Eloquent mutator值

时间:2015-10-22 05:20:11

标签: php laravel laravel-4 eloquent

假设我在表格中有五个字段:价格,成本,处理,运输,利润

字段shippingprofit具有mutators,因为它们基于计算。

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'];
}

我的问题是双重的,我正试图找出最优雅的解决方案:

  1. shipping属性始终必须在保存期间首先调用它的mutator,以便profit属性正确。

  2. 每次pricecosthandling属性发生变化时,我都需要profit和/或shipping属性也要更新(再次,按照正确的顺序)。

  3. 如何通过我的mutators有效解决这两个问题?

    注意:这是我在此特定型号上进行的计算的简化版本。实际上,在其他20个有变异器的例子中,有两个例子,所以我需要做的任何事情都可以对许多相互依赖的计算进行扩展。

3 个答案:

答案 0 :(得分:0)

也许您可以扩展Laravel的cvNamedWindow类并覆盖(并使用)Model方法。

如果您查看代码,您会看到fill来电fill。我可能会覆盖这些方法,按照我需要更新的顺序返回字段。

所以我想你最终会得到fillableFromArray

希望这会有所帮助。 (供您参考,我使用的是Laravel 4.1)

答案 1 :(得分:0)

属于模型的所有内容应仅负责数据存储。应将计算封装到自己的域对象中。这样您就不会将存储逻辑与计算混合在一起,这样它们就可以相互独立 - 这对于维护和单元测试非常有用。

话虽如此,处理此类情况的更好方法是create a custom facade并将所有计算封装在那里。

按照这种方式,你不需要破解Laravel的核心来实现它并创建紧密耦合。

但是,这种方法有一个小缺点 - 您无法利用自动表单模型绑定,因此您需要手动提供值。

答案 2 :(得分:-1)

为了解决这个问题并尽可能保持自我维护,我不得不在这个模型上创建一个有点复杂的解决方案。以下是任何感兴趣的人根据原始帖子的简化版本,但它可以很容易地扩展。

对于每个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或关系。

然后使用mutators确保数据缓存在数据库中,以便将来对它们进行任何查询(这与仅添加数据相反):

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的每次调用都应该级联,触发所有其他相关的mutator。

逻辑可能需要稍微调整,如果我注意到任何更新乱序,但到目前为止它完成了工作并且可扩展,如果我需要添加更多字段与mutator。