mirror of
https://github.com/flarum/core.git
synced 2025-08-05 07:57:46 +02:00
feat: allow dependency injection in resources
This commit is contained in:
@@ -23,6 +23,7 @@ use Flarum\Http\RouteHandlerFactory;
|
|||||||
use Flarum\Http\UrlGenerator;
|
use Flarum\Http\UrlGenerator;
|
||||||
use Illuminate\Contracts\Container\Container;
|
use Illuminate\Contracts\Container\Container;
|
||||||
use Laminas\Stratigility\MiddlewarePipe;
|
use Laminas\Stratigility\MiddlewarePipe;
|
||||||
|
use ReflectionClass;
|
||||||
|
|
||||||
class ApiServiceProvider extends AbstractServiceProvider
|
class ApiServiceProvider extends AbstractServiceProvider
|
||||||
{
|
{
|
||||||
@@ -48,11 +49,12 @@ class ApiServiceProvider extends AbstractServiceProvider
|
|||||||
$resources = $this->container->make('flarum.api.resources');
|
$resources = $this->container->make('flarum.api.resources');
|
||||||
|
|
||||||
$api = new JsonApi('/');
|
$api = new JsonApi('/');
|
||||||
|
$api->container($container);
|
||||||
|
|
||||||
foreach ($resources as $resourceClass) {
|
foreach ($resources as $resourceClass) {
|
||||||
/** @var \Flarum\Api\Resource\AbstractResource|\Flarum\Api\Resource\AbstractDatabaseResource $resource */
|
/** @var \Flarum\Api\Resource\AbstractResource|\Flarum\Api\Resource\AbstractDatabaseResource $resource */
|
||||||
$resource = new $resourceClass;
|
$resource = $container->make($resourceClass);
|
||||||
$resource->boot($container);
|
$resource->boot($api);
|
||||||
$api->resource($resource);
|
$api->resource($resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,9 +63,9 @@ class ApiServiceProvider extends AbstractServiceProvider
|
|||||||
|
|
||||||
$this->container->alias('flarum.api.resource_handler', JsonApi::class);
|
$this->container->alias('flarum.api.resource_handler', JsonApi::class);
|
||||||
|
|
||||||
$this->container->singleton('flarum.api.routes', function () {
|
$this->container->singleton('flarum.api.routes', function (Container $container) {
|
||||||
$routes = new RouteCollection;
|
$routes = new RouteCollection;
|
||||||
$this->populateRoutes($routes);
|
$this->populateRoutes($routes, $container);
|
||||||
|
|
||||||
return $routes;
|
return $routes;
|
||||||
});
|
});
|
||||||
@@ -158,10 +160,10 @@ class ApiServiceProvider extends AbstractServiceProvider
|
|||||||
AbstractSerializer::setContainer($container);
|
AbstractSerializer::setContainer($container);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function populateRoutes(RouteCollection $routes): void
|
protected function populateRoutes(RouteCollection $routes, Container $container): void
|
||||||
{
|
{
|
||||||
/** @var RouteHandlerFactory $factory */
|
/** @var RouteHandlerFactory $factory */
|
||||||
$factory = $this->container->make(RouteHandlerFactory::class);
|
$factory = $container->make(RouteHandlerFactory::class);
|
||||||
|
|
||||||
$callback = include __DIR__.'/routes.php';
|
$callback = include __DIR__.'/routes.php';
|
||||||
$callback($routes, $factory);
|
$callback($routes, $factory);
|
||||||
@@ -169,16 +171,30 @@ class ApiServiceProvider extends AbstractServiceProvider
|
|||||||
$resources = $this->container->make('flarum.api.resources');
|
$resources = $this->container->make('flarum.api.resources');
|
||||||
|
|
||||||
foreach ($resources as $resourceClass) {
|
foreach ($resources as $resourceClass) {
|
||||||
/** @var \Flarum\Api\Resource\AbstractResource|\Flarum\Api\Resource\AbstractDatabaseResource $resource */
|
/**
|
||||||
$resource = new $resourceClass;
|
* This is an empty shell instance,
|
||||||
/** @var \Flarum\Api\Endpoint\Endpoint[] $endpoints */
|
* we only need it to get the endpoint routes and types.
|
||||||
$endpoints = $resource->endpoints();
|
*
|
||||||
|
* We avoid dependency injection here to avoid early resolution.
|
||||||
|
*
|
||||||
|
* @var \Flarum\Api\Resource\AbstractResource|\Flarum\Api\Resource\AbstractDatabaseResource $resource
|
||||||
|
*/
|
||||||
|
$resource = (new ReflectionClass($resourceClass))->newInstanceWithoutConstructor();
|
||||||
|
|
||||||
$type = $resource->type();
|
$type = $resource->type();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* None of the injected dependencies should be directly used within
|
||||||
|
* the `endpoints` method. Encourage using callbacks.
|
||||||
|
*
|
||||||
|
* @var \Flarum\Api\Endpoint\Endpoint[] $endpoints
|
||||||
|
*/
|
||||||
|
$endpoints = $resource->endpoints();
|
||||||
|
|
||||||
foreach ($endpoints as $endpoint) {
|
foreach ($endpoints as $endpoint) {
|
||||||
$route = $endpoint->route();
|
$route = $endpoint->route();
|
||||||
|
|
||||||
$routes->addRoute($route->method, rtrim("/$type$route->path", '/'), "$type.$route->name", $factory->toApiResource($resourceClass, $endpoint::class));
|
$routes->addRoute($route->method, rtrim("/$type$route->path", '/'), "$type.$route->name", $factory->toApiResource($resource::class, $endpoint::class));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +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;
|
||||||
|
|
||||||
class Context extends BaseContext
|
class Context extends BaseContext
|
||||||
|
@@ -1,74 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Flarum.
|
|
||||||
*
|
|
||||||
* For detailed copyright and license information, please view the
|
|
||||||
* LICENSE file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Flarum\Api\Controller;
|
|
||||||
|
|
||||||
use Flarum\Api\Serializer\DiscussionSerializer;
|
|
||||||
use Flarum\Discussion\Command\EditDiscussion;
|
|
||||||
use Flarum\Discussion\Command\ReadDiscussion;
|
|
||||||
use Flarum\Discussion\Discussion;
|
|
||||||
use Flarum\Http\RequestUtil;
|
|
||||||
use Flarum\Post\Post;
|
|
||||||
use Illuminate\Contracts\Bus\Dispatcher;
|
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
|
||||||
use Illuminate\Support\Arr;
|
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
|
||||||
use Tobscure\JsonApi\Document;
|
|
||||||
|
|
||||||
class UpdateDiscussionController extends AbstractShowController
|
|
||||||
{
|
|
||||||
public ?string $serializer = DiscussionSerializer::class;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
protected Dispatcher $bus
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function data(ServerRequestInterface $request, Document $document): Discussion
|
|
||||||
{
|
|
||||||
$actor = RequestUtil::getActor($request);
|
|
||||||
$discussionId = (int) Arr::get($request->getQueryParams(), 'id');
|
|
||||||
$data = Arr::get($request->getParsedBody(), 'data', []);
|
|
||||||
|
|
||||||
/** @var Discussion $discussion */
|
|
||||||
$discussion = $this->bus->dispatch(
|
|
||||||
new EditDiscussion($discussionId, $actor, $data)
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO: Refactor the ReadDiscussion (state) command into EditDiscussion?
|
|
||||||
// That's what extensions will do anyway.
|
|
||||||
if ($readNumber = Arr::get($data, 'attributes.lastReadPostNumber')) {
|
|
||||||
$state = $this->bus->dispatch(
|
|
||||||
new ReadDiscussion($discussionId, $actor, $readNumber)
|
|
||||||
);
|
|
||||||
|
|
||||||
$discussion = $state->discussion;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($posts = $discussion->getModifiedPosts()) {
|
|
||||||
/** @var Collection<int, Post> $posts */
|
|
||||||
$posts = (new Collection($posts))->load('discussion', 'user');
|
|
||||||
$discussionPosts = $discussion->posts()->whereVisibleTo($actor)->oldest()->pluck('id')->all();
|
|
||||||
|
|
||||||
foreach ($discussionPosts as &$id) {
|
|
||||||
foreach ($posts as $post) {
|
|
||||||
if ($id == $post->id) {
|
|
||||||
$id = $post;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$discussion->setRelation('posts', $discussionPosts);
|
|
||||||
|
|
||||||
$this->include = array_merge($this->include, ['posts', 'posts.discussion', 'posts.user']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $discussion;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -71,7 +71,7 @@ class Index extends BaseIndex implements Endpoint
|
|||||||
|
|
||||||
// This model has a searcher API, so we'll use that instead of the default.
|
// 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.
|
// The searcher API allows swapping the default search engine for a custom one.
|
||||||
$search = resolve(SearchManager::class);
|
$search = $context->api->getContainer()->make(SearchManager::class);
|
||||||
$modelClass = $query->getModel()::class;
|
$modelClass = $query->getModel()::class;
|
||||||
|
|
||||||
if ($query instanceof Builder && $search->searchable($modelClass)) {
|
if ($query instanceof Builder && $search->searchable($modelClass)) {
|
||||||
@@ -86,8 +86,7 @@ class Index extends BaseIndex implements Endpoint
|
|||||||
|
|
||||||
$sortIsDefault = ! $context->queryParam('sort');
|
$sortIsDefault = ! $context->queryParam('sort');
|
||||||
|
|
||||||
// @todo: resources and endpoints have no room for dependency injection
|
$results = $search->query(
|
||||||
$results = resolve(SearchManager::class)->query(
|
|
||||||
$modelClass,
|
$modelClass,
|
||||||
new SearchCriteria($actor, $filters, $limit, $offset, $sort, $sortIsDefault),
|
new SearchCriteria($actor, $filters, $limit, $offset, $sort, $sortIsDefault),
|
||||||
);
|
);
|
||||||
|
@@ -6,20 +6,22 @@ use Flarum\Api\Endpoint\Endpoint;
|
|||||||
use Flarum\Api\Endpoint\EndpointRoute;
|
use Flarum\Api\Endpoint\EndpointRoute;
|
||||||
use Flarum\Api\Resource\AbstractDatabaseResource;
|
use Flarum\Api\Resource\AbstractDatabaseResource;
|
||||||
use Flarum\Http\RequestUtil;
|
use Flarum\Http\RequestUtil;
|
||||||
|
use Illuminate\Contracts\Container\Container;
|
||||||
use Laminas\Diactoros\ServerRequestFactory;
|
use Laminas\Diactoros\ServerRequestFactory;
|
||||||
use Laminas\Diactoros\Uri;
|
use Laminas\Diactoros\Uri;
|
||||||
use Psr\Http\Message\ResponseInterface as Response;
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
use Tobyz\JsonApiServer\Exception\BadRequestException;
|
use Tobyz\JsonApiServer\Exception\BadRequestException;
|
||||||
use Tobyz\JsonApiServer\JsonApi as BaseJsonApi;
|
use Tobyz\JsonApiServer\JsonApi as BaseJsonApi;
|
||||||
use Tobyz\JsonApiServer\Resource\Collection;
|
use Tobyz\JsonApiServer\Resource\Collection;
|
||||||
|
use Tobyz\JsonApiServer\Resource\Resource;
|
||||||
|
|
||||||
class JsonApi extends BaseJsonApi
|
class JsonApi extends BaseJsonApi
|
||||||
{
|
{
|
||||||
protected string $resourceClass;
|
protected string $resourceClass;
|
||||||
protected string $endpoint;
|
protected string $endpoint;
|
||||||
protected ?Request $baseRequest = null;
|
protected ?Request $baseRequest = null;
|
||||||
|
protected ?Container $container = null;
|
||||||
|
|
||||||
public function forResource(string $resourceClass): self
|
public function forResource(string $resourceClass): self
|
||||||
{
|
{
|
||||||
@@ -41,7 +43,7 @@ class JsonApi extends BaseJsonApi
|
|||||||
throw new BadRequestException('No resource or endpoint specified');
|
throw new BadRequestException('No resource or endpoint specified');
|
||||||
}
|
}
|
||||||
|
|
||||||
$collection = $this->getCollection((new $this->resourceClass)->type());
|
$collection = $this->getCollection($this->resourceClass);
|
||||||
|
|
||||||
return (new Context($this, $request))
|
return (new Context($this, $request))
|
||||||
->withCollection($collection)
|
->withCollection($collection)
|
||||||
@@ -85,6 +87,8 @@ class JsonApi extends BaseJsonApi
|
|||||||
$request = RequestUtil::withActor($request, $options['actor']);
|
$request = RequestUtil::withActor($request, $options['actor']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$resource = $this->getCollection($this->resourceClass);
|
||||||
|
|
||||||
$request = $request
|
$request = $request
|
||||||
->withMethod($route->method)
|
->withMethod($route->method)
|
||||||
->withUri(new Uri($route->path))
|
->withUri(new Uri($route->path))
|
||||||
@@ -93,7 +97,9 @@ class JsonApi extends BaseJsonApi
|
|||||||
'data' => [
|
'data' => [
|
||||||
...($request->getParsedBody()['data'] ?? []),
|
...($request->getParsedBody()['data'] ?? []),
|
||||||
...($body['data'] ?? []),
|
...($body['data'] ?? []),
|
||||||
'type' => (new $this->resourceClass)->type(),
|
'type' => $resource instanceof Resource
|
||||||
|
? $resource->type()
|
||||||
|
: $resource->name(),
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -136,4 +142,16 @@ class JsonApi extends BaseJsonApi
|
|||||||
{
|
{
|
||||||
return array_values(array_unique(array_map(fn ($modelClass) => $this->typeForModel($modelClass), $modelClasses)));
|
return array_values(array_unique(array_map(fn ($modelClass) => $this->typeForModel($modelClass), $modelClasses)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function container(Container $container): static
|
||||||
|
{
|
||||||
|
$this->container = $container;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContainer(): ?Container
|
||||||
|
{
|
||||||
|
return $this->container;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,6 @@ abstract class AbstractDatabaseResource extends BaseResource implements
|
|||||||
use DispatchEventsTrait {
|
use DispatchEventsTrait {
|
||||||
dispatchEventsFor as traitDispatchEventsFor;
|
dispatchEventsFor as traitDispatchEventsFor;
|
||||||
}
|
}
|
||||||
use ResolvesValidationFactory;
|
|
||||||
|
|
||||||
abstract public function model(): string;
|
abstract public function model(): string;
|
||||||
|
|
||||||
@@ -147,7 +146,7 @@ abstract class AbstractDatabaseResource extends BaseResource implements
|
|||||||
$savingEvent = $this->newSavingEvent($context, Arr::get($context->body(), 'data', []));
|
$savingEvent = $this->newSavingEvent($context, Arr::get($context->body(), 'data', []));
|
||||||
|
|
||||||
if ($savingEvent) {
|
if ($savingEvent) {
|
||||||
$this->container->make(Dispatcher::class)->dispatch($savingEvent);
|
$this->events->dispatch($savingEvent);
|
||||||
|
|
||||||
$dirtyAfterEvent = $context->model->getDirty();
|
$dirtyAfterEvent = $context->model->getDirty();
|
||||||
|
|
||||||
|
@@ -9,5 +9,4 @@ use Tobyz\JsonApiServer\Resource\AbstractResource as BaseResource;
|
|||||||
abstract class AbstractResource extends BaseResource
|
abstract class AbstractResource extends BaseResource
|
||||||
{
|
{
|
||||||
use Bootable;
|
use Bootable;
|
||||||
use ResolvesValidationFactory;
|
|
||||||
}
|
}
|
||||||
|
@@ -19,6 +19,11 @@ use Jenssegers\Agent\Agent;
|
|||||||
|
|
||||||
class AccessTokenResource extends AbstractDatabaseResource
|
class AccessTokenResource extends AbstractDatabaseResource
|
||||||
{
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected TranslatorInterface $translator
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
public function type(): string
|
public function type(): string
|
||||||
{
|
{
|
||||||
return 'access-tokens';
|
return 'access-tokens';
|
||||||
@@ -84,12 +89,10 @@ class AccessTokenResource extends AbstractDatabaseResource
|
|||||||
Schema\Str::make('lastIpAddress'),
|
Schema\Str::make('lastIpAddress'),
|
||||||
Schema\Str::make('device')
|
Schema\Str::make('device')
|
||||||
->get(function (AccessToken $token) {
|
->get(function (AccessToken $token) {
|
||||||
$translator = resolve(TranslatorInterface::class);
|
|
||||||
|
|
||||||
$agent = new Agent();
|
$agent = new Agent();
|
||||||
$agent->setUserAgent($token->last_user_agent);
|
$agent->setUserAgent($token->last_user_agent);
|
||||||
|
|
||||||
return $translator->trans('core.forum.security.browser_on_operating_system', [
|
return $this->translator->trans('core.forum.security.browser_on_operating_system', [
|
||||||
'browser' => $agent->browser(),
|
'browser' => $agent->browser(),
|
||||||
'os' => $agent->platform(),
|
'os' => $agent->platform(),
|
||||||
]);
|
]);
|
||||||
|
@@ -2,17 +2,32 @@
|
|||||||
|
|
||||||
namespace Flarum\Api\Resource\Concerns;
|
namespace Flarum\Api\Resource\Concerns;
|
||||||
|
|
||||||
|
use Flarum\Api\JsonApi;
|
||||||
use Illuminate\Contracts\Container\Container;
|
use Illuminate\Contracts\Container\Container;
|
||||||
use Illuminate\Contracts\Events\Dispatcher;
|
use Illuminate\Contracts\Events\Dispatcher;
|
||||||
|
use Illuminate\Contracts\Validation\Factory;
|
||||||
|
|
||||||
trait Bootable
|
trait Bootable
|
||||||
{
|
{
|
||||||
protected readonly Container $container;
|
protected readonly JsonApi $api;
|
||||||
protected readonly Dispatcher $events;
|
protected readonly Dispatcher $events;
|
||||||
|
protected readonly Factory $validation;
|
||||||
|
|
||||||
public function boot(Container $container): void
|
/**
|
||||||
|
* Avoids polluting the constructor of the resource with dependencies.
|
||||||
|
*/
|
||||||
|
public function boot(JsonApi $api): void
|
||||||
{
|
{
|
||||||
$this->container = $container;
|
$this->api = $api;
|
||||||
$this->events = $container->make(Dispatcher::class);
|
$this->events = $api->getContainer()->make(Dispatcher::class);
|
||||||
|
$this->validation = $api->getContainer()->make(Factory::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the JSON:API server package to resolve the validation factory.
|
||||||
|
*/
|
||||||
|
public function validationFactory(): Factory
|
||||||
|
{
|
||||||
|
return $this->validation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,16 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Flarum\Api\Resource\Concerns;
|
|
||||||
|
|
||||||
use Illuminate\Contracts\Validation\Factory;
|
|
||||||
|
|
||||||
trait ResolvesValidationFactory
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Called by the JSON:API server package to resolve the validation factory.
|
|
||||||
*/
|
|
||||||
public function validationFactory(): Factory
|
|
||||||
{
|
|
||||||
return resolve(Factory::class);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -24,6 +24,13 @@ use Tobyz\JsonApiServer\Laravel\Sort\SortColumn;
|
|||||||
|
|
||||||
class DiscussionResource extends AbstractDatabaseResource
|
class DiscussionResource extends AbstractDatabaseResource
|
||||||
{
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected Dispatcher $bus,
|
||||||
|
protected SlugManager $slugManager,
|
||||||
|
protected PostRepository $posts
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
public function type(): string
|
public function type(): string
|
||||||
{
|
{
|
||||||
return 'discussions';
|
return 'discussions';
|
||||||
@@ -41,11 +48,10 @@ class DiscussionResource extends AbstractDatabaseResource
|
|||||||
|
|
||||||
public function find(string $id, \Tobyz\JsonApiServer\Context $context): ?object
|
public function find(string $id, \Tobyz\JsonApiServer\Context $context): ?object
|
||||||
{
|
{
|
||||||
$slugManager = resolve(SlugManager::class);
|
|
||||||
$actor = $context->getActor();
|
$actor = $context->getActor();
|
||||||
|
|
||||||
if (Arr::get($context->request->getQueryParams(), 'bySlug', false)) {
|
if (Arr::get($context->request->getQueryParams(), 'bySlug', false)) {
|
||||||
$discussion = $slugManager->forResource(Discussion::class)->fromSlug($id, $actor);
|
$discussion = $this->slugManager->forResource(Discussion::class)->fromSlug($id, $actor);
|
||||||
} else {
|
} else {
|
||||||
$discussion = $this->query($context)->findOrFail($id);
|
$discussion = $this->query($context)->findOrFail($id);
|
||||||
}
|
}
|
||||||
@@ -113,7 +119,7 @@ class DiscussionResource extends AbstractDatabaseResource
|
|||||||
->set(fn () => null),
|
->set(fn () => null),
|
||||||
Schema\Str::make('slug')
|
Schema\Str::make('slug')
|
||||||
->get(function (Discussion $discussion) {
|
->get(function (Discussion $discussion) {
|
||||||
return resolve(SlugManager::class)->forResource(Discussion::class)->toSlug($discussion);
|
return $this->slugManager->forResource(Discussion::class)->toSlug($discussion);
|
||||||
}),
|
}),
|
||||||
Schema\Integer::make('commentCount'),
|
Schema\Integer::make('commentCount'),
|
||||||
Schema\Integer::make('participantCount'),
|
Schema\Integer::make('participantCount'),
|
||||||
@@ -167,7 +173,7 @@ class DiscussionResource extends AbstractDatabaseResource
|
|||||||
->set(function (Discussion $discussion, int $value, Context $context) {
|
->set(function (Discussion $discussion, int $value, Context $context) {
|
||||||
if ($readNumber = Arr::get($context->body(), 'data.attributes.lastReadPostNumber')) {
|
if ($readNumber = Arr::get($context->body(), 'data.attributes.lastReadPostNumber')) {
|
||||||
$discussion->afterSave(function (Discussion $discussion) use ($readNumber, $context) {
|
$discussion->afterSave(function (Discussion $discussion) use ($readNumber, $context) {
|
||||||
resolve(Dispatcher::class)->dispatch(
|
$this->bus->dispatch(
|
||||||
new ReadDiscussion($discussion->id, $context->getActor(), $readNumber)
|
new ReadDiscussion($discussion->id, $context->getActor(), $readNumber)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -196,7 +202,7 @@ class DiscussionResource extends AbstractDatabaseResource
|
|||||||
$limit = $context->endpoint->extractLimitValue($context, $context->endpoint->defaultExtracts($context));
|
$limit = $context->endpoint->extractLimitValue($context, $context->endpoint->defaultExtracts($context));
|
||||||
|
|
||||||
if (($near = Arr::get($context->request->getQueryParams(), 'page.near')) > 1) {
|
if (($near = Arr::get($context->request->getQueryParams(), 'page.near')) > 1) {
|
||||||
$offset = resolve(PostRepository::class)->getIndexForNumber($discussion->id, $near, $actor);
|
$offset = $this->posts->getIndexForNumber($discussion->id, $near, $actor);
|
||||||
$offset = max(0, $offset - $limit / 2);
|
$offset = max(0, $offset - $limit / 2);
|
||||||
} else {
|
} else {
|
||||||
$offset = $context->endpoint->extractOffsetValue($context, $context->endpoint->defaultExtracts($context));
|
$offset = $context->endpoint->extractOffsetValue($context, $context->endpoint->defaultExtracts($context));
|
||||||
@@ -263,7 +269,7 @@ class DiscussionResource extends AbstractDatabaseResource
|
|||||||
$actor = $context->getActor();
|
$actor = $context->getActor();
|
||||||
|
|
||||||
if ($actor->exists) {
|
if ($actor->exists) {
|
||||||
resolve(Dispatcher::class)->dispatch(
|
$this->bus->dispatch(
|
||||||
new ReadDiscussion($model->id, $actor, 1)
|
new ReadDiscussion($model->id, $actor, 1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -12,10 +12,22 @@ use Flarum\Group\Group;
|
|||||||
use Flarum\Http\UrlGenerator;
|
use Flarum\Http\UrlGenerator;
|
||||||
use Flarum\Settings\SettingsRepositoryInterface;
|
use Flarum\Settings\SettingsRepositoryInterface;
|
||||||
use Illuminate\Contracts\Filesystem\Factory;
|
use Illuminate\Contracts\Filesystem\Factory;
|
||||||
|
use Illuminate\Contracts\Filesystem\Filesystem;
|
||||||
use stdClass;
|
use stdClass;
|
||||||
|
|
||||||
class ForumResource extends AbstractResource implements Findable
|
class ForumResource extends AbstractResource implements Findable
|
||||||
{
|
{
|
||||||
|
protected Filesystem $assetsFilesystem;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
protected UrlGenerator $url,
|
||||||
|
protected SettingsRepositoryInterface $settings,
|
||||||
|
protected Config $config,
|
||||||
|
Factory $filesystemFactory
|
||||||
|
) {
|
||||||
|
$this->assetsFilesystem = $filesystemFactory->disk('flarum-assets');
|
||||||
|
}
|
||||||
|
|
||||||
public function type(): string
|
public function type(): string
|
||||||
{
|
{
|
||||||
return 'forums';
|
return 'forums';
|
||||||
@@ -42,21 +54,16 @@ class ForumResource extends AbstractResource implements Findable
|
|||||||
|
|
||||||
public function fields(): array
|
public function fields(): array
|
||||||
{
|
{
|
||||||
$url = resolve(UrlGenerator::class);
|
$forumUrl = $this->url->to('forum')->base();
|
||||||
$settings = resolve(SettingsRepositoryInterface::class);
|
|
||||||
$config = resolve(Config::class);
|
|
||||||
$assetsFilesystem = resolve(Factory::class)->disk('flarum-assets');
|
|
||||||
|
|
||||||
$forumUrl = $url->to('forum')->base();
|
|
||||||
$path = parse_url($forumUrl, PHP_URL_PATH) ?: '';
|
$path = parse_url($forumUrl, PHP_URL_PATH) ?: '';
|
||||||
|
|
||||||
return [
|
return [
|
||||||
Schema\Str::make('title')
|
Schema\Str::make('title')
|
||||||
->get(fn () => $settings->get('forum_title')),
|
->get(fn () => $this->settings->get('forum_title')),
|
||||||
Schema\Str::make('description')
|
Schema\Str::make('description')
|
||||||
->get(fn () => $settings->get('forum_description')),
|
->get(fn () => $this->settings->get('forum_description')),
|
||||||
Schema\Boolean::make('showLanguageSelector')
|
Schema\Boolean::make('showLanguageSelector')
|
||||||
->get(fn () => $settings->get('show_language_selector', true)),
|
->get(fn () => $this->settings->get('show_language_selector', true)),
|
||||||
Schema\Str::make('baseUrl')
|
Schema\Str::make('baseUrl')
|
||||||
->get(fn () => $forumUrl),
|
->get(fn () => $forumUrl),
|
||||||
Schema\Str::make('basePath')
|
Schema\Str::make('basePath')
|
||||||
@@ -64,29 +71,29 @@ class ForumResource extends AbstractResource implements Findable
|
|||||||
Schema\Str::make('baseOrigin')
|
Schema\Str::make('baseOrigin')
|
||||||
->get(fn () => substr($forumUrl, 0, strlen($forumUrl) - strlen($path))),
|
->get(fn () => substr($forumUrl, 0, strlen($forumUrl) - strlen($path))),
|
||||||
Schema\Str::make('debug')
|
Schema\Str::make('debug')
|
||||||
->get(fn () => $config->inDebugMode()),
|
->get(fn () => $this->config->inDebugMode()),
|
||||||
Schema\Str::make('apiUrl')
|
Schema\Str::make('apiUrl')
|
||||||
->get(fn () => $url->to('api')->base()),
|
->get(fn () => $this->url->to('api')->base()),
|
||||||
Schema\Str::make('welcomeTitle')
|
Schema\Str::make('welcomeTitle')
|
||||||
->get(fn () => $settings->get('welcome_title')),
|
->get(fn () => $this->settings->get('welcome_title')),
|
||||||
Schema\Str::make('welcomeMessage')
|
Schema\Str::make('welcomeMessage')
|
||||||
->get(fn () => $settings->get('welcome_message')),
|
->get(fn () => $this->settings->get('welcome_message')),
|
||||||
Schema\Str::make('themePrimaryColor')
|
Schema\Str::make('themePrimaryColor')
|
||||||
->get(fn () => $settings->get('theme_primary_color')),
|
->get(fn () => $this->settings->get('theme_primary_color')),
|
||||||
Schema\Str::make('themeSecondaryColor')
|
Schema\Str::make('themeSecondaryColor')
|
||||||
->get(fn () => $settings->get('theme_secondary_color')),
|
->get(fn () => $this->settings->get('theme_secondary_color')),
|
||||||
Schema\Str::make('logoUrl')
|
Schema\Str::make('logoUrl')
|
||||||
->get(fn () => $this->getLogoUrl()),
|
->get(fn () => $this->getLogoUrl()),
|
||||||
Schema\Str::make('faviconUrl')
|
Schema\Str::make('faviconUrl')
|
||||||
->get(fn () => $this->getFaviconUrl()),
|
->get(fn () => $this->getFaviconUrl()),
|
||||||
Schema\Str::make('headerHtml')
|
Schema\Str::make('headerHtml')
|
||||||
->get(fn () => $settings->get('custom_header')),
|
->get(fn () => $this->settings->get('custom_header')),
|
||||||
Schema\Str::make('footerHtml')
|
Schema\Str::make('footerHtml')
|
||||||
->get(fn () => $settings->get('custom_footer')),
|
->get(fn () => $this->settings->get('custom_footer')),
|
||||||
Schema\Boolean::make('allowSignUp')
|
Schema\Boolean::make('allowSignUp')
|
||||||
->get(fn () => $settings->get('allow_sign_up')),
|
->get(fn () => $this->settings->get('allow_sign_up')),
|
||||||
Schema\Str::make('defaultRoute')
|
Schema\Str::make('defaultRoute')
|
||||||
->get(fn () => $settings->get('default_route')),
|
->get(fn () => $this->settings->get('default_route')),
|
||||||
Schema\Boolean::make('canViewForum')
|
Schema\Boolean::make('canViewForum')
|
||||||
->get(fn ($model, Context $context) => $context->getActor()->can('viewForum')),
|
->get(fn ($model, Context $context) => $context->getActor()->can('viewForum')),
|
||||||
Schema\Boolean::make('canStartDiscussion')
|
Schema\Boolean::make('canStartDiscussion')
|
||||||
@@ -100,13 +107,13 @@ class ForumResource extends AbstractResource implements Findable
|
|||||||
Schema\Boolean::make('canEditUserCredentials')
|
Schema\Boolean::make('canEditUserCredentials')
|
||||||
->get(fn ($model, Context $context) => $context->getActor()->hasPermission('user.editCredentials')),
|
->get(fn ($model, Context $context) => $context->getActor()->hasPermission('user.editCredentials')),
|
||||||
Schema\Str::make('assetsBaseUrl')
|
Schema\Str::make('assetsBaseUrl')
|
||||||
->get(fn () => rtrim($assetsFilesystem->url(''), '/')),
|
->get(fn () => rtrim($this->assetsFilesystem->url(''), '/')),
|
||||||
Schema\Str::make('jsChunksBaseUrl')
|
Schema\Str::make('jsChunksBaseUrl')
|
||||||
->get(fn () => $assetsFilesystem->url('js')),
|
->get(fn () => $this->assetsFilesystem->url('js')),
|
||||||
|
|
||||||
Schema\Str::make('adminUrl')
|
Schema\Str::make('adminUrl')
|
||||||
->visible(fn ($model, Context $context) => $context->getActor()->can('administrate'))
|
->visible(fn ($model, Context $context) => $context->getActor()->can('administrate'))
|
||||||
->get(fn () => $url->to('admin')->base()),
|
->get(fn () => $this->url->to('admin')->base()),
|
||||||
Schema\Str::make('version')
|
Schema\Str::make('version')
|
||||||
->visible(fn ($model, Context $context) => $context->getActor()->can('administrate'))
|
->visible(fn ($model, Context $context) => $context->getActor()->can('administrate'))
|
||||||
->get(fn () => Application::VERSION),
|
->get(fn () => Application::VERSION),
|
||||||
@@ -123,20 +130,20 @@ class ForumResource extends AbstractResource implements Findable
|
|||||||
|
|
||||||
protected function getLogoUrl(): ?string
|
protected function getLogoUrl(): ?string
|
||||||
{
|
{
|
||||||
$logoPath = resolve(SettingsRepositoryInterface::class)->get('logo_path');
|
$logoPath = $this->settings->get('logo_path');
|
||||||
|
|
||||||
return $logoPath ? $this->getAssetUrl($logoPath) : null;
|
return $logoPath ? $this->getAssetUrl($logoPath) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getFaviconUrl(): ?string
|
protected function getFaviconUrl(): ?string
|
||||||
{
|
{
|
||||||
$faviconPath = resolve(SettingsRepositoryInterface::class)->get('favicon_path');
|
$faviconPath = $this->settings->get('favicon_path');
|
||||||
|
|
||||||
return $faviconPath ? $this->getAssetUrl($faviconPath) : null;
|
return $faviconPath ? $this->getAssetUrl($faviconPath) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAssetUrl(string $assetPath): string
|
public function getAssetUrl(string $assetPath): string
|
||||||
{
|
{
|
||||||
return resolve(Factory::class)->disk('flarum-assets')->url($assetPath);
|
return $this->assetsFilesystem->url($assetPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,11 @@ use Tobyz\JsonApiServer\Laravel\Sort\SortColumn;
|
|||||||
|
|
||||||
class GroupResource extends AbstractDatabaseResource
|
class GroupResource extends AbstractDatabaseResource
|
||||||
{
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected TranslatorInterface $translator
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
public function type(): string
|
public function type(): string
|
||||||
{
|
{
|
||||||
return 'groups';
|
return 'groups';
|
||||||
@@ -92,7 +97,7 @@ class GroupResource extends AbstractDatabaseResource
|
|||||||
|
|
||||||
private function translateGroupName(string $name): string
|
private function translateGroupName(string $name): string
|
||||||
{
|
{
|
||||||
$translation = resolve(TranslatorInterface::class)->trans($key = 'core.group.'.strtolower($name));
|
$translation = $this->translator->trans($key = 'core.group.'.strtolower($name));
|
||||||
|
|
||||||
if ($translation !== $key) {
|
if ($translation !== $key) {
|
||||||
return $translation;
|
return $translation;
|
||||||
|
@@ -14,6 +14,12 @@ use Tobyz\JsonApiServer\Pagination\Pagination;
|
|||||||
|
|
||||||
class NotificationResource extends AbstractDatabaseResource
|
class NotificationResource extends AbstractDatabaseResource
|
||||||
{
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected Dispatcher $bus,
|
||||||
|
protected NotificationRepository $notifications,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
public function type(): string
|
public function type(): string
|
||||||
{
|
{
|
||||||
return 'notifications';
|
return 'notifications';
|
||||||
@@ -30,7 +36,7 @@ class NotificationResource extends AbstractDatabaseResource
|
|||||||
/** @var Pagination $pagination */
|
/** @var Pagination $pagination */
|
||||||
$pagination = ($context->endpoint->paginationResolver)($context);
|
$pagination = ($context->endpoint->paginationResolver)($context);
|
||||||
|
|
||||||
return resolve(NotificationRepository::class)->query($context->getActor(), $pagination->limit, $pagination->offset);
|
return $this->notifications->query($context->getActor(), $pagination->limit, $pagination->offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::query($context);
|
return parent::query($context);
|
||||||
@@ -57,7 +63,7 @@ class NotificationResource extends AbstractDatabaseResource
|
|||||||
|
|
||||||
public function fields(): array
|
public function fields(): array
|
||||||
{
|
{
|
||||||
$subjectTypes = resolve(JsonApi::class)->typesForModels(
|
$subjectTypes = $this->api->typesForModels(
|
||||||
(new Notification())->getSubjectModels()
|
(new Notification())->getSubjectModels()
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -71,7 +77,7 @@ class NotificationResource extends AbstractDatabaseResource
|
|||||||
->writable()
|
->writable()
|
||||||
->get(fn (Notification $notification) => (bool) $notification->read_at)
|
->get(fn (Notification $notification) => (bool) $notification->read_at)
|
||||||
->set(function (Notification $notification, Context $context) {
|
->set(function (Notification $notification, Context $context) {
|
||||||
resolve(Dispatcher::class)->dispatch(
|
$this->bus->dispatch(
|
||||||
new ReadNotification($notification->id, $context->getActor())
|
new ReadNotification($notification->id, $context->getActor())
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
@@ -23,6 +23,14 @@ use Tobyz\JsonApiServer\Laravel\Sort\SortColumn;
|
|||||||
|
|
||||||
class PostResource extends AbstractDatabaseResource
|
class PostResource extends AbstractDatabaseResource
|
||||||
{
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected PostRepository $posts,
|
||||||
|
protected TranslatorInterface $translator,
|
||||||
|
protected LogReporter $log,
|
||||||
|
protected Dispatcher $bus
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
public function type(): string
|
public function type(): string
|
||||||
{
|
{
|
||||||
return 'posts';
|
return 'posts';
|
||||||
@@ -115,7 +123,7 @@ class PostResource extends AbstractDatabaseResource
|
|||||||
}
|
}
|
||||||
|
|
||||||
$limit = $defaultExtracts['limit'];
|
$limit = $defaultExtracts['limit'];
|
||||||
$offset = resolve(PostRepository::class)->getIndexForNumber((int) $filter['discussion'], $near, $context->getActor());
|
$offset = $this->posts->getIndexForNumber((int) $filter['discussion'], $near, $context->getActor());
|
||||||
|
|
||||||
return max(0, $offset - $limit / 2);
|
return max(0, $offset - $limit / 2);
|
||||||
}
|
}
|
||||||
@@ -184,8 +192,8 @@ class PostResource extends AbstractDatabaseResource
|
|||||||
$rendered = $post->formatContent($context->request);
|
$rendered = $post->formatContent($context->request);
|
||||||
$post->setAttribute('renderFailed', false);
|
$post->setAttribute('renderFailed', false);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$rendered = resolve(TranslatorInterface::class)->trans('core.lib.error.render_failed_message');
|
$rendered = $this->translator->trans('core.lib.error.render_failed_message');
|
||||||
resolve(LogReporter::class)->report($e);
|
$this->log->report($e);
|
||||||
$post->setAttribute('renderFailed', true);
|
$post->setAttribute('renderFailed', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,7 +266,7 @@ class PostResource extends AbstractDatabaseResource
|
|||||||
// in the discussion; thus, we will mark the discussion as read if
|
// in the discussion; thus, we will mark the discussion as read if
|
||||||
// they are logged in.
|
// they are logged in.
|
||||||
if ($actor->exists) {
|
if ($actor->exists) {
|
||||||
resolve(Dispatcher::class)->dispatch(
|
$this->bus->dispatch(
|
||||||
new ReadDiscussion($model->discussion_id, $actor, $model->number)
|
new ReadDiscussion($model->discussion_id, $actor, $model->number)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ use Flarum\Foundation\ValidationException;
|
|||||||
use Flarum\Http\SlugManager;
|
use Flarum\Http\SlugManager;
|
||||||
use Flarum\Locale\TranslatorInterface;
|
use Flarum\Locale\TranslatorInterface;
|
||||||
use Flarum\Settings\SettingsRepositoryInterface;
|
use Flarum\Settings\SettingsRepositoryInterface;
|
||||||
|
use Flarum\User\AvatarUploader;
|
||||||
use Flarum\User\Event\Deleting;
|
use Flarum\User\Event\Deleting;
|
||||||
use Flarum\User\Event\GroupsChanged;
|
use Flarum\User\Event\GroupsChanged;
|
||||||
use Flarum\User\Event\RegisteringFromProvider;
|
use Flarum\User\Event\RegisteringFromProvider;
|
||||||
@@ -19,11 +20,21 @@ use Flarum\User\User;
|
|||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use Intervention\Image\ImageManager;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Tobyz\JsonApiServer\Laravel\Sort\SortColumn;
|
use Tobyz\JsonApiServer\Laravel\Sort\SortColumn;
|
||||||
|
|
||||||
class UserResource extends AbstractDatabaseResource
|
class UserResource extends AbstractDatabaseResource
|
||||||
{
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected TranslatorInterface $translator,
|
||||||
|
protected SlugManager $slugManager,
|
||||||
|
protected SettingsRepositoryInterface $settings,
|
||||||
|
protected ImageManager $imageManager,
|
||||||
|
protected AvatarUploader $avatarUploader
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
public function type(): string
|
public function type(): string
|
||||||
{
|
{
|
||||||
return 'users';
|
return 'users';
|
||||||
@@ -41,11 +52,10 @@ class UserResource extends AbstractDatabaseResource
|
|||||||
|
|
||||||
public function find(string $id, \Tobyz\JsonApiServer\Context $context): ?object
|
public function find(string $id, \Tobyz\JsonApiServer\Context $context): ?object
|
||||||
{
|
{
|
||||||
$slugManager = resolve(SlugManager::class);
|
|
||||||
$actor = $context->getActor();
|
$actor = $context->getActor();
|
||||||
|
|
||||||
if (Arr::get($context->request->getQueryParams(), 'bySlug', false)) {
|
if (Arr::get($context->request->getQueryParams(), 'bySlug', false)) {
|
||||||
$user = $slugManager->forResource(User::class)->fromSlug($id, $actor);
|
$user = $this->slugManager->forResource(User::class)->fromSlug($id, $actor);
|
||||||
} else {
|
} else {
|
||||||
$user = $this->query($context)->findOrFail($id);
|
$user = $this->query($context)->findOrFail($id);
|
||||||
}
|
}
|
||||||
@@ -58,9 +68,7 @@ class UserResource extends AbstractDatabaseResource
|
|||||||
return [
|
return [
|
||||||
Endpoint\Create::make()
|
Endpoint\Create::make()
|
||||||
->visible(function (Context $context) {
|
->visible(function (Context $context) {
|
||||||
$settings = resolve(SettingsRepositoryInterface::class);
|
if (! $this->settings->get('allow_sign_up')) {
|
||||||
|
|
||||||
if (! $settings->get('allow_sign_up')) {
|
|
||||||
return $context->getActor()->isAdmin();
|
return $context->getActor()->isAdmin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +109,7 @@ class UserResource extends AbstractDatabaseResource
|
|||||||
|
|
||||||
public function fields(): array
|
public function fields(): array
|
||||||
{
|
{
|
||||||
$translator = resolve(TranslatorInterface::class);
|
$translator = $this->translator;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
Schema\Str::make('username')
|
Schema\Str::make('username')
|
||||||
@@ -203,7 +211,7 @@ class UserResource extends AbstractDatabaseResource
|
|||||||
Schema\Str::make('avatarUrl'),
|
Schema\Str::make('avatarUrl'),
|
||||||
Schema\Str::make('slug')
|
Schema\Str::make('slug')
|
||||||
->get(function (User $user) {
|
->get(function (User $user) {
|
||||||
return resolve(SlugManager::class)->forResource(User::class)->toSlug($user);
|
return $this->slugManager->forResource(User::class)->toSlug($user);
|
||||||
}),
|
}),
|
||||||
Schema\DateTime::make('joinTime')
|
Schema\DateTime::make('joinTime')
|
||||||
->property('joined_at'),
|
->property('joined_at'),
|
||||||
@@ -363,12 +371,7 @@ class UserResource extends AbstractDatabaseResource
|
|||||||
*/
|
*/
|
||||||
private function uploadAvatarFromUrl(User $user, string $url): void
|
private function uploadAvatarFromUrl(User $user, string $url): void
|
||||||
{
|
{
|
||||||
// @todo: constructor dependency injection
|
$urlValidator = $this->validation->make(compact('url'), [
|
||||||
$this->validator = resolve(\Illuminate\Contracts\Validation\Factory::class);
|
|
||||||
$this->imageManager = resolve(\Intervention\Image\ImageManager::class);
|
|
||||||
$this->avatarUploader = resolve(\Flarum\User\AvatarUploader::class);
|
|
||||||
|
|
||||||
$urlValidator = $this->validator->make(compact('url'), [
|
|
||||||
'url' => 'required|active_url',
|
'url' => 'required|active_url',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
namespace Flarum\Http;
|
namespace Flarum\Http;
|
||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
|
use Flarum\Api\JsonApi;
|
||||||
use Flarum\Frontend\Controller as FrontendController;
|
use Flarum\Frontend\Controller as FrontendController;
|
||||||
use Illuminate\Contracts\Container\Container;
|
use Illuminate\Contracts\Container\Container;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
@@ -45,8 +46,8 @@ class RouteHandlerFactory
|
|||||||
public function toApiResource(string $resourceClass, string $endpointClass): Closure
|
public function toApiResource(string $resourceClass, string $endpointClass): Closure
|
||||||
{
|
{
|
||||||
return function (Request $request, array $routeParams) use ($resourceClass, $endpointClass) {
|
return function (Request $request, array $routeParams) use ($resourceClass, $endpointClass) {
|
||||||
/** @var \Flarum\Api\JsonApi $api */
|
/** @var JsonApi $api */
|
||||||
$api = $this->container->make(\Flarum\Api\JsonApi::class);
|
$api = $this->container->make(JsonApi::class);
|
||||||
|
|
||||||
$api->validateQueryParameters($request);
|
$api->validateQueryParameters($request);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user