Skip to content

Commit 19b4209

Browse files
authored
[10.x] Use model cast when builder created updated at value (#47942)
* Use model cast when builder created updated at value * lint * Support mutators and attributes * Force fill column
1 parent bfae965 commit 19b4209

File tree

2 files changed

+208
-4
lines changed

2 files changed

+208
-4
lines changed

src/Illuminate/Database/Eloquent/Builder.php

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,10 +1139,21 @@ protected function addUpdatedAtColumn(array $values)
11391139

11401140
$column = $this->model->getUpdatedAtColumn();
11411141

1142-
$values = array_merge(
1143-
[$column => $this->model->freshTimestampString()],
1144-
$values
1145-
);
1142+
if (! array_key_exists($column, $values)) {
1143+
$timestamp = $this->model->freshTimestampString();
1144+
1145+
if (
1146+
$this->model->hasSetMutator($column)
1147+
|| $this->model->hasAttributeSetMutator($column)
1148+
|| $this->model->hasCast($column)
1149+
) {
1150+
$timestamp = $this->model->newInstance()
1151+
->forceFill([$column => $timestamp])
1152+
->getAttributes()[$column];
1153+
}
1154+
1155+
$values = array_merge([$column => $timestamp], $values);
1156+
}
11461157

11471158
$segments = preg_split('/\s+as\s+/i', $this->query->from);
11481159

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Integration\Database\MySql;
4+
5+
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
6+
use Illuminate\Database\Eloquent\Casts\Attribute;
7+
use Illuminate\Database\Eloquent\Model;
8+
use Illuminate\Support\Carbon;
9+
use Illuminate\Support\Facades\Schema;
10+
11+
class EloquentCastTest extends MySqlTestCase
12+
{
13+
protected $driver = 'mysql';
14+
15+
protected function defineDatabaseMigrationsAfterDatabaseRefreshed()
16+
{
17+
Schema::create('users', function ($table) {
18+
$table->increments('id');
19+
$table->string('email')->unique();
20+
$table->integer('created_at');
21+
$table->integer('updated_at');
22+
});
23+
}
24+
25+
protected function destroyDatabaseMigrations()
26+
{
27+
Schema::drop('users');
28+
}
29+
30+
public function testItCastTimestampsCreatedByTheBuilderWhenTimeHasNotPassed()
31+
{
32+
Carbon::setTestNow(now());
33+
$createdAt = now()->timestamp;
34+
35+
$castUser = UserWithIntTimestampsViaCasts::create([
36+
'email' => fake()->unique()->email,
37+
]);
38+
$attributeUser = UserWithIntTimestampsViaAttribute::create([
39+
'email' => fake()->unique()->email,
40+
]);
41+
$mutatorUser = UserWithIntTimestampsViaMutator::create([
42+
'email' => fake()->unique()->email,
43+
]);
44+
45+
$this->assertSame($createdAt, $castUser->created_at->timestamp);
46+
$this->assertSame($createdAt, $castUser->updated_at->timestamp);
47+
$this->assertSame($createdAt, $attributeUser->created_at->timestamp);
48+
$this->assertSame($createdAt, $attributeUser->updated_at->timestamp);
49+
$this->assertSame($createdAt, $mutatorUser->created_at->timestamp);
50+
$this->assertSame($createdAt, $mutatorUser->updated_at->timestamp);
51+
52+
$castUser->update([
53+
'email' => fake()->unique()->email,
54+
]);
55+
$attributeUser->update([
56+
'email' => fake()->unique()->email,
57+
]);
58+
$mutatorUser->update([
59+
'email' => fake()->unique()->email,
60+
]);
61+
62+
$this->assertSame($createdAt, $castUser->created_at->timestamp);
63+
$this->assertSame($createdAt, $castUser->updated_at->timestamp);
64+
$this->assertSame($createdAt, $castUser->fresh()->updated_at->timestamp);
65+
$this->assertSame($createdAt, $attributeUser->created_at->timestamp);
66+
$this->assertSame($createdAt, $attributeUser->updated_at->timestamp);
67+
$this->assertSame($createdAt, $attributeUser->fresh()->updated_at->timestamp);
68+
$this->assertSame($createdAt, $mutatorUser->created_at->timestamp);
69+
$this->assertSame($createdAt, $mutatorUser->updated_at->timestamp);
70+
$this->assertSame($createdAt, $mutatorUser->fresh()->updated_at->timestamp);
71+
}
72+
73+
public function testItCastTimestampsCreatedByTheBuilderWhenTimeHasPassed()
74+
{
75+
Carbon::setTestNow(now());
76+
$createdAt = now()->timestamp;
77+
78+
$castUser = UserWithIntTimestampsViaCasts::create([
79+
'email' => fake()->unique()->email,
80+
]);
81+
$attributeUser = UserWithIntTimestampsViaAttribute::create([
82+
'email' => fake()->unique()->email,
83+
]);
84+
$mutatorUser = UserWithIntTimestampsViaMutator::create([
85+
'email' => fake()->unique()->email,
86+
]);
87+
88+
$this->assertSame($createdAt, $castUser->created_at->timestamp);
89+
$this->assertSame($createdAt, $castUser->updated_at->timestamp);
90+
$this->assertSame($createdAt, $attributeUser->created_at->timestamp);
91+
$this->assertSame($createdAt, $attributeUser->updated_at->timestamp);
92+
$this->assertSame($createdAt, $mutatorUser->created_at->timestamp);
93+
$this->assertSame($createdAt, $mutatorUser->updated_at->timestamp);
94+
95+
Carbon::setTestNow(now()->addSecond());
96+
$updatedAt = now()->timestamp;
97+
98+
$castUser->update([
99+
'email' => fake()->unique()->email,
100+
]);
101+
$attributeUser->update([
102+
'email' => fake()->unique()->email,
103+
]);
104+
$mutatorUser->update([
105+
'email' => fake()->unique()->email,
106+
]);
107+
108+
$this->assertSame($createdAt, $castUser->created_at->timestamp);
109+
$this->assertSame($updatedAt, $castUser->updated_at->timestamp);
110+
$this->assertSame($updatedAt, $castUser->fresh()->updated_at->timestamp);
111+
$this->assertSame($createdAt, $attributeUser->created_at->timestamp);
112+
$this->assertSame($updatedAt, $attributeUser->updated_at->timestamp);
113+
$this->assertSame($updatedAt, $attributeUser->fresh()->updated_at->timestamp);
114+
$this->assertSame($createdAt, $mutatorUser->created_at->timestamp);
115+
$this->assertSame($updatedAt, $mutatorUser->updated_at->timestamp);
116+
$this->assertSame($updatedAt, $mutatorUser->fresh()->updated_at->timestamp);
117+
}
118+
}
119+
120+
class UserWithIntTimestampsViaCasts extends Model
121+
{
122+
protected $table = 'users';
123+
124+
protected $fillable = ['email'];
125+
126+
protected $casts = [
127+
'created_at' => UnixTimeStampToCarbon::class,
128+
'updated_at' => UnixTimeStampToCarbon::class,
129+
];
130+
}
131+
132+
class UnixTimeStampToCarbon implements CastsAttributes
133+
{
134+
public function get($model, string $key, $value, array $attributes)
135+
{
136+
return Carbon::parse($value);
137+
}
138+
139+
public function set($model, string $key, $value, array $attributes)
140+
{
141+
return Carbon::parse($value)->timestamp;
142+
}
143+
}
144+
145+
class UserWithIntTimestampsViaAttribute extends Model
146+
{
147+
protected $table = 'users';
148+
149+
protected $fillable = ['email'];
150+
151+
protected function updatedAt(): Attribute
152+
{
153+
return Attribute::make(
154+
get: fn ($value) => Carbon::parse($value),
155+
set: fn ($value) => Carbon::parse($value)->timestamp,
156+
);
157+
}
158+
159+
protected function createdAt(): Attribute
160+
{
161+
return Attribute::make(
162+
get: fn ($value) => Carbon::parse($value),
163+
set: fn ($value) => Carbon::parse($value)->timestamp,
164+
);
165+
}
166+
}
167+
168+
class UserWithIntTimestampsViaMutator extends Model
169+
{
170+
protected $table = 'users';
171+
172+
protected $fillable = ['email'];
173+
174+
protected function getUpdatedAtAttribute($value)
175+
{
176+
return Carbon::parse($value);
177+
}
178+
179+
protected function setUpdatedAtAttribute($value)
180+
{
181+
$this->attributes['updated_at'] = Carbon::parse($value)->timestamp;
182+
}
183+
184+
protected function getCreatedAtAttribute($value)
185+
{
186+
return Carbon::parse($value);
187+
}
188+
189+
protected function setCreatedAtAttribute($value)
190+
{
191+
$this->attributes['created_at'] = Carbon::parse($value)->timestamp;
192+
}
193+
}

0 commit comments

Comments
 (0)