jamie.buck's avatar

Updated_at changed but model field not changed

I have a simple piece of code which retrieves a model, sets a boolean field on the model to true and then saves the model. Normally this works fine. However, occasionally the field is not changed but the updated_at is changed.

$member = $this->member::findByCode((int) Auth::user()->name);
$member->funds()->sync($newFunds);
$member->made_choice = true;
$member->save();

(Yes, I know the authorised user's name is an integer, not my choice).

I cannot, for the life of me, work out where to start debugging. made_choice is defined as a boolean in the migration and there is no $casts on the model. There is no consistency as to when this fails and when it succeeds. No errors are appearing in the logs. Nothing to indicate what is happening.

 $table->boolean('made_choice')->default(false)->after('member_code');

I'm sure I'm missing something obvious but what?

0 likes
4 replies
bobbybouwmann's avatar

Did you set a fillable or guarded property on the member model?

Also, the updated_at is only updated whenever there is a change on the model. If there is no change, the datetime is not updated.

jamie.buck's avatar

The model is set to

  protected $guarded = [];

This is why I'm so confused. I realise the updated_at is only changed when the model is changed. The model is being changed but the field that is changing is not persisting on the database, the updated_at is.

If it were happening consistently I would suspect something about boolean casting but it is not consistent.

jamie.buck's avatar

Well, after some head scratching I decided to add some log statements to the code to see if there was a problem with the boolean casting. Now something even stranger has come to light.

 $member = $this->member::findByCode((int) Auth::user()->name);
 \Log::info('Member: ' . $member->member_code);
 \Log::info('Original made choice:' . $member->made_choice);
$member->made_choice = true;
$member->save();
\Log::info('In lifestyle: ' . $member->made_choice);

For those records which updated successfully, the time on all 3 log statements match the updated_at time on the model in the database and the made_choice on the model matches the log.

However, for those records which failed, the time on all 3 log statements, while the same, is a minute or so BEFORE the updated_at time on the model in the database and the model does not appear to have changed on the database apart from the updated_at field.

So to sum up:

  1. The log appears to be being written before the model updated_at on the database
  2. The made_choice is not being set on the database.
  3. The log is showing made_choice correctly.

I'm leaning towards an issue with the database server now but it's all confusing.

guizo's avatar

If you are using a database that stores booleans as integers, when you set $member->made_choice = true;, Eloquent will assume that there were changes because it will compare true with 1 and therefore the updated_at column is updated.

To avoid this, you can cast this column to boolean in the Model:

    protected function casts(): array
    {
        return ['made_choice' => 'boolean'];
    }

More details here - https://medium.com/@gusiol/eloquent-model-timestamp-updated-without-changes-1972c91bb778.

Please or to participate in this conversation.