eComEvo eComEvo - 1 year ago 192
PHP Question

Updating Laravel Eloquent mutator values on save in the correct order

Suppose I have five fields in a table: price, cost, handling, shipping, profit

The fields

have mutators because they are based off calculations.

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

My problem here is two-fold, and I'm trying to figure out the most elegant solution for them:

  1. The
    attribute always has to have it's mutator called first during a save so the
    attribute will be correct.

  2. Each time there is a change on the
    attributes, I need the
    attributes to update as well (again, in proper order).

How can I effectively address these two issues with my mutators?

NOTE: The is a simplified version of the calculations I have on this particular model. In reality, there are these two examples out of 20 others that have mutators, so I need whatever I do to be scalable against many interdependent calculations.

Answer Source

To solve this and keep everything as self maintaining as possible, I had to create a somewhat complex solution on this model. Below is the simplified version based on the original post for anyone interested, but it can be easily expanded.

For each of the mutators, I created accessors to make sure calculations are always done for each new request for the data:

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

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

The accessors use the getAttribute method instead of direct attribute access to ensure that any other mutators or relations behind the scenes get called automatically.

The mutators are then used to ensure the data is cached in the database for any future queries against them (this, as opposed to just appending the data):

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

public function setProfitAttribute($value) {
    $this->attributes['profit'] = $this->getProfitAttribute($value);

To make sure all dependent fields are updated when there is a change, I added this to the model:

protected static $mutatorDependants = [
    'handling'        => [
    'price'              => [
    'cost'              => [
    'shipping'              => [

protected $mutatorCalled = []; // Added to prevent unnecessary mutator calls.

If any of the above fields are changed, then their dependents are automatically recalculated on the saving event:

public static function 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 = [];

The way it is structured, every call to setAttribute in thesaving method above should cascade down triggering every other related mutator.

Logic may need a little tweaking if I notice anything updating out of order, but so far it does the job and is scalable if I need to add more fields with mutators.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download