Allow relationships to specify adapter-specific scope logic

This commit is contained in:
Toby Zerner 2019-10-03 16:35:41 +09:30
parent 4a001556d8
commit 4883a8b595
4 changed files with 43 additions and 16 deletions

View File

@ -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

View File

@ -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

View File

@ -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);
}
}
}
}

View File

@ -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;
}
}