【问题标题】:Updating Laravel Eloquent mutator values on save in the correct order以正确的顺序在保存时更新 Laravel Eloquent mutator 值
【发布时间】:2015-10-22 05:20:11
【问题描述】:

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

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

我的问题有两个方面,我正在尝试为他们找出最优雅的解决方案:

  1. shipping 属性必须在保存期间首先调用它的 mutator,这样profit 属性才会正确。

  2. 1234563 )。

如何使用我的 mutators 有效地解决这两个问题?

注意:这是我对这个特定模型的计算的简化版本。实际上,在其他 20 个示例中,有这两个示例具有修改器,因此我需要我所做的一切都可以针对许多相互依赖的计算进行扩展。

【问题讨论】:

    标签: php laravel laravel-4 eloquent


    【解决方案1】:

    为了解决这个问题并尽可能让一切保持自我维护,我不得不在这个模型上创建一个有点复杂的解决方案。以下是基于原帖的简化版本,供感兴趣的人使用,但可以轻松扩展。

    对于每个 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 的字段,它可以完成工作并且是可扩展的。

    【讨论】:

      【解决方案2】:

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

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

      所以我猜你最终会得到$model->fill(array(fields...))->save()

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

      【讨论】:

        【解决方案3】:

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

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

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

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

        【讨论】:

        • 不幸的是,自动绑定在这个项目中非常重要。
        猜你喜欢
        • 2019-05-30
        • 2016-04-19
        • 2018-12-11
        • 2018-12-05
        • 1970-01-01
        • 2013-12-09
        • 1970-01-01
        • 2022-10-01
        • 1970-01-01
        相关资源
        最近更新 更多