Repository: laravel/framework (php)

Issue:
When updating with a qualified updated_at column, the current time is still persisted
<!-- DO NOT THROW THIS AWAY -->
<!-- Fill out the FULL versions with patch versions -->

- Laravel Version: 8.64.0
- PHP Version: 7.4
- Database Driver & Version: MySQL 8.0.20

### Description:

When the `updated_at` column is manually updated using a qualified column name, the current time is still persisted. If the column name is not qualified, it works as expected.


### Steps To Reproduce:

This works:
```php
MyModel::whereIn('my_model.id', $ids)->update(['updated_at' => $date]);
```

But this results in the `updated_at` column being set to the current time rather than the provided time:
```php
MyModel::whereIn('my_model.id', $ids)->update(['my_model.updated_at' => $time]);
```

<!-- If possible, please provide a GitHub repository to demonstrate your issue -->
<!-- laravel new bug-report --github="--public" -->


Pull request description:
[8.x] fix updated with provided qualified `updated_at`
<!--
Please only send a pull request to branches that are currently supported: https://laravel.com/docs/releases#support-policy 

If you are unsure which branch your pull request should be sent to, please read: https://laravel.com/docs/contributions#which-branch

Pull requests without a descriptive title, thorough description, or tests will be closed.

In addition, please describe the benefit to end users; the reasons it does not break any existing features; how it makes building web applications easier, etc.
-->
Fixes #41128 

This work as expected:

~~~php
User::query()->update(['updated_at' => null]);
~~~

While this does not:

~~~php
User::query()->update(['users.updated_at' => null]);
~~~

This PR:

- Fixes the `Eloquent\Builder@addUpdatedAtColumn` to verify for a qualified updated account
- Adds two relevant tests that would not pass without the changes proposed in this PR

Notes:

- As this is a bug fix I targeted branch 8.x
- The reason I used `Arr::get(...)` instead of the `null` coalescing operator is to allow users to specify a `null` value as they can when not qualifying the `updated_at` column


Patch:
diff --git a/src/Illuminate/Database/Eloquent/Builder.php b/src/Illuminate/Database/Eloquent/Builder.php
index c98e76cf7c..babd44b857 100755
--- a/src/Illuminate/Database/Eloquent/Builder.php
+++ b/src/Illuminate/Database/Eloquent/Builder.php
@@ -1004,7 +1004,7 @@ protected function addUpdatedAtColumn(array $values)
 
         $qualifiedColumn = end($segments).'.'.$column;
 
-        $values[$qualifiedColumn] = $values[$column];
+        $values[$qualifiedColumn] = Arr::get($values, $qualifiedColumn, $values[$column]);
 
         unset($values[$column]);
 


Test patch:
diff --git a/tests/Database/DatabaseEloquentBuilderTest.php b/tests/Database/DatabaseEloquentBuilderTest.php
index 863344c4ec..63f9b08de4 100755
--- a/tests/Database/DatabaseEloquentBuilderTest.php
+++ b/tests/Database/DatabaseEloquentBuilderTest.php
@@ -1683,6 +1683,20 @@ public function testUpdateWithTimestampValue()
         $this->assertEquals(1, $result);
     }
 
+    public function testUpdateWithQualifiedTimestampValue()
+    {
+        $query = new BaseBuilder(m::mock(ConnectionInterface::class), new Grammar, m::mock(Processor::class));
+        $builder = new Builder($query);
+        $model = new EloquentBuilderTestStub;
+        $this->mockConnectionForModel($model, '');
+        $builder->setModel($model);
+        $builder->getConnection()->shouldReceive('update')->once()
+            ->with('update "table" set "table"."foo" = ?, "table"."updated_at" = ?', ['bar', null])->andReturn(1);
+
+        $result = $builder->update(['table.foo' => 'bar', 'table.updated_at' => null]);
+        $this->assertEquals(1, $result);
+    }
+
     public function testUpdateWithoutTimestamp()
     {
         $query = new BaseBuilder(m::mock(ConnectionInterface::class), new Grammar, m::mock(Processor::class));
@@ -1715,6 +1729,24 @@ public function testUpdateWithAlias()
         Carbon::setTestNow(null);
     }
 
+    public function testUpdateWithAliasWithQualifiedTimestampValue()
+    {
+        Carbon::setTestNow($now = '2017-10-10 10:10:10');
+
+        $query = new BaseBuilder(m::mock(ConnectionInterface::class), new Grammar, m::mock(Processor::class));
+        $builder = new Builder($query);
+        $model = new EloquentBuilderTestStub;
+        $this->mockConnectionForModel($model, '');
+        $builder->setModel($model);
+        $builder->getConnection()->shouldReceive('update')->once()
+            ->with('update "table" as "alias" set "foo" = ?, "alias"."updated_at" = ?', ['bar', null])->andReturn(1);
+
+        $result = $builder->from('table as alias')->update(['foo' => 'bar', 'alias.updated_at' => null]);
+        $this->assertEquals(1, $result);
+
+        Carbon::setTestNow(null);
+    }
+
     public function testUpsert()
     {
         Carbon::setTestNow($now = '2017-10-10 10:10:10');