Skip to content

Commit f4ad23e

Browse files
committed
fix: ensure non-existing relation inclusion does not break requests
In case relation has a wildcard whitelisting and request contains a non-existing relation inclusion
1 parent 4156974 commit f4ad23e

File tree

3 files changed

+35
-9
lines changed

3 files changed

+35
-9
lines changed

src/Drivers/Standard/QueryBuilder.php

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -339,14 +339,17 @@ public function getQualifiedFieldName(string $field): string
339339
* @param string $relation
340340
* @return string
341341
*/
342-
public function getRelationModelClass(string $relation): string
342+
public function getRelationModelClass(string $relation): ?string
343343
{
344344
$relations = collect(explode('.', $relation));
345-
$relation = $relations[0];
346345

347-
$resourceModel = (new $this->resourceModelClass)->$relation()->getModel();
346+
$resourceModel = (new $this->resourceModelClass);
347+
348+
foreach ($relations as $nestedRelation) {
349+
if (!method_exists($resourceModel, $nestedRelation)) {
350+
return null;
351+
}
348352

349-
foreach ($relations->skip(1) as $nestedRelation) {
350353
$resourceModel = $resourceModel->$nestedRelation()->getModel();
351354
}
352355

@@ -545,12 +548,16 @@ public function applyAggregatesToQuery($query, Request $request, array $aggregat
545548
);
546549
}
547550

551+
if (!$relationModelClass = $this->getRelationModelClass($aggregateDescriptor['relation'])) {
552+
continue;
553+
}
554+
548555
$query->withAggregate([
549556
$aggregateDescriptor['relation'] => function (Builder $aggregateQuery) use (
550557
$aggregateDescriptor,
551-
$request
558+
$request,
559+
$relationModelClass
552560
) {
553-
$relationModelClass = $this->getRelationModelClass($aggregateDescriptor['relation']);
554561
$relationQueryBuilder = $this->clone($relationModelClass);
555562

556563
$relationQueryBuilder->applyFiltersToQuery(
@@ -590,9 +597,16 @@ public function applyIncludesToQuery($query, Request $request, array $includeDes
590597
}
591598

592599
foreach ($includeDescriptors as $includeDescriptor) {
600+
if (!$relationModelClass = $this->getRelationModelClass($includeDescriptor['relation'])) {
601+
continue;
602+
}
603+
593604
$query->with([
594-
$includeDescriptor['relation'] => function (Relation $includeQuery) use ($includeDescriptor, $request) {
595-
$relationModelClass = $this->getRelationModelClass($includeDescriptor['relation']);
605+
$includeDescriptor['relation'] => function (Relation $includeQuery) use (
606+
$includeDescriptor,
607+
$request,
608+
$relationModelClass
609+
) {
596610
$relationQueryBuilder = $this->clone($relationModelClass);
597611

598612
$relationQueryBuilder->applyFiltersToQuery(

tests/Feature/StandardShowOperationsTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,16 @@ public function getting_a_single_resource_with_nested_included_relation(): void
120120

121121
$this->assertResourceShown($response, $post->fresh('user.roles')->toArray());
122122
}
123+
124+
/** @test */
125+
public function getting_a_single_resource_with_nested_non_existing_included_relation(): void
126+
{
127+
$post = factory(Post::class)->create(['user_id' => factory(User::class)->create()->id]);
128+
129+
Gate::policy(Post::class, GreenPolicy::class);
130+
131+
$response = $this->get("/api/posts/{$post->id}?include=image.non-existing-relation");
132+
133+
$this->assertResourceShown($response, $post->fresh()->toArray());
134+
}
123135
}

tests/Fixtures/app/Http/Controllers/PostsController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,6 @@ public function exposedScopes(): array
5858
*/
5959
public function includes(): array
6060
{
61-
return ['user', 'user.roles'];
61+
return ['user', 'user.roles', 'image.*'];
6262
}
6363
}

0 commit comments

Comments
 (0)