Skip to content

Commit 3a154be

Browse files
committed
Apply EloquentResource scope to related queries
1 parent 00c294f commit 3a154be

3 files changed

Lines changed: 78 additions & 68 deletions

File tree

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
namespace Tobyz\JsonApiServer\Laravel\Concerns;
4+
5+
use Illuminate\Database\Eloquent\Relations\MorphTo;
6+
use Tobyz\JsonApiServer\Context;
7+
use Tobyz\JsonApiServer\Laravel\EloquentResource;
8+
use Tobyz\JsonApiServer\Laravel\Field\ToMany;
9+
use Tobyz\JsonApiServer\Laravel\Field\ToOne;
10+
use Tobyz\JsonApiServer\Schema\Field\Relationship;
11+
12+
trait ScopesRelatedResourceQueries
13+
{
14+
protected static function scopeRelatedQuery(
15+
Relationship $relationship,
16+
object $relation,
17+
object $query,
18+
Context $context,
19+
): void {
20+
$applyRelationshipScope = function () use ($relationship, $relation, $context): void {
21+
if (
22+
($relationship instanceof ToMany || $relationship instanceof ToOne) &&
23+
$relationship->scope
24+
) {
25+
($relationship->scope)($relation, $context);
26+
}
27+
};
28+
29+
$constrain = [];
30+
31+
foreach ($relationship->collections as $collection) {
32+
foreach ($context->api->getCollection($collection)->resources() as $resourceName) {
33+
$resource = $context->api->getResource($resourceName);
34+
35+
if (!$resource instanceof EloquentResource) {
36+
continue;
37+
}
38+
39+
$modelClass = get_class($resource->newModel($context));
40+
41+
if (isset($constrain[$modelClass])) {
42+
continue;
43+
}
44+
45+
$constrain[$modelClass] = function ($query) use (
46+
$resource,
47+
$context,
48+
$applyRelationshipScope,
49+
) {
50+
$resource->scope($query, $context);
51+
$applyRelationshipScope();
52+
};
53+
}
54+
}
55+
56+
if ($relation instanceof MorphTo) {
57+
$relation->constrain($constrain);
58+
} elseif ($constrain) {
59+
$relatedModelClass = get_class($query->getModel());
60+
($constrain[$relatedModelClass] ?? reset($constrain))($query);
61+
} else {
62+
$applyRelationshipScope();
63+
}
64+
}
65+
}

src/Laravel/EloquentBuffer.php

Lines changed: 7 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44

55
use Illuminate\Database\Eloquent\Collection;
66
use Illuminate\Database\Eloquent\Model;
7-
use Illuminate\Database\Eloquent\Relations\MorphTo;
87
use Tobyz\JsonApiServer\Context;
9-
use Tobyz\JsonApiServer\Laravel\Field\ToMany;
10-
use Tobyz\JsonApiServer\Laravel\Field\ToOne;
8+
use Tobyz\JsonApiServer\Laravel\Concerns\ScopesRelatedResourceQueries;
119
use Tobyz\JsonApiServer\Schema\Field\Relationship;
1210

1311
abstract class EloquentBuffer
1412
{
13+
use ScopesRelatedResourceQueries;
14+
1515
private static array $buffer = [];
1616

1717
public static function add(Model $model, string $relationName): void
@@ -30,68 +30,12 @@ public static function load(
3030
}
3131

3232
Collection::make($models)->load([
33-
$relationName => function ($relation) use (
34-
$model,
35-
$relationName,
33+
$relationName => fn($relation) => static::scopeRelatedQuery(
3634
$relationship,
35+
$relation,
36+
$relation->getQuery(),
3737
$context,
38-
) {
39-
$query = $relation->getQuery();
40-
41-
// When loading the relationship, we need to scope the query
42-
// using the scopes defined in the related API resource – there
43-
// may be multiple if this is a polymorphic relationship. We
44-
// start by getting the resource types this relationship
45-
// could possibly contain.
46-
$resources = array_merge(
47-
...array_map(
48-
fn($collection) => array_map(
49-
fn($resource) => $context->api->getResource($resource),
50-
$context->api->getCollection($collection)->resources(),
51-
),
52-
$relationship->collections,
53-
),
54-
);
55-
56-
// Now, construct a map of model class names -> scoping
57-
// functions. This will be provided to the MorphTo::constrain
58-
// method in order to apply type-specific scoping.
59-
$constrain = [];
60-
61-
foreach ($resources as $resource) {
62-
if (!$resource instanceof EloquentResource) {
63-
continue;
64-
}
65-
66-
$modelClass = get_class($resource->newModel($context));
67-
68-
if (isset($constrain[$modelClass])) {
69-
continue;
70-
}
71-
72-
$constrain[$modelClass] = function ($query) use (
73-
$resource,
74-
$context,
75-
$relationship,
76-
$relation,
77-
) {
78-
$resource->scope($query, $context);
79-
80-
if (
81-
($relationship instanceof ToMany || $relationship instanceof ToOne) &&
82-
$relationship->scope
83-
) {
84-
($relationship->scope)($relation, $context);
85-
}
86-
};
87-
}
88-
89-
if ($relation instanceof MorphTo) {
90-
$relation->constrain($constrain);
91-
} elseif ($constrain) {
92-
reset($constrain)($query);
93-
}
94-
},
38+
),
9539
]);
9640

9741
static::$buffer[get_class($model)][$relationName] = [];

src/Laravel/EloquentResource.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
use Tobyz\JsonApiServer\Context;
1616
use Tobyz\JsonApiServer\Exception\Pagination\InvalidPageCursorException;
1717
use Tobyz\JsonApiServer\Exception\Pagination\RangePaginationNotSupportedException;
18-
use Tobyz\JsonApiServer\Laravel\Field\ToMany as LaravelToMany;
18+
use Tobyz\JsonApiServer\Laravel\Concerns\ScopesRelatedResourceQueries;
1919
use Tobyz\JsonApiServer\Pagination\Page;
2020
use Tobyz\JsonApiServer\Resource\AbstractResource;
2121
use Tobyz\JsonApiServer\Resource\Attachable;
@@ -49,6 +49,8 @@ abstract class EloquentResource extends AbstractResource implements
4949
Attachable,
5050
RelatedListable
5151
{
52+
use ScopesRelatedResourceQueries;
53+
5254
public function resource(object $model, Context $context): ?string
5355
{
5456
$eloquentModel = $this->newModel($context);
@@ -137,12 +139,11 @@ public function relatedQuery(object $model, ToMany $relationship, Context $conte
137139
}
138140

139141
$relation = $model->$method();
142+
$query = $relation->getQuery();
140143

141-
if ($relationship instanceof LaravelToMany && $relationship->scope) {
142-
($relationship->scope)($relation, $context);
143-
}
144+
static::scopeRelatedQuery($relationship, $relation, $query, $context);
144145

145-
return $relation->getQuery();
146+
return $query;
146147
}
147148

148149
/**

0 commit comments

Comments
 (0)