mirror of
https://github.com/flarum/framework.git
synced 2024-12-12 22:53:37 +08:00
refactor: simplify index endpoint
This commit is contained in:
parent
f52c6238ba
commit
7756e330b3
|
@ -5,10 +5,7 @@ namespace Flarum\Api;
|
||||||
use Flarum\Http\RequestUtil;
|
use Flarum\Http\RequestUtil;
|
||||||
use Flarum\Search\SearchResults;
|
use Flarum\Search\SearchResults;
|
||||||
use Flarum\User\User;
|
use Flarum\User\User;
|
||||||
use Illuminate\Contracts\Container\Container;
|
|
||||||
use Tobyz\JsonApiServer\Context as BaseContext;
|
use Tobyz\JsonApiServer\Context as BaseContext;
|
||||||
use Tobyz\JsonApiServer\Resource\Collection;
|
|
||||||
use Tobyz\JsonApiServer\Resource\Resource;
|
|
||||||
|
|
||||||
class Context extends BaseContext
|
class Context extends BaseContext
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,7 +19,7 @@ class Create extends BaseCreate implements EndpointInterface
|
||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
$this->after(function (Context $context, object $model) {
|
$this->beforeSerialization(function (Context $context, object $model) {
|
||||||
$this->loadRelations(Collection::make([$model]), $context, $this->getInclude($context));
|
$this->loadRelations(Collection::make([$model]), $context, $this->getInclude($context));
|
||||||
|
|
||||||
return $model;
|
return $model;
|
||||||
|
|
|
@ -3,9 +3,11 @@
|
||||||
namespace Flarum\Api\Endpoint;
|
namespace Flarum\Api\Endpoint;
|
||||||
|
|
||||||
use Flarum\Api\Endpoint\Concerns\HasAuthorization;
|
use Flarum\Api\Endpoint\Concerns\HasAuthorization;
|
||||||
|
use Flarum\Api\Endpoint\Concerns\HasCustomHooks;
|
||||||
use Tobyz\JsonApiServer\Endpoint\Delete as BaseDelete;
|
use Tobyz\JsonApiServer\Endpoint\Delete as BaseDelete;
|
||||||
|
|
||||||
class Delete extends BaseDelete implements EndpointInterface
|
class Delete extends BaseDelete implements EndpointInterface
|
||||||
{
|
{
|
||||||
use HasAuthorization;
|
use HasAuthorization;
|
||||||
|
use HasCustomHooks;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,16 @@
|
||||||
|
|
||||||
namespace Flarum\Api\Endpoint;
|
namespace Flarum\Api\Endpoint;
|
||||||
|
|
||||||
|
use Flarum\Api\Endpoint\Concerns\ExtractsListingParams;
|
||||||
|
use Flarum\Api\Endpoint\Concerns\HasAuthorization;
|
||||||
|
use Flarum\Api\Endpoint\Concerns\HasCustomHooks;
|
||||||
|
use Flarum\Api\Endpoint\Concerns\HasEagerLoading;
|
||||||
use Tobyz\JsonApiServer\Endpoint\Endpoint as BaseEndpoint;
|
use Tobyz\JsonApiServer\Endpoint\Endpoint as BaseEndpoint;
|
||||||
|
|
||||||
class Endpoint extends BaseEndpoint implements EndpointInterface
|
class Endpoint extends BaseEndpoint implements EndpointInterface
|
||||||
{
|
{
|
||||||
//
|
use HasAuthorization;
|
||||||
|
use HasCustomHooks;
|
||||||
|
use HasEagerLoading;
|
||||||
|
use ExtractsListingParams;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,26 +2,18 @@
|
||||||
|
|
||||||
namespace Flarum\Api\Endpoint;
|
namespace Flarum\Api\Endpoint;
|
||||||
|
|
||||||
|
use Flarum\Api\Context;
|
||||||
use Flarum\Api\Endpoint\Concerns\ExtractsListingParams;
|
use Flarum\Api\Endpoint\Concerns\ExtractsListingParams;
|
||||||
use Flarum\Api\Endpoint\Concerns\HasAuthorization;
|
use Flarum\Api\Endpoint\Concerns\HasAuthorization;
|
||||||
use Flarum\Api\Endpoint\Concerns\HasCustomHooks;
|
use Flarum\Api\Endpoint\Concerns\HasCustomHooks;
|
||||||
use Flarum\Api\Endpoint\Concerns\HasCustomRoute;
|
|
||||||
use Flarum\Api\Endpoint\Concerns\HasEagerLoading;
|
use Flarum\Api\Endpoint\Concerns\HasEagerLoading;
|
||||||
use Flarum\Http\RequestUtil;
|
|
||||||
use Flarum\Search\SearchCriteria;
|
use Flarum\Search\SearchCriteria;
|
||||||
use Flarum\Search\SearchManager;
|
use Flarum\Search\SearchManager;
|
||||||
use Illuminate\Contracts\Database\Eloquent\Builder;
|
use Illuminate\Contracts\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Psr\Http\Message\ResponseInterface as Response;
|
|
||||||
use RuntimeException;
|
|
||||||
use Tobyz\JsonApiServer\Context;
|
|
||||||
use Tobyz\JsonApiServer\Endpoint\Index as BaseIndex;
|
use Tobyz\JsonApiServer\Endpoint\Index as BaseIndex;
|
||||||
use Tobyz\JsonApiServer\Exception\ForbiddenException;
|
|
||||||
use Tobyz\JsonApiServer\Pagination\OffsetPagination;
|
use Tobyz\JsonApiServer\Pagination\OffsetPagination;
|
||||||
use Tobyz\JsonApiServer\Resource\Countable;
|
use Tobyz\JsonApiServer\Pagination\Pagination;
|
||||||
use Tobyz\JsonApiServer\Resource\Listable;
|
|
||||||
use Tobyz\JsonApiServer\Serializer;
|
|
||||||
use function Tobyz\JsonApiServer\json_api_response;
|
|
||||||
|
|
||||||
class Index extends BaseIndex implements EndpointInterface
|
class Index extends BaseIndex implements EndpointInterface
|
||||||
{
|
{
|
||||||
|
@ -30,6 +22,53 @@ class Index extends BaseIndex implements EndpointInterface
|
||||||
use ExtractsListingParams;
|
use ExtractsListingParams;
|
||||||
use HasCustomHooks;
|
use HasCustomHooks;
|
||||||
|
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this
|
||||||
|
->query(function ($query, ?Pagination $pagination, Context $context): Context {
|
||||||
|
// This model has a searcher API, so we'll use that instead of the default.
|
||||||
|
// The searcher API allows swapping the default search engine for a custom one.
|
||||||
|
$search = $context->api->getContainer()->make(SearchManager::class);
|
||||||
|
$modelClass = $query->getModel()::class;
|
||||||
|
|
||||||
|
if ($query instanceof Builder && $search->searchable($modelClass)) {
|
||||||
|
$actor = $context->getActor();
|
||||||
|
|
||||||
|
$extracts = $this->defaultExtracts($context);
|
||||||
|
|
||||||
|
$filters = $this->extractFilterValue($context, $extracts);
|
||||||
|
$sort = $this->extractSortValue($context, $extracts);
|
||||||
|
$limit = $this->extractLimitValue($context, $extracts);
|
||||||
|
$offset = $this->extractOffsetValue($context, $extracts);
|
||||||
|
|
||||||
|
$sortIsDefault = ! $context->queryParam('sort');
|
||||||
|
|
||||||
|
$results = $search->query(
|
||||||
|
$modelClass,
|
||||||
|
new SearchCriteria($actor, $filters, $limit, $offset, $sort, $sortIsDefault),
|
||||||
|
);
|
||||||
|
|
||||||
|
$context = $context->withSearchResults($results);
|
||||||
|
}
|
||||||
|
// If the model doesn't have a searcher API, we'll just use the default logic.
|
||||||
|
else {
|
||||||
|
$context = $context->withQuery($query);
|
||||||
|
|
||||||
|
$this->applySorts($query, $context);
|
||||||
|
$this->applyFilters($query, $context);
|
||||||
|
|
||||||
|
$pagination?->apply($query);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $context;
|
||||||
|
})
|
||||||
|
->beforeSerialization(function (Context $context, iterable $models) {
|
||||||
|
$this->loadRelations(Collection::make($models), $context, $this->getInclude($context));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public function paginate(int $defaultLimit = 20, int $maxLimit = 50): static
|
public function paginate(int $defaultLimit = 20, int $maxLimit = 50): static
|
||||||
{
|
{
|
||||||
$this->limit = $defaultLimit;
|
$this->limit = $defaultLimit;
|
||||||
|
@ -43,97 +82,4 @@ class Index extends BaseIndex implements EndpointInterface
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
|
||||||
public function handle(Context $context): ?Response
|
|
||||||
{
|
|
||||||
$collection = $context->collection;
|
|
||||||
|
|
||||||
if (!$collection instanceof Listable) {
|
|
||||||
throw new RuntimeException(
|
|
||||||
sprintf('%s must implement %s', get_class($collection), Listable::class),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->isVisible($context)) {
|
|
||||||
throw new ForbiddenException();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->callBeforeHook($context);
|
|
||||||
|
|
||||||
$pagination = ($this->paginationResolver)($context);
|
|
||||||
|
|
||||||
$query = $collection->query($context);
|
|
||||||
|
|
||||||
// This model has a searcher API, so we'll use that instead of the default.
|
|
||||||
// The searcher API allows swapping the default search engine for a custom one.
|
|
||||||
$search = $context->api->getContainer()->make(SearchManager::class);
|
|
||||||
$modelClass = $query->getModel()::class;
|
|
||||||
|
|
||||||
if ($query instanceof Builder && $search->searchable($modelClass)) {
|
|
||||||
$actor = $context->getActor();
|
|
||||||
|
|
||||||
$extracts = $this->defaultExtracts($context);
|
|
||||||
|
|
||||||
$filters = $this->extractFilterValue($context, $extracts);
|
|
||||||
$sort = $this->extractSortValue($context, $extracts);
|
|
||||||
$limit = $this->extractLimitValue($context, $extracts);
|
|
||||||
$offset = $this->extractOffsetValue($context, $extracts);
|
|
||||||
|
|
||||||
$sortIsDefault = ! $context->queryParam('sort');
|
|
||||||
|
|
||||||
$results = $search->query(
|
|
||||||
$modelClass,
|
|
||||||
new SearchCriteria($actor, $filters, $limit, $offset, $sort, $sortIsDefault),
|
|
||||||
);
|
|
||||||
|
|
||||||
$context = $context->withSearchResults($results);
|
|
||||||
}
|
|
||||||
// If the model doesn't have a searcher API, we'll just use the default logic.
|
|
||||||
else {
|
|
||||||
$context = $context->withQuery($query);
|
|
||||||
|
|
||||||
$this->applySorts($query, $context);
|
|
||||||
$this->applyFilters($query, $context);
|
|
||||||
|
|
||||||
$pagination?->apply($query);
|
|
||||||
}
|
|
||||||
|
|
||||||
$meta = $this->serializeMeta($context);
|
|
||||||
$links = [];
|
|
||||||
|
|
||||||
if (
|
|
||||||
$collection instanceof Countable &&
|
|
||||||
!is_null($total = $collection->count($query, $context))
|
|
||||||
) {
|
|
||||||
$meta['page']['total'] = $total;
|
|
||||||
}
|
|
||||||
|
|
||||||
$models = $collection->results($query, $context);
|
|
||||||
|
|
||||||
$models = $this->callAfterHook($context, $models);
|
|
||||||
|
|
||||||
$include = $this->getInclude($context);
|
|
||||||
|
|
||||||
$this->loadRelations($models, $context, $include);
|
|
||||||
|
|
||||||
$serializer = new Serializer($context);
|
|
||||||
|
|
||||||
foreach ($models as $model) {
|
|
||||||
$serializer->addPrimary(
|
|
||||||
$context->resource($collection->resource($model, $context)),
|
|
||||||
$model,
|
|
||||||
$include,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
[$data, $included] = $serializer->serialize();
|
|
||||||
|
|
||||||
if ($pagination) {
|
|
||||||
$meta['page'] = array_merge($meta['page'] ?? [], $pagination->meta());
|
|
||||||
$links = array_merge($links, $pagination->links(count($data), $total ?? null));
|
|
||||||
}
|
|
||||||
|
|
||||||
return json_api_response(compact('data', 'included', 'meta', 'links'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,8 @@ class Show extends BaseShow implements EndpointInterface
|
||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
$this->after(function (Context $context, object $model) {
|
$this->beforeSerialization(function (Context $context, object $model) {
|
||||||
$this->loadRelations(Collection::make([$model]), $context, $this->getInclude($context));
|
$this->loadRelations(Collection::make([$model]), $context, $this->getInclude($context));
|
||||||
|
|
||||||
return $model;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,8 @@ class Update extends BaseUpdate implements EndpointInterface
|
||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
$this->after(function (Context $context, object $model) {
|
$this->beforeSerialization(function (Context $context, object $model) {
|
||||||
$this->loadRelations(Collection::make([$model]), $context, $this->getInclude($context));
|
$this->loadRelations(Collection::make([$model]), $context, $this->getInclude($context));
|
||||||
|
|
||||||
return $model;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,7 +149,7 @@ class ThemeTest extends TestCase
|
||||||
|
|
||||||
$response = $this->send($this->request('GET', '/'));
|
$response = $this->send($this->request('GET', '/'));
|
||||||
|
|
||||||
$this->assertEquals(200, $response->getStatusCode());
|
$this->assertEquals(200, $response->getStatusCode(), $response->getBody()->getContents());
|
||||||
|
|
||||||
$cssFilePath = $this->app()->getContainer()->make('filesystem')->disk('flarum-assets')->path('forum.css');
|
$cssFilePath = $this->app()->getContainer()->make('filesystem')->disk('flarum-assets')->path('forum.css');
|
||||||
$contents = file_get_contents($cssFilePath);
|
$contents = file_get_contents($cssFilePath);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user