Skip to content

Commit a7dd882

Browse files
committed
feat: eager load custom field in options relationship and order by sort_order
1 parent c46ddf6 commit a7dd882

File tree

2 files changed

+68
-1
lines changed

2 files changed

+68
-1
lines changed

src/Models/CustomField.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,9 @@ public function values(): HasMany
143143
public function options(): HasMany
144144
{
145145
/** @var HasMany<CustomFieldOption, self> */
146-
return $this->hasMany(CustomFields::optionModel());
146+
return $this->hasMany(CustomFields::optionModel())
147+
->with('customField')
148+
->orderBy('sort_order');
147149
}
148150

149151
public function typeData(): Attribute
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Illuminate\Support\Facades\DB;
6+
use Relaticle\CustomFields\Models\CustomField;
7+
use Relaticle\CustomFields\Models\CustomFieldOption;
8+
9+
describe('CustomField Relationships', function (): void {
10+
it('eager loads custom field when accessing options to prevent N+1 queries', function (): void {
11+
// Arrange
12+
$customField = CustomField::factory()
13+
->has(CustomFieldOption::factory()->count(3), 'options')
14+
->create();
15+
16+
// Enable query logging to track database queries
17+
DB::flushQueryLog();
18+
DB::enableQueryLog();
19+
20+
// Act
21+
// Fetch the custom field fresh and load its options
22+
$options = $customField->fresh()->options;
23+
24+
// Access customField on each option - should not trigger new queries
25+
// if eager loading is working correctly
26+
$options->each(fn (CustomFieldOption $option): ?string => $option->customField->name);
27+
28+
$queries = DB::getQueryLog();
29+
30+
// Assert
31+
// Should only be 2 queries:
32+
// 1. SELECT * FROM custom_fields WHERE id = ?
33+
// 2. SELECT * FROM custom_field_options WHERE custom_field_id = ? ORDER BY sort_order
34+
// (with eager load of customField relationship)
35+
expect($queries)->toHaveCount(2);
36+
});
37+
38+
it('orders options by sort_order when accessing them', function (): void {
39+
// Arrange
40+
$customField = CustomField::factory()->create();
41+
42+
// Create options with specific sort orders (intentionally out of order)
43+
CustomFieldOption::factory()->create([
44+
'custom_field_id' => $customField->id,
45+
'sort_order' => 3,
46+
'label' => 'Third',
47+
]);
48+
CustomFieldOption::factory()->create([
49+
'custom_field_id' => $customField->id,
50+
'sort_order' => 1,
51+
'label' => 'First',
52+
]);
53+
CustomFieldOption::factory()->create([
54+
'custom_field_id' => $customField->id,
55+
'sort_order' => 2,
56+
'label' => 'Second',
57+
]);
58+
59+
// Act
60+
$options = $customField->fresh()->options;
61+
62+
// Assert
63+
expect($options->pluck('label')->toArray())->toBe(['First', 'Second', 'Third']);
64+
});
65+
});

0 commit comments

Comments
 (0)