Skip to content

Commit 9b873b4

Browse files
committed
wip
1 parent 4eae871 commit 9b873b4

File tree

3 files changed

+66
-151
lines changed

3 files changed

+66
-151
lines changed

packages/database/src/IsDatabaseModel.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,13 +179,15 @@ public function refresh(): self
179179
$relations = [];
180180

181181
foreach (new ClassReflector($this)->getPublicProperties() as $property) {
182-
if (! $property->getValue($this)) {
182+
if (! $property->isInitialized($this) || ! $property->getValue($this)) {
183183
continue;
184184
}
185185

186-
if ($model->isRelation($property->getName())) {
187-
$relations[] = $property->getName();
186+
if (! $model->isRelation($property->getName())) {
187+
continue;
188188
}
189+
190+
$relations[] = $property->getName();
189191
}
190192

191193
$this->load(...$relations);

tests/Integration/Database/ModelsWithoutIdTest.php

Lines changed: 1 addition & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use Tempest\Database\DatabaseMigration;
99
use Tempest\Database\Exceptions\ModelDidNotHavePrimaryColumn;
1010
use Tempest\Database\HasOne;
11-
use Tempest\Database\IsDatabaseModel;
1211
use Tempest\Database\Migrations\CreateMigrationsTable;
1312
use Tempest\Database\PrimaryKey;
1413
use Tempest\Database\QueryStatement;
@@ -23,39 +22,6 @@
2322
*/
2423
final class ModelsWithoutIdTest extends FrameworkIntegrationTestCase
2524
{
26-
public function test_save_creates_new_record_for_model_without_id(): void
27-
{
28-
$this->migrate(CreateMigrationsTable::class, CreateLogEntryMigration::class);
29-
30-
$log = new LogEntry(level: 'INFO', message: 'Frieren discovered ancient magic', context: 'exploration');
31-
$savedLog = $log->save();
32-
33-
$this->assertSame($log, $savedLog);
34-
$this->assertSame('INFO', $savedLog->level);
35-
$this->assertSame('Frieren discovered ancient magic', $savedLog->message);
36-
37-
$allLogs = query(LogEntry::class)->all();
38-
$this->assertCount(1, $allLogs);
39-
$this->assertSame('INFO', $allLogs[0]->level);
40-
}
41-
42-
public function test_save_always_inserts_for_models_without_id(): void
43-
{
44-
$this->migrate(CreateMigrationsTable::class, CreateLogEntryMigration::class);
45-
46-
$log = new LogEntry(level: 'INFO', message: 'Original message', context: 'test');
47-
$log->save();
48-
49-
// Models without primary keys always insert when save() is called
50-
$log->message = 'Modified message';
51-
$log->save();
52-
53-
$allLogs = query(LogEntry::class)->all();
54-
$this->assertCount(2, $allLogs);
55-
$this->assertSame('Original message', $allLogs[0]->message);
56-
$this->assertSame('Modified message', $allLogs[1]->message);
57-
}
58-
5925
public function test_update_model_without_id_with_specific_conditions(): void
6026
{
6127
$this->migrate(CreateMigrationsTable::class, CreateLogEntryMigration::class);
@@ -166,13 +132,11 @@ public function test_model_with_mixed_id_and_non_id_properties(): void
166132
{
167133
$this->migrate(CreateMigrationsTable::class, CreateMixedModelMigration::class);
168134

169-
$mixed = new MixedModel(
135+
$mixed = query(MixedModel::class)->create(
170136
regular_field: 'test',
171137
another_field: 'data',
172138
);
173139

174-
$mixed->save();
175-
176140
$this->assertInstanceOf(PrimaryKey::class, $mixed->id);
177141
$this->assertSame('test', $mixed->regular_field);
178142

@@ -182,107 +146,6 @@ public function test_model_with_mixed_id_and_non_id_properties(): void
182146
$this->assertSame('test', $all[0]->regular_field);
183147
}
184148

185-
public function test_refresh_throws_for_models_without_id(): void
186-
{
187-
$this->migrate(CreateMigrationsTable::class, CreateLogEntryMigration::class);
188-
189-
$log = new LogEntry(
190-
level: 'INFO',
191-
message: 'Frieren studies magic',
192-
context: 'training',
193-
);
194-
195-
$this->expectException(ModelDidNotHavePrimaryColumn::class);
196-
$this->expectExceptionMessage('does not have a primary column defined, which is required for the `refresh` method');
197-
198-
$log->refresh();
199-
}
200-
201-
public function test_load_throws_for_models_without_id(): void
202-
{
203-
$this->migrate(CreateMigrationsTable::class, CreateLogEntryMigration::class);
204-
205-
$log = new LogEntry(
206-
level: 'INFO',
207-
message: 'Frieren explores ruins',
208-
context: 'adventure',
209-
);
210-
211-
$this->expectException(ModelDidNotHavePrimaryColumn::class);
212-
$this->expectExceptionMessage('does not have a primary column defined, which is required for the `load` method');
213-
214-
$log->load('someRelation');
215-
}
216-
217-
public function test_refresh_works_for_models_with_id(): void
218-
{
219-
$this->migrate(CreateMigrationsTable::class, CreateMixedModelMigration::class);
220-
221-
$mixed = query(MixedModel::class)->create(
222-
regular_field: 'original',
223-
another_field: 'data',
224-
);
225-
226-
query(MixedModel::class)
227-
->update(regular_field: 'updated')
228-
->where('id', $mixed->id->value)
229-
->execute();
230-
231-
$mixed->refresh();
232-
233-
$this->assertSame('updated', $mixed->regular_field);
234-
$this->assertSame('data', $mixed->another_field);
235-
}
236-
237-
public function test_refresh_works_for_models_with_unloaded_relation(): void
238-
{
239-
$this->migrate(
240-
CreateMigrationsTable::class,
241-
CreateTestUserMigration::class,
242-
CreateTestProfileMigration::class,
243-
);
244-
245-
$user = query(TestUser::class)->create(
246-
name: 'Frieren',
247-
248-
);
249-
250-
query(TestProfile::class)->create(
251-
user: $user,
252-
bio: 'Ancient elf mage',
253-
age: 1000,
254-
);
255-
256-
// Get user without loading the profile relation
257-
$userWithoutProfile = query(TestUser::class)->findById($user->id);
258-
259-
$this->assertNull($userWithoutProfile->profile);
260-
261-
// Update the user's name in the database
262-
query(TestUser::class)
263-
->update(name: 'Frieren the Mage')
264-
->where('id', $user->id->value)
265-
->execute();
266-
267-
// Refresh should work even with unloaded relations
268-
$userWithoutProfile->refresh();
269-
270-
$this->assertSame('Frieren the Mage', $userWithoutProfile->name);
271-
$this->assertSame('[email protected]', $userWithoutProfile->email);
272-
$this->assertNull($userWithoutProfile->profile); // Relation should still be unloaded
273-
274-
// Load the relation
275-
$userWithoutProfile->load('profile');
276-
277-
$this->assertInstanceOf(TestProfile::class, $userWithoutProfile->profile);
278-
$this->assertSame('Ancient elf mage', $userWithoutProfile->profile->bio);
279-
$this->assertSame(1000, $userWithoutProfile->profile->age);
280-
281-
$userWithoutProfile->refresh();
282-
283-
$this->assertInstanceOf(TestProfile::class, $userWithoutProfile->profile);
284-
}
285-
286149
public function test_load_works_for_models_with_id(): void
287150
{
288151
$this->migrate(CreateMigrationsTable::class, CreateMixedModelMigration::class);
@@ -361,8 +224,6 @@ public function test_load_method_refreshes_all_properties_not_just_relations():
361224

362225
final class LogEntry
363226
{
364-
use IsDatabaseModel;
365-
366227
public function __construct(
367228
public string $level,
368229
public string $message,
@@ -373,8 +234,6 @@ public function __construct(
373234
#[Table('cache_entries')]
374235
final class CacheEntry
375236
{
376-
use IsDatabaseModel;
377-
378237
public function __construct(
379238
public string $cache_key,
380239
public string $cache_value,
@@ -384,8 +243,6 @@ public function __construct(
384243

385244
final class MixedModel
386245
{
387-
use IsDatabaseModel;
388-
389246
public ?PrimaryKey $id = null;
390247

391248
public function __construct(
@@ -396,8 +253,6 @@ public function __construct(
396253

397254
final class TestUser
398255
{
399-
use IsDatabaseModel;
400-
401256
public ?PrimaryKey $id = null;
402257

403258
#[HasOne(ownerJoin: 'user_id')]
@@ -411,8 +266,6 @@ public function __construct(
411266

412267
final class TestProfile
413268
{
414-
use IsDatabaseModel;
415-
416269
public ?PrimaryKey $id = null;
417270

418271
#[BelongsTo(ownerJoin: 'user_id')]
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
namespace Integration\Database;
4+
5+
use Tempest\Database\Migrations\CreateMigrationsTable;
6+
use Tests\Tempest\Fixtures\Migrations\CreateAuthorTable;
7+
use Tests\Tempest\Fixtures\Migrations\CreateBookTable;
8+
use Tests\Tempest\Fixtures\Modules\Books\Models\Author;
9+
use Tests\Tempest\Fixtures\Modules\Books\Models\Book;
10+
use Tests\Tempest\Integration\FrameworkIntegrationTestCase;
11+
use function Tempest\Database\query;
12+
13+
final class RefreshModelTest extends FrameworkIntegrationTestCase
14+
{
15+
public function test_refresh_works_for_models_with_unloaded_relation(): void
16+
{
17+
$this->migrate(
18+
CreateMigrationsTable::class,
19+
CreateBookTable::class,
20+
CreateAuthorTable::class,
21+
);
22+
23+
$author = Author::create(
24+
name: 'Brent Roose',
25+
);
26+
27+
$book = Book::create(
28+
title: 'Timeline Taxi',
29+
author: $author,
30+
);
31+
32+
// Get user without loading the profile relation
33+
$book = Book::get($book->id);
34+
35+
$this->assertNull($book->author);
36+
37+
// Update the user's name in the database
38+
query(Book::class)
39+
->update(
40+
title: 'Timeline Taxi 2',
41+
)
42+
->where('id', $book->id)
43+
->execute();
44+
45+
// Refresh should work even with unloaded relations
46+
$book->refresh();
47+
48+
$this->assertSame('Timeline Taxi 2', $book->title);
49+
$this->assertNull($book->author); // Relation should still be unloaded
50+
51+
// Load the relation
52+
$book->load('author');
53+
54+
$this->assertInstanceOf(Author::class, $book->author);
55+
56+
$book->refresh();
57+
58+
$this->assertInstanceOf(Author::class, $book->author);
59+
}
60+
}

0 commit comments

Comments
 (0)