Framework: Model::increment/Model::decrement does not fire Model Events

Created on 13 Apr 2017  路  22Comments  路  Source: laravel/framework

  • Laravel Version: 5.3
  • PHP Version: 7.1.1
  • Database Driver & Version:

Description:

Is there any specific reason why Model::increment/Model::decrement does not fire update events?

Steps To Reproduce:

Just increment/decrement anything on a model and try to catch eloquent.updating:*/eloquent.updated:*

Most helpful comment

Why was this issue closed? This issue still exists.

All 22 comments

Please upgrade to 5.4 since we fixed a lot of the issues that was reported in 5.3

Sorry to say but its not fixed there, but i am not sure if it is bug or intended

@themsaid I also confirm this issue. Increment and Decrement function is not firing an event. You can reopen the issue.

@themsaid I debugged this, If I listen through DB::listen(), It is giving me query of increment and decrement. But If I listen through Event::listen('eloquent.*') It is not giving me anything.

Im pretty sure it has never fired an event as it doesn't call save, it calls increment/decrement directly on a builder.

Why was this issue closed? This issue still exists.

Anyone can help me?
I have this view post function
errorpost

I also make categories for posts :
errorcat

visit count will increase if we view posts.
If i view posts at homepage i don't get error. But if i try to view posts by categories and get this errror :
incr

Overriding the increment / decrement method works for me

Added this to my models in which I need the save event to trigger

<?php

namespace App\Traits;

trait IncrementDecrement
{
    public function increment($column, $amount = 1, array $extra = [])
    {
        $this->$column = $this->$column + $amount;

        $this->save();
    }

    public function decrement($column, $amount = 1, array $extra = [])
    {
        $this->$column = $this->$column - $amount;

        $this->save();
    }
}

```php
class SomeModel extends Model
{
use \App\Traits\IncrementDecrement;
}

I think you need to "save" the model after making changes, and then the event will be fired.

Instead of $post->increment(); try

$post->update([ 'views' => DB::row('views + 1') ])

I think anyway, not sure :)

I think, decrement and increment, should fire update events! It's updating the model.

Please reopen this issue.

I completely agree here.

Hey ... nothing seems to be moving here.
Can we have an "official" point of view ?
Can we expect such an improvement in the future OR is it not planed at all ?

Thanks a lot

+1

Any updates on this? @themsaid @taylorotwell

I am curious why an update to a model wouldn't trigger the model update events. I am specifically asking related to Scout. When I increment a comments like count value the comment instance in Algolia is now out of sync.

+1

I do want to add though that these increment and decrement functions have a specific use case, which is to solve concurrency issues when running multiple processes.

Trying to increment or decrement in PHP code using something like:

$model->update([
    'value' => $model->value + 1
]);

can occasionally cause issues depending on the time the $model is retrieved from the database. Since the time of retrieval is not the same as the time of updating, something might've changed in the meantime (like process A wants to change it from 1 to 2, but process B is running at the same time which also retrieved a 1 from the database, incrementing it to 2 (which should be 3)).

In sql (InnoDB) you can ensure that these queries are run one after another (so they are atomic).

After some extra thought I think I know why this is not currently implemented:

If the updating hook would be added to the increment/decrement function, you couldnt know the getDirty() value of your incrementing column when you retrieve it inside your updating callback, since you aren't "setting" the value, but "incrementing".

Since the query doesn't actually become change col_b to 50, but instead it will be executed as change col_b to col_b + 20 (you dont know the end result).

This is my way, it's not perfect but working.

Add this function to your model.

protected function incrementOrDecrement($column, $amount, $extra, $method)
{
    $this->{$column} = ($method == 'increment' ? $this->{$column} + $amount : $this->{$column} - $amount);
    $this->fireModelEvent('saving', false);
    $this->{$column} = ($method == 'increment' ? $this->{$column} - $amount : $this->{$column} + $amount);
    $result = parent::incrementOrDecrement($column, $amount, $extra, $method);
    $this->fireModelEvent('saved', false);
    return $result;
}

@themsaid @taylorotwell
This issue still exists.

@amir9480 what about updating/updated hooks? Read my last comment to see what the possible issue is.

@bert-w
First I change attribute on the model.

$this->{$column} = ($method == 'increment' ? $this->{$column} + $amount : $this->{$column} - $amount);

Then firing the "saving" event.
when the "saving" event done I return them to the original value.

You can also call "updating"/"updated" events.

I know it's not a perfect solution if multiple increments simultaneously happen.

It would be nice to mention this in the documentation ..
blah blah blah .. increment/decrement do not trigger events udating/updated .. =|
Spent time after refactoring to understand why observer broke =D
Very very dirty hack .. call the method $model->touch(); after...

Nobody is thinking this through. There is just complaining.

From my post on another issue:

Note that, sure, we can add the events to this method; however, there is no way to gurantee that the dirty / changed values are actually what the values are going to end up being in the database. We must always run this query as a query that looks like update table set column = column + 1 because multiple processes could be trying to increment the column at the same time.

Therefore, it is impossible for us to know what the new column value will actually be until after we run the query, so the "dirty" value in the updating event would always be a guesstimate. It could never be guaranteed to be correct.

Whether this matters to you depends on what you are doing in the event handler.

@taylorotwell

What about just saved / updated and not saving / updating?
Can we get value from the database after updating?
Just saved / updated gonna fit my need for most cases.

Or any other possible way to catch increment/decrement events inside an observer.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

PhiloNL picture PhiloNL  路  3Comments

klimentLambevski picture klimentLambevski  路  3Comments

fideloper picture fideloper  路  3Comments

ghost picture ghost  路  3Comments

RomainSauvaire picture RomainSauvaire  路  3Comments