1
0
mirror of https://github.com/flarum/core.git synced 2025-08-06 16:36:47 +02:00

ApiController Extender and Tests (#2451)

This commit is contained in:
Sami Mazouz
2020-12-06 21:07:48 +01:00
committed by GitHub
parent 056d420c7b
commit 51a97fb12e
6 changed files with 1237 additions and 0 deletions

View File

@@ -82,6 +82,16 @@ abstract class AbstractSerializeController implements RequestHandlerInterface
*/
protected static $events;
/**
* @var array
*/
protected static $beforeDataCallbacks = [];
/**
* @var array
*/
protected static $beforeSerializationCallbacks = [];
/**
* {@inheritdoc}
*/
@@ -89,12 +99,30 @@ abstract class AbstractSerializeController implements RequestHandlerInterface
{
$document = new Document;
foreach (array_reverse(array_merge([static::class], class_parents($this))) as $class) {
if (isset(static::$beforeDataCallbacks[$class])) {
foreach (static::$beforeDataCallbacks[$class] as $callback) {
$callback($this);
}
}
}
// Deprected in beta 15, removed in beta 16
static::$events->dispatch(
new WillGetData($this)
);
$data = $this->data($request, $document);
foreach (array_reverse(array_merge([static::class], class_parents($this))) as $class) {
if (isset(static::$beforeSerializationCallbacks[$class])) {
foreach (static::$beforeSerializationCallbacks[$class] as $callback) {
$callback($this, $data, $request, $document);
}
}
}
// Deprecated in beta 15, removed in beta 16
static::$events->dispatch(
new WillSerializeData($this, $data, $request, $document)
);
@@ -197,6 +225,106 @@ abstract class AbstractSerializeController implements RequestHandlerInterface
return new Parameters($request->getQueryParams());
}
/**
* Set the serializer that will serialize data for the endpoint.
*
* @param string $serializer
*/
public function setSerializer(string $serializer)
{
$this->serializer = $serializer;
}
/**
* Include the given relationship by default.
*
* @param string|array $name
*/
public function addInclude($name)
{
$this->include = array_merge($this->include, (array) $name);
}
/**
* Don't include the given relationship by default.
*
* @param string|array $name
*/
public function removeInclude($name)
{
$this->include = array_diff($this->include, (array) $name);
}
/**
* Make the given relationship available for inclusion.
*
* @param string|array $name
*/
public function addOptionalInclude($name)
{
$this->optionalInclude = array_merge($this->optionalInclude, (array) $name);
}
/**
* Don't allow the given relationship to be included.
*
* @param string|array $name
*/
public function removeOptionalInclude($name)
{
$this->optionalInclude = array_diff($this->optionalInclude, (array) $name);
}
/**
* Set the default number of results.
*
* @param int $limit
*/
public function setLimit(int $limit)
{
$this->limit = $limit;
}
/**
* Set the maximum number of results.
*
* @param int $max
*/
public function setMaxLimit(int $max)
{
$this->maxLimit = $max;
}
/**
* Allow sorting results by the given field.
*
* @param string|array $field
*/
public function addSortField($field)
{
$this->sortFields = array_merge($this->sortFields, (array) $field);
}
/**
* Disallow sorting results by the given field.
*
* @param string|array $field
*/
public function removeSortField($field)
{
$this->sortFields = array_diff($this->sortFields, (array) $field);
}
/**
* Set the default sort order for the results.
*
* @param array $sort
*/
public function setSort(array $sort)
{
$this->sort = $sort;
}
/**
* @return Dispatcher
*/
@@ -228,4 +356,30 @@ abstract class AbstractSerializeController implements RequestHandlerInterface
{
static::$container = $container;
}
/**
* @param string $controllerClass
* @param callable $callback
*/
public static function addDataPreparationCallback(string $controllerClass, callable $callback)
{
if (! isset(static::$beforeDataCallbacks[$controllerClass])) {
static::$beforeDataCallbacks[$controllerClass] = [];
}
static::$beforeDataCallbacks[$controllerClass][] = $callback;
}
/**
* @param string $controllerClass
* @param callable $callback
*/
public static function addSerializationPreparationCallback(string $controllerClass, callable $callback)
{
if (! isset(static::$beforeSerializationCallbacks[$controllerClass])) {
static::$beforeSerializationCallbacks[$controllerClass] = [];
}
static::$beforeSerializationCallbacks[$controllerClass][] = $callback;
}
}

View File

@@ -12,6 +12,9 @@ namespace Flarum\Api\Event;
use Flarum\Api\Controller\AbstractSerializeController;
use Illuminate\Support\Arr;
/**
* @deprecated in beta 15, removed in beta 16
*/
class WillGetData
{
/**

View File

@@ -13,6 +13,9 @@ use Flarum\Api\Controller\AbstractSerializeController;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
/**
* @deprecated in beta 15, removed in beta 16
*/
class WillSerializeData
{
/**

View File

@@ -0,0 +1,302 @@
<?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\Extend;
use Flarum\Api\Controller\AbstractSerializeController;
use Flarum\Extension\Extension;
use Flarum\Foundation\ContainerUtil;
use Illuminate\Contracts\Container\Container;
class ApiController implements ExtenderInterface
{
private $controllerClass;
private $beforeDataCallbacks = [];
private $beforeSerializationCallbacks = [];
private $serializer;
private $addIncludes = [];
private $removeIncludes = [];
private $addOptionalIncludes = [];
private $removeOptionalIncludes = [];
private $limit;
private $maxLimit;
private $addSortFields = [];
private $removeSortFields = [];
private $sort;
/**
* @param string $controllerClass The ::class attribute of the controller you are modifying.
* This controller should extend from \Flarum\Api\Controller\AbstractSerializeController.
*/
public function __construct(string $controllerClass)
{
$this->controllerClass = $controllerClass;
}
/**
* @param callable|string $callback
*
* The callback can be a closure or an invokable class, and should accept:
* - $controller: An instance of this controller.
*
* @return self
*/
public function prepareDataQuery($callback)
{
$this->beforeDataCallbacks[] = $callback;
return $this;
}
/**
* @param callable|string $callback
*
* The callback can be a closure or an invokable class, and should accept:
* - $controller: An instance of this controller.
* - $data: Mixed, can be an array of data or an object (like an instance of Collection or AbstractModel).
* - $request: An instance of \Psr\Http\Message\ServerRequestInterface.
* - $document: An instance of \Tobscure\JsonApi\Document.
*
* The callable should return:
* - An array of additional data to merge with the existing array.
* Or a modified $data array.
*
* @return self
*/
public function prepareDataForSerialization($callback)
{
$this->beforeSerializationCallbacks[] = $callback;
return $this;
}
/**
* Set the serializer that will serialize data for the endpoint.
*
* @param string $serializerClass
* @param callable|string|null $callback
* @return self
*/
public function setSerializer(string $serializerClass, $callback = null)
{
$this->serializer = [$serializerClass, $callback];
return $this;
}
/**
* Include the given relationship by default.
*
* @param string|array $name
* @param callable|string|null $callback
* @return self
*/
public function addInclude($name, $callback = null)
{
$this->addIncludes[] = [$name, $callback];
return $this;
}
/**
* Don't include the given relationship by default.
*
* @param string|array $name
* @param callable|string|null $callback
* @return self
*/
public function removeInclude($name, $callback = null)
{
$this->removeIncludes[] = [$name, $callback];
return $this;
}
/**
* Make the given relationship available for inclusion.
*
* @param string|array $name
* @param callable|string|null $callback
* @return self
*/
public function addOptionalInclude($name, $callback = null)
{
$this->addOptionalIncludes[] = [$name, $callback];
return $this;
}
/**
* Don't allow the given relationship to be included.
*
* @param string|array $name
* @param callable|string|null $callback
* @return self
*/
public function removeOptionalInclude($name, $callback = null)
{
$this->removeOptionalIncludes[] = [$name, $callback];
return $this;
}
/**
* Set the default number of results.
*
* @param int $limit
* @param callable|string|null $callback
* @return self
*/
public function setLimit(int $limit, $callback = null)
{
$this->limit = [$limit, $callback];
return $this;
}
/**
* Set the maximum number of results.
*
* @param int $max
* @param callable|string|null $callback
* @return self
*/
public function setMaxLimit(int $max, $callback = null)
{
$this->maxLimit = [$max, $callback];
return $this;
}
/**
* Allow sorting results by the given field.
*
* @param string|array $field
* @param callable|string|null $callback
* @return self
*/
public function addSortField($field, $callback = null)
{
$this->addSortFields[] = [$field, $callback];
return $this;
}
/**
* Disallow sorting results by the given field.
*
* @param string|array $field
* @param callable|string|null $callback
* @return self
*/
public function removeSortField($field, $callback = null)
{
$this->removeSortFields[] = [$field, $callback];
return $this;
}
/**
* Set the default sort order for the results.
*
* @param array $sort
* @param callable|string|null $callback
* @return self
*/
public function setSort(array $sort, $callback = null)
{
$this->sort = [$sort, $callback];
return $this;
}
public function extend(Container $container, Extension $extension = null)
{
$this->beforeDataCallbacks[] = function (AbstractSerializeController $controller) use ($container) {
if (isset($this->serializer) && $this->isApplicable($this->serializer[1], $controller, $container)) {
$controller->setSerializer($this->serializer[0]);
}
foreach ($this->addIncludes as $addingInclude) {
if ($this->isApplicable($addingInclude[1], $controller, $container)) {
$controller->addInclude($addingInclude[0]);
}
}
foreach ($this->removeIncludes as $removingInclude) {
if ($this->isApplicable($removingInclude[1], $controller, $container)) {
$controller->removeInclude($removingInclude[0]);
}
}
foreach ($this->addOptionalIncludes as $addingOptionalInclude) {
if ($this->isApplicable($addingOptionalInclude[1], $controller, $container)) {
$controller->addOptionalInclude($addingOptionalInclude[0]);
}
}
foreach ($this->removeOptionalIncludes as $removingOptionalInclude) {
if ($this->isApplicable($removingOptionalInclude[1], $controller, $container)) {
$controller->removeOptionalInclude($removingOptionalInclude[0]);
}
}
foreach ($this->addSortFields as $addingSortField) {
if ($this->isApplicable($addingSortField[1], $controller, $container)) {
$controller->addSortField($addingSortField[0]);
}
}
foreach ($this->removeSortFields as $removingSortField) {
if ($this->isApplicable($removingSortField[1], $controller, $container)) {
$controller->removeSortField($removingSortField[0]);
}
}
if (isset($this->limit) && $this->isApplicable($this->limit[1], $controller, $container)) {
$controller->setLimit($this->limit[0]);
}
if (isset($this->maxLimit) && $this->isApplicable($this->maxLimit[1], $controller, $container)) {
$controller->setMaxLimit($this->maxLimit[0]);
}
if (isset($this->sort) && $this->isApplicable($this->sort[1], $controller, $container)) {
$controller->setSort($this->sort[0]);
}
};
foreach ($this->beforeDataCallbacks as $beforeDataCallback) {
$beforeDataCallback = ContainerUtil::wrapCallback($beforeDataCallback, $container);
AbstractSerializeController::addDataPreparationCallback($this->controllerClass, $beforeDataCallback);
}
foreach ($this->beforeSerializationCallbacks as $beforeSerializationCallback) {
$beforeSerializationCallback = ContainerUtil::wrapCallback($beforeSerializationCallback, $container);
AbstractSerializeController::addSerializationPreparationCallback($this->controllerClass, $beforeSerializationCallback);
}
}
/**
* @param callable|string|null $callback
* @param AbstractSerializeController $controller
* @param Container $container
* @return bool
*/
private function isApplicable($callback, AbstractSerializeController $controller, Container $container)
{
if (! isset($callback)) {
return true;
}
$callback = ContainerUtil::wrapCallback($callback, $container);
return (bool) $callback($controller);
}
}