Allow relationships to specify adapter-specific scope logic
This commit is contained in:
parent
4a001556d8
commit
4883a8b595
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue