Skip to content

Commit 214d69e

Browse files
Merge pull request #773 from Tofandel/patch-2
Avoid loading already loaded relations and allow loading non studly relation names
2 parents 8ff9118 + 7c4a354 commit 214d69e

File tree

4 files changed

+46
-21
lines changed

4 files changed

+46
-21
lines changed

.github/workflows/run-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,4 @@ jobs:
4646
composer update --${{ matrix.stability }} --prefer-dist --no-interaction
4747
4848
- name: Execute tests
49-
run: vendor/bin/pest
49+
run: vendor/bin/pest --enforce-time-limit --fail-on-risky

src/Normalizers/Normalized/NormalizedModel.php

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public function getProperty(string $name, DataProperty $dataProperty): mixed
3434

3535
protected function initialize(Model $model): void
3636
{
37-
$this->properties = $model->toArray();
37+
$this->properties = $model->withoutRelations()->toArray();
3838

3939
foreach ($model->getDates() as $key) {
4040
if (isset($this->properties[$key])) {
@@ -49,14 +49,6 @@ protected function initialize(Model $model): void
4949
}
5050
}
5151
}
52-
53-
foreach ($model->getRelations() as $key => $relation) {
54-
$key = $model::$snakeAttributes ? Str::snake($key) : $key;
55-
56-
if (isset($this->properties[$key])) {
57-
$this->properties[$key] = $relation;
58-
}
59-
}
6052
}
6153

6254
protected function isDateCast(string $cast): bool
@@ -77,18 +69,23 @@ protected function fetchNewProperty(string $name, DataProperty $dataProperty): m
7769
return $this->properties[$name] = $this->model->getAttribute($name);
7870
}
7971

80-
if (! $dataProperty->attributes->contains(fn (object $attribute) => $attribute::class === LoadRelation::class)) {
81-
return UnknownProperty::create();
82-
}
83-
84-
$studlyName = Str::studly($name);
72+
$camelName = Str::camel($name);
8573

86-
if (! method_exists($this->model, $studlyName)) {
87-
return UnknownProperty::create();
74+
if ($dataProperty->attributes->contains(fn (object $attribute) => $attribute::class === LoadRelation::class)) {
75+
if (method_exists($this->model, $name)) {
76+
$this->model->loadMissing($name);
77+
} elseif (method_exists($this->model, $camelName)) {
78+
$this->model->loadMissing($camelName);
79+
}
8880
}
8981

90-
$this->model->load($studlyName);
82+
if ($this->model->relationLoaded($name)) {
83+
return $this->properties[$name] = $this->model->getRelation($name);
84+
}
85+
if ($this->model->relationLoaded($camelName)) {
86+
return $this->properties[$name] = $this->model->getRelation($camelName);
87+
}
9188

92-
return $this->properties[$name] = $this->model->{$studlyName};
89+
return $this->properties[$name] = UnknownProperty::create();
9390
}
9491
}

tests/Fakes/Models/FakeModel.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ public function fakeNestedModels(): HasMany
2222
return $this->hasMany(FakeNestedModel::class);
2323
}
2424

25+
public function alt_fake_nested_models(): HasMany
26+
{
27+
return $this->hasMany(FakeNestedModel::class);
28+
}
29+
2530
public function accessor(): Attribute
2631
{
2732
return Attribute::get(fn () => "accessor_{$this->string}");

tests/Normalizers/ModelNormalizerTest.php

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,21 @@
2222
->date->toEqual($data->date);
2323
});
2424

25+
it('does not loop infinitely on relations', function () {
26+
$m1 = FakeModel::factory()->makeOne();
27+
$m2 = FakeNestedModel::factory()->makeOne();
28+
$m2->setRelation('parent', $m1);
29+
$m1->setRelation('pivot', $m2);
30+
31+
$data = FakeModelData::from($m1);
32+
33+
expect($m1)
34+
->string->toEqual($data->string)
35+
->nullable->toEqual($data->nullable)
36+
->date->toEqual($data->date);
37+
38+
});
39+
2540
it('can get a data object with nesting from model and relations when loaded', function () {
2641
$model = FakeModel::factory()->create();
2742

@@ -103,8 +118,12 @@
103118
$dataClass = new class () extends Data {
104119
#[LoadRelation, DataCollectionOf(FakeNestedModelData::class)]
105120
public array $fake_nested_models;
121+
122+
#[LoadRelation, DataCollectionOf(FakeNestedModelData::class)]
123+
public array $alt_fake_nested_models;
106124
};
107125

126+
$model->load('alt_fake_nested_models');
108127
DB::enableQueryLog();
109128

110129
$data = $dataClass::from($model);
@@ -114,6 +133,10 @@
114133
expect($data->fake_nested_models)
115134
->toHaveCount(2)
116135
->each->toBeInstanceOf(FakeNestedModelData::class);
136+
137+
expect($data->alt_fake_nested_models)
138+
->toHaveCount(2)
139+
->each->toBeInstanceOf(FakeNestedModelData::class);
117140
expect($queryLog)->toHaveCount(1);
118141
});
119142

@@ -154,8 +177,8 @@
154177
it('can use mappers to map the names', function () {
155178
$model = FakeModel::factory()->create();
156179

157-
$nestedModelA = FakeNestedModel::factory()->for($model)->create();
158-
$nestedModelB = FakeNestedModel::factory()->for($model)->create();
180+
FakeNestedModel::factory()->for($model)->create();
181+
FakeNestedModel::factory()->for($model)->create();
159182

160183
$dataClass = new class () extends Data {
161184
#[DataCollectionOf(FakeNestedModelData::class), MapInputName(SnakeCaseMapper::class)]

0 commit comments

Comments
 (0)