Skip to content

Commit 68c9ae8

Browse files
committed
Merge remote-tracking branch 'origin/query-optimizations' into query-optimizations
# Conflicts: # src/Filters/SearchableFilter.php # tests/Controllers/Index/EagerLoadingOptimizationTest.php
2 parents e05151b + 5ad582e commit 68c9ae8

File tree

2 files changed

+53
-53
lines changed

2 files changed

+53
-53
lines changed

src/Services/Search/RepositorySearchService.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,13 @@ public function prepareRelations(RestifyRequest $request, Builder|Relation $quer
116116
})->all();
117117

118118
$eagerRelations = array_merge($filtered, ($this->repository)::withs());
119-
119+
120120
// Only exclude joined relationships if JOIN optimization is enabled
121121
if (config('restify.search.use_joins', false)) {
122122
$joinedRelations = $this->getJoinedRelationships($query);
123123
$eagerRelations = array_diff($eagerRelations, $joinedRelations);
124124
}
125-
125+
126126
return $query->with($eagerRelations);
127127
}
128128

@@ -254,15 +254,15 @@ protected function getJoinedRelationships($query): array
254254
{
255255
$joinedRelations = [];
256256
$joins = collect($query->getQuery()->joins ?? []);
257-
257+
258258
// Extract relationship names from join aliases
259259
$joins->each(function ($join) use (&$joinedRelations) {
260260
if (str_contains($join->table, '_for_')) {
261261
$relationName = str($join->table)->after('_for_')->toString();
262262
$joinedRelations[] = $relationName;
263263
}
264264
});
265-
265+
266266
return $joinedRelations;
267267
}
268268

tests/Unit/RelatedEagerLoadingTest.php

Lines changed: 49 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,23 @@ public function it_uses_eager_loaded_relation_when_available(): void
2424
{
2525
$company = Company::factory()->create(['name' => 'Eager Company']);
2626
$user = User::factory()->for($company)->create();
27-
27+
2828
// Manually load the relation
2929
$user->load('company');
30-
30+
3131
$repository = UserRepository::resolveWith($user);
3232
$related = new Related('company');
33-
33+
3434
$request = Mockery::mock(RestifyRequest::class);
3535
$request->shouldReceive('related->resolved')->once();
36-
36+
3737
// Resolve the related field
3838
$related->resolve($request, $repository);
39-
39+
4040
// Verify it returns the already loaded company
4141
$this->assertInstanceOf(Company::class, $related->getValue());
4242
$this->assertEquals('Eager Company', $related->getValue()->name);
43-
43+
4444
// Verify the relation was already loaded (no new query)
4545
$this->assertTrue($user->relationLoaded('company'));
4646
}
@@ -50,19 +50,19 @@ public function it_makes_query_when_relation_not_loaded(): void
5050
{
5151
$company = Company::factory()->create(['name' => 'Lazy Company']);
5252
$user = User::factory()->for($company)->create();
53-
53+
5454
// Do NOT load the relation
5555
$this->assertFalse($user->relationLoaded('company'));
56-
56+
5757
$repository = UserRepository::resolveWith($user);
5858
$related = new Related('company');
59-
59+
6060
$request = Mockery::mock(RestifyRequest::class);
6161
$request->shouldReceive('related->resolved')->once();
62-
62+
6363
// Resolve the related field
6464
$related->resolve($request, $repository);
65-
65+
6666
// Verify it still returns the company (via query)
6767
$this->assertInstanceOf(Company::class, $related->getValue());
6868
$this->assertEquals('Lazy Company', $related->getValue()->name);
@@ -75,19 +75,19 @@ public function it_handles_collection_relations_when_eager_loaded(): void
7575
$posts = \Binaryk\LaravelRestify\Tests\Fixtures\Post\Post::factory()->count(3)->create([
7676
'user_id' => $user->id,
7777
]);
78-
78+
7979
// Manually load the posts relation
8080
$user->load('posts');
81-
81+
8282
$repository = UserRepository::resolveWith($user);
8383
$related = new Related('posts');
84-
84+
8585
$request = Mockery::mock(RestifyRequest::class);
8686
$request->shouldReceive('related->resolved')->once();
87-
87+
8888
// Resolve the related field
8989
$related->resolve($request, $repository);
90-
90+
9191
// Verify it returns the collection
9292
$this->assertInstanceOf(Collection::class, $related->getValue());
9393
$this->assertCount(3, $related->getValue());
@@ -98,42 +98,41 @@ public function it_handles_collection_relations_when_eager_loaded(): void
9898
public function it_handles_null_relations(): void
9999
{
100100
$user = User::factory()->create(['company_id' => null]);
101-
101+
102102
// Load the empty relation
103103
$user->load('company');
104-
104+
105105
$repository = UserRepository::resolveWith($user);
106106
$related = new Related('company');
107-
107+
108108
$request = Mockery::mock(RestifyRequest::class);
109109
$request->shouldReceive('related->resolved')->once();
110-
110+
111111
// Resolve the related field
112112
$related->resolve($request, $repository);
113-
113+
114114
// Verify it handles null correctly
115115
$this->assertNull($related->getValue());
116116
}
117117

118-
119118
#[Test]
120119
public function it_handles_eager_field_relations(): void
121120
{
122121
$company = Company::factory()->create();
123122
$user = User::factory()->for($company)->create();
124-
123+
125124
$repository = UserRepository::resolveWith($user);
126-
125+
127126
// Create an eager field
128127
$eagerField = BelongsTo::make('company', CompanyRepository::class);
129128
$related = new Related('company', $eagerField);
130-
129+
131130
$request = Mockery::mock(RestifyRequest::class);
132131
$request->shouldReceive('related->resolved')->once();
133-
132+
134133
// Resolve the related field
135134
$related->resolve($request, $repository);
136-
135+
137136
// Verify it's recognized as eager
138137
$this->assertTrue($related->isEager());
139138
}
@@ -146,19 +145,19 @@ public function it_handles_nested_relations_with_dot_notation(): void
146145
$posts = \Binaryk\LaravelRestify\Tests\Fixtures\Post\Post::factory()->count(2)->create([
147146
'user_id' => $user->id,
148147
]);
149-
148+
150149
// Load nested relation
151150
$user->load('posts.user');
152-
151+
153152
$repository = UserRepository::resolveWith($user);
154153
$related = new Related('posts.user');
155-
154+
156155
$request = Mockery::mock(RestifyRequest::class);
157156
$request->shouldReceive('related->resolved')->once();
158-
157+
159158
// Resolve the related field
160159
$related->resolve($request, $repository);
161-
160+
162161
// For nested relations, it returns the first level
163162
$value = $related->getValue();
164163
$this->assertIsArray($value);
@@ -169,20 +168,21 @@ public function it_uses_custom_resolver_callback(): void
169168
{
170169
$user = User::factory()->create();
171170
$repository = UserRepository::resolveWith($user);
172-
171+
173172
$related = new Related('custom');
174173
$related->resolveUsing(function ($request, $repo) {
175174
$this->assertInstanceOf(RestifyRequest::class, $request);
176175
$this->assertInstanceOf(UserRepository::class, $repo);
176+
177177
return 'custom-value';
178178
});
179-
179+
180180
$request = Mockery::mock(RestifyRequest::class);
181181
$request->shouldReceive('related->resolved')->once();
182-
182+
183183
// Resolve the related field
184184
$related->resolve($request, $repository);
185-
185+
186186
// Verify custom resolver was used
187187
$this->assertEquals('custom-value', $related->getValue());
188188
}
@@ -192,7 +192,7 @@ public function search_with_join_optimization_reduces_query_count_when_enabled()
192192
{
193193
// Enable JOIN optimization
194194
config(['restify.search.use_joins' => true]);
195-
195+
196196
// Create test data
197197
$companies = Company::factory()->count(5)->create();
198198
foreach ($companies as $company) {
@@ -208,23 +208,23 @@ public function search_with_join_optimization_reduces_query_count_when_enabled()
208208
];
209209

210210
\Illuminate\Support\Facades\DB::enableQueryLog();
211-
211+
212212
// Perform search that would trigger related field search
213213
$response = $this->getJson(UserRepository::route(query: [
214214
'search' => $companies->first()->name,
215-
'perPage' => 50
215+
'perPage' => 50,
216216
]));
217217

218218
$queries = \Illuminate\Support\Facades\DB::getQueryLog();
219219
\Illuminate\Support\Facades\DB::disableQueryLog();
220220

221221
$response->assertSuccessful();
222-
222+
223223
// With JOIN optimization, we should have minimal queries:
224224
// 1. Main search query with JOIN
225225
// 2. Potentially count query
226-
$this->assertLessThan(4, count($queries),
227-
'Expected fewer than 4 queries with JOIN optimization, got: ' . count($queries)
226+
$this->assertLessThan(4, count($queries),
227+
'Expected fewer than 4 queries with JOIN optimization, got: '.count($queries)
228228
);
229229

230230
// Verify that the main query uses JOIN when optimization is enabled
@@ -233,10 +233,10 @@ public function search_with_join_optimization_reduces_query_count_when_enabled()
233233
});
234234

235235
$this->assertNotNull($searchQuery, 'Search query should be found');
236-
$this->assertStringContainsString('left join', strtolower($searchQuery['query']),
236+
$this->assertStringContainsString('left join', strtolower($searchQuery['query']),
237237
'Search query should use LEFT JOIN when optimization is enabled'
238238
);
239-
239+
240240
// Clean up
241241
UserRepository::$search = [];
242242
UserRepository::$related = [];
@@ -248,7 +248,7 @@ public function joined_relationships_excluded_from_eager_loading_when_optimizati
248248
{
249249
// Enable JOIN optimization
250250
config(['restify.search.use_joins' => true]);
251-
251+
252252
$company = Company::factory()->create(['name' => 'TestCompany']);
253253
User::factory()->count(10)->for($company)->create();
254254

@@ -265,7 +265,7 @@ public function joined_relationships_excluded_from_eager_loading_when_optimizati
265265
$response = $this->getJson(UserRepository::route(query: [
266266
'search' => 'TestCompany',
267267
'related' => 'company',
268-
'perPage' => 10
268+
'perPage' => 10,
269269
]));
270270

271271
$queries = \Illuminate\Support\Facades\DB::getQueryLog();
@@ -280,14 +280,14 @@ public function joined_relationships_excluded_from_eager_loading_when_optimizati
280280
str_contains($query['query'], 'where "companies"."id" in');
281281
});
282282

283-
$this->assertCount(0, $eagerLoadingQueries,
283+
$this->assertCount(0, $eagerLoadingQueries,
284284
'Should not execute separate eager loading query for joined relationship when optimization is enabled'
285285
);
286-
286+
287287
// Clean up
288288
UserRepository::$search = [];
289289
UserRepository::$related = [];
290290
UserRepository::$with = [];
291291
config(['restify.search.use_joins' => false]);
292292
}
293-
}
293+
}

0 commit comments

Comments
 (0)