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; namespace Tobyz\JsonApiServer\Adapter;
use Closure;
use Tobyz\JsonApiServer\Schema\Attribute; use Tobyz\JsonApiServer\Schema\Attribute;
use Tobyz\JsonApiServer\Schema\HasMany; use Tobyz\JsonApiServer\Schema\HasMany;
use Tobyz\JsonApiServer\Schema\HasOne; use Tobyz\JsonApiServer\Schema\HasOne;
@ -216,9 +217,11 @@ interface AdapterInterface
* *
* @param array $models * @param array $models
* @param array $relationships * @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 * @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 * Load information about the IDs of related resources onto a collection

View File

@ -2,6 +2,7 @@
namespace Tobyz\JsonApiServer\Adapter; namespace Tobyz\JsonApiServer\Adapter;
use Closure;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -188,9 +189,13 @@ class EloquentAdapter implements AdapterInterface
$query->take($limit)->skip($offset); $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 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\Exception\BadRequestException;
use Tobyz\JsonApiServer\ResourceType; use Tobyz\JsonApiServer\ResourceType;
use Tobyz\JsonApiServer\Schema\Relationship; use Tobyz\JsonApiServer\Schema\Relationship;
use function Tobyz\JsonApiServer\run_callbacks;
trait IncludesData trait IncludesData
{ {
@ -103,9 +104,27 @@ trait IncludesData
$adapter = $this->resource->getAdapter(); $adapter = $this->resource->getAdapter();
$fields = $this->resource->getSchema()->getFields(); $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) { 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; continue;
} }
@ -115,16 +134,5 @@ trait IncludesData
$adapter->loadIds($models, $field); $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 $links = true;
private $loadable = true; private $loadable = true;
private $includable = false; private $includable = false;
private $scopes = [];
public function type($type) public function type($type)
{ {
@ -122,4 +123,14 @@ abstract class Relationship extends Field
{ {
return 'relationships'; return 'relationships';
} }
public function scope(Closure $callback)
{
$this->scopes[] = $callback;
}
public function getScopes(): array
{
return $this->scopes;
}
} }