diff --git a/src/Endpoint/Index.php b/src/Endpoint/Index.php index 9f3eb27..7f53440 100644 --- a/src/Endpoint/Index.php +++ b/src/Endpoint/Index.php @@ -17,15 +17,12 @@ use JsonApiPhp\JsonApi\Link\NextLink; use JsonApiPhp\JsonApi\Link\PrevLink; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface as Request; -use Tobyz\JsonApiServer\Adapter\AdapterInterface; +use Tobyz\JsonApiServer\Context; use Tobyz\JsonApiServer\Exception\BadRequestException; use Tobyz\JsonApiServer\Exception\ForbiddenException; use Tobyz\JsonApiServer\JsonApi; use Tobyz\JsonApiServer\ResourceType; use Tobyz\JsonApiServer\Schema\Attribute; -use Tobyz\JsonApiServer\Context; -use Tobyz\JsonApiServer\Schema\HasMany; -use Tobyz\JsonApiServer\Schema\HasOne; use Tobyz\JsonApiServer\Schema\Meta; use Tobyz\JsonApiServer\Serializer; use function Tobyz\JsonApiServer\evaluate; @@ -59,13 +56,16 @@ class Index $query = $adapter->newQuery($context); - run_callbacks($schema->getListeners('scope'), [$query, $context]); + $this->resource->scope($query, $context); $include = $this->getInclude($context); [$offset, $limit] = $this->paginate($query, $context); $this->sort($query, $context); - $this->filter($query, $context); + + if ($filter = $context->getRequest()->getQueryParams()['filter'] ?? null) { + $this->resource->filter($query, $filter, $context); + } run_callbacks($schema->getListeners('listing'), [$query, $context]); @@ -234,71 +234,4 @@ class Index return [$offset, $limit]; } - - private function filter($query, Context $context) - { - if (! $filter = $context->getRequest()->getQueryParams()['filter'] ?? null) { - return; - } - - if (! is_array($filter)) { - throw new BadRequestException('filter must be an array', 'filter'); - } - - $schema = $this->resource->getSchema(); - $adapter = $this->resource->getAdapter(); - $filters = $schema->getFilters(); - $fields = $schema->getFields(); - - foreach ($filter as $name => $value) { - if ($name === 'id') { - $adapter->filterByIds($query, explode(',', $value)); - continue; - } - - if (isset($filters[$name]) && evaluate($filters[$name]->getVisible(), [$context])) { - $filters[$name]->getCallback()($query, $value, $context); - continue; - } - - if (isset($fields[$name]) && evaluate($fields[$name]->getFilterable(), [$context])) { - if ($fields[$name] instanceof Attribute) { - $this->filterByAttribute($adapter, $query, $fields[$name], $value); - } elseif ($fields[$name] instanceof HasOne) { - $value = array_filter(explode(',', $value)); - $adapter->filterByHasOne($query, $fields[$name], $value); - } elseif ($fields[$name] instanceof HasMany) { - $value = array_filter(explode(',', $value)); - $adapter->filterByHasMany($query, $fields[$name], $value); - } - continue; - } - - throw new BadRequestException("Invalid filter [$name]", "filter[$name]"); - } - } - - private function filterByAttribute(AdapterInterface $adapter, $query, Attribute $attribute, $value) - { - if (preg_match('/(.+)\.\.(.+)/', $value, $matches)) { - if ($matches[1] !== '*') { - $adapter->filterByAttribute($query, $attribute, $value, '>='); - } - if ($matches[2] !== '*') { - $adapter->filterByAttribute($query, $attribute, $value, '<='); - } - - return; - } - - foreach (['>=', '>', '<=', '<'] as $operator) { - if (strpos($value, $operator) === 0) { - $adapter->filterByAttribute($query, $attribute, substr($value, strlen($operator)), $operator); - - return; - } - } - - $adapter->filterByAttribute($query, $attribute, $value); - } } diff --git a/src/ResourceType.php b/src/ResourceType.php index c23d27c..cc9df44 100644 --- a/src/ResourceType.php +++ b/src/ResourceType.php @@ -12,6 +12,10 @@ namespace Tobyz\JsonApiServer; use Tobyz\JsonApiServer\Adapter\AdapterInterface; +use Tobyz\JsonApiServer\Exception\BadRequestException; +use Tobyz\JsonApiServer\Schema\Attribute; +use Tobyz\JsonApiServer\Schema\HasMany; +use Tobyz\JsonApiServer\Schema\HasOne; use Tobyz\JsonApiServer\Schema\Type; final class ResourceType @@ -50,4 +54,72 @@ final class ResourceType return $this->schema; } + + public function scope($query, Context $context) + { + run_callbacks($this->getSchema()->getListeners('scope'), [$query, $context]); + } + + public function filter($query, $filter, Context $context) + { + if (! is_array($filter)) { + throw new BadRequestException('filter must be an array', 'filter'); + } + + $schema = $this->getSchema(); + $adapter = $this->getAdapter(); + $filters = $schema->getFilters(); + $fields = $schema->getFields(); + + foreach ($filter as $name => $value) { + if ($name === 'id') { + $adapter->filterByIds($query, explode(',', $value)); + continue; + } + + if (isset($filters[$name]) && evaluate($filters[$name]->getVisible(), [$context])) { + $filters[$name]->getCallback()($query, $value, $context); + continue; + } + + if (isset($fields[$name]) && evaluate($fields[$name]->getFilterable(), [$context])) { + if ($fields[$name] instanceof Attribute) { + $this->filterByAttribute($adapter, $query, $fields[$name], $value); + } elseif ($fields[$name] instanceof HasOne) { + $value = array_filter(explode(',', $value)); + $adapter->filterByHasOne($query, $fields[$name], $value); + } elseif ($fields[$name] instanceof HasMany) { + $value = array_filter(explode(',', $value)); + $adapter->filterByHasMany($query, $fields[$name], $value); + } + continue; + } + + throw new BadRequestException("Invalid filter [$name]", "filter[$name]"); + } + } + + private function filterByAttribute(AdapterInterface $adapter, $query, Attribute $attribute, $value) + { + if (preg_match('/(.+)\.\.(.+)/', $value, $matches)) { + if ($matches[1] !== '*') { + $adapter->filterByAttribute($query, $attribute, $value, '>='); + } + if ($matches[2] !== '*') { + $adapter->filterByAttribute($query, $attribute, $value, '<='); + } + + return; + } + + foreach (['>=', '>', '<=', '<'] as $operator) { + if (strpos($value, $operator) === 0) { + $adapter->filterByAttribute($query, $attribute, substr($value, strlen($operator)), $operator); + + return; + } + } + + $adapter->filterByAttribute($query, $attribute, $value); + } }