diff --git a/src/Handler/Concerns/IncludesData.php b/src/Handler/Concerns/IncludesData.php index ef20730..39a0b67 100644 --- a/src/Handler/Concerns/IncludesData.php +++ b/src/Handler/Concerns/IncludesData.php @@ -57,9 +57,11 @@ trait IncludesData throw new BadRequestException("Invalid include [{$path}{$name}]", 'include'); } - $relatedResource = $this->api->getResource($schema->fields[$name]->resource); + if (is_string($schema->fields[$name]->resource)) { + $relatedResource = $this->api->getResource($schema->fields[$name]->resource); - $this->validateInclude($relatedResource, $nested, $name.'.'); + $this->validateInclude($relatedResource, $nested, $name.'.'); + } } } @@ -75,17 +77,19 @@ trait IncludesData $trails[] = [$relationship]; } - $relatedResource = $this->api->getResource($relationship->resource); + if (is_string($schema->fields[$name]->resource)) { + $relatedResource = $this->api->getResource($relationship->resource); - $trails = array_merge( - $trails, - array_map( - function ($trail) use ($relationship) { - return array_merge([$relationship], $trail); - }, - $this->buildRelationshipTrails($relatedResource, $nested) - ) - ); + $trails = array_merge( + $trails, + array_map( + function ($trail) use ($relationship) { + return array_merge([$relationship], $trail); + }, + $this->buildRelationshipTrails($relatedResource, $nested) + ) + ); + } } return $trails; @@ -101,13 +105,21 @@ trait IncludesData continue; } - $adapter->loadIds($models, $field); + if ($field->loader) { + ($field->loader)($models, true); + } else { + $adapter->loadIds($models, $field); + } } $trails = $this->buildRelationshipTrails($this->resource, $include); foreach ($trails as $relationships) { - $adapter->load($models, $relationships); + if ($loader = end($relationships)->loader) { + ($loader)($models, false); + } else { + $adapter->load($models, $relationships); + } } } } diff --git a/src/Handler/Index.php b/src/Handler/Index.php index 5764adf..47de1eb 100644 --- a/src/Handler/Index.php +++ b/src/Handler/Index.php @@ -10,6 +10,7 @@ use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Server\RequestHandlerInterface; use Tobscure\JsonApiServer\Api; use Tobscure\JsonApiServer\Exception\BadRequestException; +use Tobscure\JsonApiServer\Exception\ForbiddenException; use Tobscure\JsonApiServer\JsonApiResponse; use Tobscure\JsonApiServer\ResourceType; use Tobscure\JsonApiServer\Schema; @@ -37,6 +38,10 @@ class Index implements RequestHandlerInterface $adapter = $this->resource->getAdapter(); $schema = $this->resource->getSchema(); + if (! ($schema->isVisible)($request)) { + throw new ForbiddenException('You cannot view this resource'); + } + $query = $adapter->query(); foreach ($schema->scopes as $scope) { @@ -219,7 +224,7 @@ class Index implements RequestHandlerInterface $attribute = $schema->fields[$name]; if ($attribute->sorter) { - ($attribute->sorter)($query, $direction, $request); + ($attribute->sorter)($request, $query, $direction); } else { $adapter->sortByAttribute($query, $attribute, $direction); } diff --git a/src/Handler/Show.php b/src/Handler/Show.php index ebab0dd..3682a68 100644 --- a/src/Handler/Show.php +++ b/src/Handler/Show.php @@ -7,6 +7,7 @@ use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Server\RequestHandlerInterface; use Tobscure\JsonApiServer\Api; +use Tobscure\JsonApiServer\Exception\ForbiddenException; use Tobscure\JsonApiServer\JsonApiResponse; use Tobscure\JsonApiServer\ResourceType; use Tobscure\JsonApiServer\Serializer; @@ -28,6 +29,12 @@ class Show implements RequestHandlerInterface public function handle(Request $request): Response { + $schema = $this->resource->getSchema(); + + if (! ($schema->isVisible)($request)) { + throw new ForbiddenException('You cannot view this resource'); + } + $include = $this->getInclude($request); $this->loadRelationships([$this->model], $include, $request); diff --git a/src/Schema/Builder.php b/src/Schema/Builder.php index f7654fe..ef55237 100644 --- a/src/Schema/Builder.php +++ b/src/Schema/Builder.php @@ -30,6 +30,7 @@ class Builder public function __construct() { + $this->visible(); $this->notCreatable(); $this->notUpdatable(); $this->notDeletable(); @@ -40,7 +41,7 @@ class Builder return $this->field(Attribute::class, $name, $property); } - public function hasOne(string $name, string $resource = null, string $property = null): HasOne + public function hasOne(string $name, $resource = null, string $property = null): HasOne { $field = $this->field(HasOne::class, $name, $property); @@ -51,7 +52,7 @@ class Builder return $field; } - public function hasMany(string $name, string $resource = null, string $property = null): HasMany + public function hasMany(string $name, $resource = null, string $property = null): HasMany { $field = $this->field(HasMany::class, $name, $property); @@ -107,6 +108,34 @@ class Builder $this->singleScopes[] = $callback; } + public function visibleIf(Closure $condition) + { + $this->isVisible = $condition; + + return $this; + } + + public function visible() + { + return $this->visibleIf(function () { + return true; + }); + } + + public function notVisibleIf(Closure $condition) + { + return $this->visibleIf(function (...$args) use ($condition) { + return ! $condition(...$args); + }); + } + + public function notVisible() + { + return $this->notVisibleIf(function () { + return true; + }); + } + public function creatableIf(Closure $condition) { $this->isCreatable = $condition; diff --git a/src/Schema/Relationship.php b/src/Schema/Relationship.php index a128c4b..21f2c55 100644 --- a/src/Schema/Relationship.php +++ b/src/Schema/Relationship.php @@ -11,6 +11,7 @@ abstract class Relationship extends Field public $linkage; public $hasLinks = true; public $loadable = true; + public $loader; public $included = false; public $resource; @@ -70,6 +71,13 @@ abstract class Relationship extends Field return $this; } + public function load(Closure $callback) + { + $this->loader = $callback; + + return $this; + } + public function included() { $this->included = true; diff --git a/src/Serializer.php b/src/Serializer.php index 78ed6dc..20ab907 100644 --- a/src/Serializer.php +++ b/src/Serializer.php @@ -184,7 +184,17 @@ class Serializer private function addRelated(Schema\Relationship $field, $model, array $include): JsonApi\ResourceIdentifier { - $relatedResource = $this->api->getResource($field->resource); + if (is_array($field->resource)) { + foreach ($field->resource as $class => $resource) { + if ($model instanceof $class) { + break; + } + } + } else { + $resource = $field->resource; + } + + $relatedResource = $this->api->getResource($resource); return $this->resourceIdentifier( $this->addToMap($relatedResource, $model, $include)