diff --git a/src/Adapter/AdapterInterface.php b/src/Adapter/AdapterInterface.php index 0815455..2da88fe 100644 --- a/src/Adapter/AdapterInterface.php +++ b/src/Adapter/AdapterInterface.php @@ -2,6 +2,7 @@ namespace Tobyz\JsonApiServer\Adapter; +use Closure; use Tobyz\JsonApiServer\Schema\Attribute; use Tobyz\JsonApiServer\Schema\HasMany; use Tobyz\JsonApiServer\Schema\HasOne; @@ -216,9 +217,11 @@ interface AdapterInterface * * @param array $models * @param array $relationships + * @param Closure $scope Should be called to give the deepest relationship + * an opportunity to scope the query that will fetch related resources * @return mixed */ - public function load(array $models, array $relationships): void; + public function load(array $models, array $relationships, Closure $scope): void; /** * Load information about the IDs of related resources onto a collection diff --git a/src/Adapter/EloquentAdapter.php b/src/Adapter/EloquentAdapter.php index d016ad3..6291f80 100644 --- a/src/Adapter/EloquentAdapter.php +++ b/src/Adapter/EloquentAdapter.php @@ -2,6 +2,7 @@ namespace Tobyz\JsonApiServer\Adapter; +use Closure; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -188,9 +189,13 @@ class EloquentAdapter implements AdapterInterface $query->take($limit)->skip($offset); } - public function load(array $models, array $relationships): void + public function load(array $models, array $relationships, Closure $scope): void { - (new Collection($models))->loadMissing($this->getRelationshipPath($relationships)); + $relationship = end($relationships); + + (new Collection($models))->loadMissing([ + $this->getRelationshipPath($relationships) => $scope + ]); } public function loadIds(array $models, Relationship $relationship): void diff --git a/src/Handler/Concerns/IncludesData.php b/src/Handler/Concerns/IncludesData.php index da59b5f..e5706ad 100644 --- a/src/Handler/Concerns/IncludesData.php +++ b/src/Handler/Concerns/IncludesData.php @@ -8,6 +8,7 @@ use function Tobyz\JsonApiServer\evaluate; use Tobyz\JsonApiServer\Exception\BadRequestException; use Tobyz\JsonApiServer\ResourceType; use Tobyz\JsonApiServer\Schema\Relationship; +use function Tobyz\JsonApiServer\run_callbacks; trait IncludesData { @@ -103,9 +104,27 @@ trait IncludesData $adapter = $this->resource->getAdapter(); $fields = $this->resource->getSchema()->getFields(); - // TODO: don't load IDs for relationships which are included below + $trails = $this->buildRelationshipTrails($this->resource, $include); + $loaded = []; + + foreach ($trails as $relationships) { + $relationship = end($relationships); + + if (($load = $relationship->getLoadable()) instanceof Closure) { + $load($models, $relationships, false, $request); + } else { + $scope = function ($query) use ($request, $relationship) { + run_callbacks($relationship->getScopes(), [$query, $request]); + }; + + $adapter->load($models, $relationships, $scope); + } + + $loaded[] = $relationships[0]; + } + foreach ($fields as $name => $field) { - if (! $field instanceof Relationship || ! evaluate($field->getLinkage(), [$request]) || ! $field->getLoadable()) { + if (! $field instanceof Relationship || ! evaluate($field->getLinkage(), [$request]) || ! $field->getLoadable() || in_array($field, $loaded)) { continue; } @@ -115,16 +134,5 @@ trait IncludesData $adapter->loadIds($models, $field); } } - - $trails = $this->buildRelationshipTrails($this->resource, $include); - - foreach ($trails as $relationships) { - if (($load = end($relationships)->getLoadable()) instanceof Closure) { - // TODO: probably need to loop through relationships here - $load($models, false); - } else { - $adapter->load($models, $relationships); - } - } } } diff --git a/src/Schema/Relationship.php b/src/Schema/Relationship.php index 57ca3ee..4a4f23f 100644 --- a/src/Schema/Relationship.php +++ b/src/Schema/Relationship.php @@ -13,6 +13,7 @@ abstract class Relationship extends Field private $links = true; private $loadable = true; private $includable = false; + private $scopes = []; public function type($type) { @@ -122,4 +123,14 @@ abstract class Relationship extends Field { return 'relationships'; } + + public function scope(Closure $callback) + { + $this->scopes[] = $callback; + } + + public function getScopes(): array + { + return $this->scopes; + } }