139 lines
4.2 KiB
PHP
139 lines
4.2 KiB
PHP
<?php
|
|
|
|
namespace Tobyz\JsonApiServer\Handler\Concerns;
|
|
|
|
use Closure;
|
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
|
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
|
|
{
|
|
private function getInclude(Request $request): array
|
|
{
|
|
$queryParams = $request->getQueryParams();
|
|
|
|
if (! empty($queryParams['include'])) {
|
|
$include = $this->parseInclude($queryParams['include']);
|
|
|
|
$this->validateInclude($this->resource, $include);
|
|
|
|
return $include;
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
private function parseInclude(string $include): array
|
|
{
|
|
$tree = [];
|
|
|
|
foreach (explode(',', $include) as $path) {
|
|
$keys = explode('.', $path);
|
|
$array = &$tree;
|
|
|
|
foreach ($keys as $key) {
|
|
if (! isset($array[$key])) {
|
|
$array[$key] = [];
|
|
}
|
|
|
|
$array = &$array[$key];
|
|
}
|
|
}
|
|
|
|
return $tree;
|
|
}
|
|
|
|
private function validateInclude(ResourceType $resource, array $include, string $path = '')
|
|
{
|
|
$fields = $resource->getSchema()->getFields();
|
|
|
|
foreach ($include as $name => $nested) {
|
|
if (! isset($fields[$name])
|
|
|| ! $fields[$name] instanceof Relationship
|
|
|| ! $fields[$name]->isIncludable()
|
|
) {
|
|
throw new BadRequestException("Invalid include [{$path}{$name}]", 'include');
|
|
}
|
|
|
|
if ($type = $fields[$name]->getType()) {
|
|
$relatedResource = $this->api->getResource($type);
|
|
|
|
$this->validateInclude($relatedResource, $nested, $name.'.');
|
|
} elseif ($nested) {
|
|
throw new BadRequestException("Invalid include [{$path}{$name}.*]", 'include');
|
|
}
|
|
}
|
|
}
|
|
|
|
private function buildRelationshipTrails(ResourceType $resource, array $include): array
|
|
{
|
|
$fields = $resource->getSchema()->getFields();
|
|
$trails = [];
|
|
|
|
foreach ($include as $name => $nested) {
|
|
$relationship = $fields[$name];
|
|
|
|
if ($relationship->getLoadable()) {
|
|
$trails[] = [$relationship];
|
|
}
|
|
|
|
if ($type = $fields[$name]->getType()) {
|
|
$relatedResource = $this->api->getResource($type);
|
|
|
|
$trails = array_merge(
|
|
$trails,
|
|
array_map(
|
|
function ($trail) use ($relationship) {
|
|
return array_merge([$relationship], $trail);
|
|
},
|
|
$this->buildRelationshipTrails($relatedResource, $nested)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
return $trails;
|
|
}
|
|
|
|
private function loadRelationships(array $models, array $include, Request $request)
|
|
{
|
|
$adapter = $this->resource->getAdapter();
|
|
$fields = $this->resource->getSchema()->getFields();
|
|
|
|
$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() || in_array($field, $loaded)) {
|
|
continue;
|
|
}
|
|
|
|
if (($load = $field->getLoadable()) instanceof Closure) {
|
|
$load($models, true);
|
|
} else {
|
|
$adapter->loadIds($models, $field);
|
|
}
|
|
}
|
|
}
|
|
}
|