Expose scope and filter as public API

This commit is contained in:
Toby Zerner 2021-05-18 16:19:52 +09:30
parent 848a8df42d
commit 5753e3a17c
2 changed files with 78 additions and 73 deletions

View File

@ -17,15 +17,12 @@ use JsonApiPhp\JsonApi\Link\NextLink;
use JsonApiPhp\JsonApi\Link\PrevLink; use JsonApiPhp\JsonApi\Link\PrevLink;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface as Request; 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\BadRequestException;
use Tobyz\JsonApiServer\Exception\ForbiddenException; use Tobyz\JsonApiServer\Exception\ForbiddenException;
use Tobyz\JsonApiServer\JsonApi; use Tobyz\JsonApiServer\JsonApi;
use Tobyz\JsonApiServer\ResourceType; use Tobyz\JsonApiServer\ResourceType;
use Tobyz\JsonApiServer\Schema\Attribute; 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\Schema\Meta;
use Tobyz\JsonApiServer\Serializer; use Tobyz\JsonApiServer\Serializer;
use function Tobyz\JsonApiServer\evaluate; use function Tobyz\JsonApiServer\evaluate;
@ -59,13 +56,16 @@ class Index
$query = $adapter->newQuery($context); $query = $adapter->newQuery($context);
run_callbacks($schema->getListeners('scope'), [$query, $context]); $this->resource->scope($query, $context);
$include = $this->getInclude($context); $include = $this->getInclude($context);
[$offset, $limit] = $this->paginate($query, $context); [$offset, $limit] = $this->paginate($query, $context);
$this->sort($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]); run_callbacks($schema->getListeners('listing'), [$query, $context]);
@ -234,71 +234,4 @@ class Index
return [$offset, $limit]; 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);
}
} }

View File

@ -12,6 +12,10 @@
namespace Tobyz\JsonApiServer; namespace Tobyz\JsonApiServer;
use Tobyz\JsonApiServer\Adapter\AdapterInterface; 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; use Tobyz\JsonApiServer\Schema\Type;
final class ResourceType final class ResourceType
@ -50,4 +54,72 @@ final class ResourceType
return $this->schema; 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);
}
} }