maxxxir's avatar

updating model in try/catch : model remembers faulty update values in the catch

i have a nested function calls to update some data in database , the column im going to update is unique in database and it may be updated with a duplicate value in that case i want to flag that model/row as duplicated

here is simplified version of my code without the nested function calls

public $defaultModel ;


function errorTest(){
    $this->defaultModel = $inprogress = Process::where('inprogress' , 1 )->first();
    try
    {
        $inprogress->update( [
            'deployment_id' => 666 ,
        ]);

    }
    catch (\Exception $exception)
    {
        $this->defaultModel->update([
            'inprogress' => 0  ,
            'error'=>'duplicate'
        ]);
    }
}

when i try to update the model with a duplicate value i keep getting laravel error page

  SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '666' for key 
  'process_deployment_id_unique'

at first i thought maybe my try/catch isnt working and i cant catch the error , but after playing around i found out the error is caused by catch section

        $this->defaultModel->update([
            'inprogress' => 0  ,
            'error'=>'duplicate'
        ]);

even though im not updating the unique column (deployment_id) in the catch , the model is still remembering the value given in the try body and trying to update deployment_id in the catch with the 666 value

crazy part is im not even using the same variable in the catch section ! it's like they are referencing each other

the only way around it is to do something like

        $model = $this->defaultModel->fresh();
        $model->update([
            'inprogress' => 0  ,
            'error'=>'duplicate'
        ]);

any idea why is this happening ?

0 likes
5 replies
tangtang's avatar

@maxxxir

when you initially retrieve the model from the database using the first() method, laravel stores the original attributes in the model instance. when you attempt to update the model using the update method, it will use the original attributes unless you reload or refresh the model using the fresh method.

maxxxir's avatar

@tangtang thanx for the reply , but the original value retrieved from db is null

i've created another table for test , as you can see the original value is null but in the catch it's updated with values given in try section

    Schema::create('unique_tests', function (Blueprint $table) {
        $table->id();
        $table->string('unique_value')->unique()->nullable();
        $table->string('other_value')->nullable();
        $table->timestamps();
    });

ran this code

    $uniqueTest = UniqueTest::find(1);
    dump('original values ->' , $uniqueTest);

    try
    {
        $uniqueTest->update(['unique_value' => 123 ]);

    }
    catch (\Exception $exception)
    {
        dump($exception->getMessage());
        dd('catch values ->' , $uniqueTest);
    }

here is the result

"original values ->" // app\Http\Controllers\TestController.php:25
App\Models\UniqueTest {#660 ▼ // app\Http\Controllers\TestController.php:25

  #attributes: array:5 [▼
	"id" => 1
	"unique_value" => null
	"other_value" => "default"
	"created_at" => "2023-11-03 06:06:55"
	"updated_at" => "2023-11-03 06:06:55"
  ]
  #original: array:5 [▼
	"id" => 1
	"unique_value" => null
	"other_value" => "default"
	"created_at" => "2023-11-03 06:06:55"
	"updated_at" => "2023-11-03 06:06:55"
  ]

}
"SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '123' for key 'unique_tests_unique_value_unique'
"catch values ->" 
App\Models\UniqueTest {#660 ▼ // app\Http\Controllers\TestController.php:35
  #attributes: array:5 [▼
	"id" => 1
	"unique_value" => 123
	"other_value" => "default"
	"created_at" => "2023-11-03 06:06:55"
	"updated_at" => "2023-11-03 06:15:30"
  ]
  #original: array:5 [▼
	"id" => 1
	"unique_value" => null
	"other_value" => "default"
	"created_at" => "2023-11-03 06:06:55"
	"updated_at" => "2023-11-03 06:06:55"
  ]
}
tangtang's avatar

@maxxxir

that's odd. already tested your code and it work well.

got the error like your case if I add the same unique_value like 123 in other record and I try to update other row with the 123, the error will appear like you do.

you sure you dont have any other row unique_value with 123 value ?

maxxxir's avatar

@tangtang yes there is another record (id=2) with the unique_value = 123 in database im trying to update record with id=1 to unique_value = 123 and getting duplicate error is expected (in try block)

im not asking why i cant update the record with duplicate values , im asking why my model with (id=1) is remembering the 123 value given in the failed update attempt in try block when im update other columns in the catch

im not trying to update unique_value in the catch and the value of original record in database (for id=1) is null so why its adding unique_value = 123 to the catch query ?

tangtang's avatar
tangtang
Best Answer
Level 6

@maxxxir

I see what you mean now. I missread the first question.

when you attempt to update a model's attribute, even if the update operation fails due to a unique constraint violation, laravel updates the model instance's attribute values in memory to reflect the attempted changes. This is a feature of eloquent that allows you to work with the attempted values even if they were not saved to the database.

The #original property shows the values as they were when the model was initially loaded from the database, which includes unique_value as null.

The #attributes property shows the current in-memory values of the model, which reflect the attempted update with unique_value set to 123, even though the update operation failed.

Please or to participate in this conversation.