diff --git a/composer.json b/composer.json
index b8cc1465a56..e6578f2aff0 100644
--- a/composer.json
+++ b/composer.json
@@ -1,8 +1,8 @@
{
"name": "api-platform/core",
"type": "library",
- "description": "Build a fully-featured hypermedia or GraphQL API in minutes",
- "keywords": ["REST", "GraphQL", "API", "JSON-LD", "Hydra", "JSONAPI", "OpenAPI", "HAL", "Swagger"],
+ "description": "The ultimate solution to create web APIs.",
+ "keywords": ["REST", "API", "JSON", "JSON-LD", "Hydra", "Swagger", "HAL"],
"homepage": "https://api-platform.com",
"license": "MIT",
"authors": [
@@ -17,10 +17,10 @@
"doctrine/inflector": "^1.0",
"psr/cache": "^1.0",
"psr/container": "^1.0",
- "symfony/http-foundation": "^3.4 || ^4.0",
- "symfony/http-kernel": "^3.4 || ^4.0",
- "symfony/property-access": "^3.4 || ^4.0",
- "symfony/property-info": "^3.4 || ^4.0",
+ "symfony/http-foundation": "^3.1 || ^4.0",
+ "symfony/http-kernel": "^2.7 || ^3.0 || ^4.0",
+ "symfony/property-access": "^2.7 || ^3.0 || ^4.0",
+ "symfony/property-info": "^3.3.11 || ^4.0",
"symfony/serializer": "^4.1",
"symfony/web-link": "^4.1",
"willdurand/negotiation": "^2.0.3"
@@ -34,54 +34,46 @@
"behatch/contexts": "3.1.0",
"doctrine/annotations": "^1.2",
"doctrine/doctrine-bundle": "^1.8",
- "doctrine/doctrine-cache-bundle": "^1.3.5",
- "doctrine/mongodb-odm": "^2.0.0@beta",
- "doctrine/mongodb-odm-bundle": "^4.0.0@beta",
- "doctrine/orm": "^2.6.3",
- "elasticsearch/elasticsearch": "^6.0",
- "friendsofsymfony/user-bundle": "dev-fix-mongodb-mapping",
+ "doctrine/orm": "^2.5.2",
+ "friendsofsymfony/user-bundle": "^2.1",
"guzzlehttp/guzzle": "^6.0",
"justinrainbow/json-schema": "^5.0",
"nelmio/api-doc-bundle": "^2.13.3",
"php-mock/php-mock-phpunit": "^2.0",
"phpdocumentor/reflection-docblock": "^3.0 || ^4.0",
- "phpdocumentor/type-resolver": "^0.3 || ^0.4",
- "phpspec/prophecy": "^1.8",
- "phpunit/phpunit": "^7.5.2",
+ "phpdocumentor/type-resolver": "^0.2.1 || ^0.3 || 0.4",
+ "phpunit/phpunit": "^6.1",
"psr/log": "^1.0",
"ramsey/uuid": "^3.7",
"ramsey/uuid-doctrine": "^1.4",
- "symfony/asset": "^3.4 || ^4.0",
- "symfony/cache": "^3.4 || ^4.0",
+ "symfony/asset": "^3.3 || ^4.0",
+ "symfony/cache": "^3.3 || ^4.0",
"symfony/config": "^3.4 || ^4.0",
"symfony/console": "^3.4 || ^4.0",
- "symfony/debug": "^3.4 || ^4.0",
+ "symfony/debug": "^2.8 || ^3.0 || ^4.0",
"symfony/dependency-injection": "^3.4 || ^4.0",
- "symfony/doctrine-bridge": "^3.4 || ^4.0",
- "symfony/event-dispatcher": "^3.4 || ^4.0",
- "symfony/expression-language": "^3.4 || ^4.0",
- "symfony/finder": "^3.4 || ^4.0",
- "symfony/form": "^3.4 || ^4.0",
- "symfony/framework-bundle": "^4.2",
+ "symfony/doctrine-bridge": "^2.8.12 || ^3.0 || ^4.0",
+ "symfony/expression-language": "^2.8 || ^3.0 || ^4.0",
+ "symfony/finder": "^3.3 || ^4.0",
+ "symfony/form": "^3.3 || ^4.0",
+ "symfony/framework-bundle": "^3.3 || ^4.0",
+ "symfony/mercure": "*",
"symfony/mercure-bundle": "*",
- "symfony/messenger": "^4.2",
- "symfony/phpunit-bridge": "^3.4.5 || ^4.0.5",
- "symfony/routing": "^3.4 || ^4.0",
- "symfony/security": "^3.4 || ^4.0",
+ "symfony/messenger": "^4.1",
+ "symfony/phpunit-bridge": "^3.3 || ^4.0",
+ "symfony/routing": "^3.3 || ^4.0",
+ "symfony/security": "^3.0 || ^4.0",
"symfony/security-bundle": "^3.4 || ^4.0",
"symfony/twig-bundle": "^3.4 || ^4.0",
- "symfony/validator": "^3.4 || ^4.0",
- "symfony/web-profiler-bundle": "^3.4 || ^4.0",
- "symfony/yaml": "^3.4 || ^4.0",
- "webonyx/graphql-php": ">=0.13 <1.0"
+ "symfony/validator": "^3.3 || ^4.0",
+ "symfony/web-profiler-bundle": "^3.3 || ^4.0",
+ "symfony/yaml": "^3.3 || ^4.0",
+ "webonyx/graphql-php": ">=0.12 <1.0"
},
"conflict": {
- "doctrine/common": "<2.7",
- "doctrine/mongodb-odm": "<2.0"
+ "symfony/dependency-injection": "<3.4"
},
"suggest": {
- "doctrine/mongodb-odm-bundle": "To support MongoDB. Only versions 4.0 and later are supported.",
- "elasticsearch/elasticsearch": "To support Elasticsearch.",
"friendsofsymfony/user-bundle": "To use the FOSUserBundle bridge.",
"guzzlehttp/guzzle": "To use the HTTP cache invalidation system.",
"phpdocumentor/reflection-docblock": "To support extracting metadata from PHPDoc.",
@@ -89,6 +81,7 @@
"ramsey/uuid": "To support Ramsey's UUID identifiers.",
"symfony/cache": "To have metadata caching when using Symfony integration.",
"symfony/config": "To load XML configuration files.",
+ "symfony/event-dispatcher": "To handle events",
"symfony/expression-language": "To use authorization features.",
"symfony/security": "To use authorization features.",
"symfony/twig-bundle": "To use the Swagger UI integration.",
@@ -103,17 +96,10 @@
},
"extra": {
"branch-alias": {
- "dev-master": "2.5.x-dev"
+ "dev-master": "2.4.x-dev"
}
},
"config": {
"sort-packages": true
- },
- "repositories": [
- {
- "type": "vcs",
- "url": "https://github.com/alanpoulain/FOSUserBundle",
- "no-api": true
- }
- ]
+ }
}
diff --git a/src/Bridge/Symfony/Bundle/Resources/config/api.xml b/src/Bridge/Symfony/Bundle/Resources/config/api.xml
index 7a23930994f..ebc4221cb66 100644
--- a/src/Bridge/Symfony/Bundle/Resources/config/api.xml
+++ b/src/Bridge/Symfony/Bundle/Resources/config/api.xml
@@ -139,6 +139,7 @@
+
@@ -149,6 +150,7 @@
+
@@ -156,6 +158,7 @@
+
@@ -164,6 +167,7 @@
+
@@ -171,12 +175,15 @@
+
+
+
diff --git a/src/Bridge/Symfony/Bundle/Resources/config/graphql.xml b/src/Bridge/Symfony/Bundle/Resources/config/graphql.xml
index bdc491d1b38..d9dd6e70b61 100644
--- a/src/Bridge/Symfony/Bundle/Resources/config/graphql.xml
+++ b/src/Bridge/Symfony/Bundle/Resources/config/graphql.xml
@@ -18,6 +18,7 @@
%api_platform.collection.pagination.enabled%
+
@@ -27,6 +28,7 @@
+
@@ -34,6 +36,7 @@
+
diff --git a/src/Event/PostDeserializeEvent.php b/src/Event/PostDeserializeEvent.php
new file mode 100644
index 00000000000..9a85bbdb635
--- /dev/null
+++ b/src/Event/PostDeserializeEvent.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Event;
+
+use Symfony\Component\EventDispatcher\Event;
+
+final class PostDeserializeEvent extends Event
+{
+ private $data;
+
+ public function __construct($controllerResult)
+ {
+ $this->data = $controllerResult;
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ public function setData($data): void
+ {
+ $this->data = $data;
+ }
+}
diff --git a/src/Event/PostReadEvent.php b/src/Event/PostReadEvent.php
new file mode 100644
index 00000000000..cf987d308f6
--- /dev/null
+++ b/src/Event/PostReadEvent.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Event;
+
+use Symfony\Component\EventDispatcher\Event;
+
+final class PostReadEvent extends Event
+{
+ private $data;
+
+ public function __construct($data)
+ {
+ $this->data = $data;
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ public function setData($data): void
+ {
+ $this->data = $data;
+ }
+}
diff --git a/src/Event/PostSerializeEvent.php b/src/Event/PostSerializeEvent.php
new file mode 100644
index 00000000000..354a68557b4
--- /dev/null
+++ b/src/Event/PostSerializeEvent.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Event;
+
+use Symfony\Component\EventDispatcher\Event;
+
+final class PostSerializeEvent extends Event
+{
+ private $data;
+
+ public function __construct($object)
+ {
+ $this->data = $object;
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ public function setData($data): void
+ {
+ $this->data = $data;
+ }
+}
diff --git a/src/Event/PostValidateEvent.php b/src/Event/PostValidateEvent.php
new file mode 100644
index 00000000000..7a9a274d375
--- /dev/null
+++ b/src/Event/PostValidateEvent.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Event;
+
+use Symfony\Component\EventDispatcher\Event;
+
+class PostValidateEvent extends Event
+{
+ private $data;
+
+ public function __construct($data)
+ {
+ $this->data = $data;
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ public function setData($data): void
+ {
+ $this->data = $data;
+ }
+}
diff --git a/src/Event/PostWriteEvent.php b/src/Event/PostWriteEvent.php
new file mode 100644
index 00000000000..914fafb5f9c
--- /dev/null
+++ b/src/Event/PostWriteEvent.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Event;
+
+use Symfony\Component\EventDispatcher\Event;
+
+final class PostWriteEvent extends Event
+{
+ private $data;
+
+ public function __construct($data)
+ {
+ $this->data = $data;
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ public function setData($data): void
+ {
+ $this->data = $data;
+ }
+}
diff --git a/src/Event/PreDeserializeEvent.php b/src/Event/PreDeserializeEvent.php
new file mode 100644
index 00000000000..a1df5494264
--- /dev/null
+++ b/src/Event/PreDeserializeEvent.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Event;
+
+use Symfony\Component\EventDispatcher\Event;
+
+final class PreDeserializeEvent extends Event
+{
+ private $data;
+
+ public function __construct($data)
+ {
+ $this->data = $data;
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ public function setData($data): void
+ {
+ $this->data = $data;
+ }
+}
diff --git a/src/Event/PreReadEvent.php b/src/Event/PreReadEvent.php
new file mode 100644
index 00000000000..2c61685e97a
--- /dev/null
+++ b/src/Event/PreReadEvent.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Event;
+
+use Symfony\Component\EventDispatcher\Event;
+
+final class PreReadEvent extends Event
+{
+ private $data;
+
+ public function __construct($data)
+ {
+ $this->data = $data;
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ public function setData($data): void
+ {
+ $this->data = $data;
+ }
+}
diff --git a/src/Event/PreSerializeEvent.php b/src/Event/PreSerializeEvent.php
new file mode 100644
index 00000000000..f2fcc91e3b9
--- /dev/null
+++ b/src/Event/PreSerializeEvent.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Event;
+
+use Symfony\Component\EventDispatcher\Event;
+
+final class PreSerializeEvent extends Event
+{
+ private $data;
+
+ public function __construct($object)
+ {
+ $this->data = $object;
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ public function setData($data): void
+ {
+ $this->data = $data;
+ }
+}
diff --git a/src/Event/PreValidateEvent.php b/src/Event/PreValidateEvent.php
new file mode 100644
index 00000000000..91a4be15baf
--- /dev/null
+++ b/src/Event/PreValidateEvent.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Event;
+
+use Symfony\Component\EventDispatcher\Event;
+
+class PreValidateEvent extends Event
+{
+ private $data;
+
+ public function __construct($data)
+ {
+ $this->data = $data;
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ public function setData($data): void
+ {
+ $this->data = $data;
+ }
+}
diff --git a/src/Event/PreWriteEvent.php b/src/Event/PreWriteEvent.php
new file mode 100644
index 00000000000..ad68047a0ae
--- /dev/null
+++ b/src/Event/PreWriteEvent.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Event;
+
+use Symfony\Component\EventDispatcher\Event;
+
+final class PreWriteEvent extends Event
+{
+ private $data;
+
+ public function __construct($data)
+ {
+ $this->data = $data;
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ public function setData($data): void
+ {
+ $this->data = $data;
+ }
+}
diff --git a/src/EventListener/AddFormatListener.php b/src/EventListener/AddFormatListener.php
index 72e53713dda..69b512258f7 100644
--- a/src/EventListener/AddFormatListener.php
+++ b/src/EventListener/AddFormatListener.php
@@ -13,7 +13,6 @@
namespace ApiPlatform\Core\EventListener;
-use ApiPlatform\Core\Api\FormatMatcher;
use ApiPlatform\Core\Api\FormatsProviderInterface;
use ApiPlatform\Core\Exception\InvalidArgumentException;
use ApiPlatform\Core\Util\RequestAttributesExtractor;
@@ -24,7 +23,7 @@
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
- * Chooses the format to use according to the Accept header and supported formats.
+ * Chooses the format to user according to the Accept header and supported formats.
*
* @author Kévin Dunglas
*/
@@ -34,7 +33,6 @@ final class AddFormatListener
private $formats = [];
private $mimeTypes;
private $formatsProvider;
- private $formatMatcher;
/**
* @throws InvalidArgumentException
@@ -45,18 +43,20 @@ public function __construct(Negotiator $negotiator, /* FormatsProviderInterface
if (\is_array($formatsProvider)) {
@trigger_error('Using an array as formats provider is deprecated since API Platform 2.3 and will not be possible anymore in API Platform 3', E_USER_DEPRECATED);
$this->formats = $formatsProvider;
- } else {
- if (!$formatsProvider instanceof FormatsProviderInterface) {
- throw new InvalidArgumentException(sprintf('The "$formatsProvider" argument is expected to be an implementation of the "%s" interface.', FormatsProviderInterface::class));
- }
- $this->formatsProvider = $formatsProvider;
+ return;
+ }
+ if (!$formatsProvider instanceof FormatsProviderInterface) {
+ throw new InvalidArgumentException(sprintf('The "$formatsProvider" argument is expected to be an implementation of the "%s" interface.', FormatsProviderInterface::class));
}
+
+ $this->formatsProvider = $formatsProvider;
}
/**
* Sets the applicable format to the HttpFoundation Request.
*
+ *
* @throws NotFoundHttpException
* @throws NotAcceptableHttpException
*/
@@ -70,9 +70,9 @@ public function onKernelRequest(GetResponseEvent $event)
if (null !== $this->formatsProvider) {
$this->formats = $this->formatsProvider->getFormatsFromAttributes(RequestAttributesExtractor::extractAttributes($request));
}
- $this->formatMatcher = new FormatMatcher($this->formats);
$this->populateMimeTypes();
+
$this->addRequestFormats($request, $this->formats);
// Empty strings must be converted to null because the Symfony router doesn't support parameter typing before 3.2 (_format)
@@ -88,11 +88,11 @@ public function onKernelRequest(GetResponseEvent $event)
/** @var string|null $accept */
$accept = $request->headers->get('Accept');
if (null !== $accept) {
- if (null === $mediaType = $this->negotiator->getBest($accept, $mimeTypes)) {
+ if (null === $acceptHeader = $this->negotiator->getBest($accept, $mimeTypes)) {
throw $this->getNotAcceptableHttpException($accept, $mimeTypes);
}
- $request->setRequestFormat($this->formatMatcher->getFormat($mediaType->getType()));
+ $request->setRequestFormat($request->getFormat($acceptHeader->getType()));
return;
}
@@ -118,14 +118,12 @@ public function onKernelRequest(GetResponseEvent $event)
}
/**
- * Adds the supported formats to the request.
- *
- * This is necessary for {@see Request::getMimeType} and {@see Request::getMimeTypes} to work.
+ * Adds API formats to the HttpFoundation Request.
*/
private function addRequestFormats(Request $request, array $formats)
{
foreach ($formats as $format => $mimeTypes) {
- $request->setFormat($format, (array) $mimeTypes);
+ $request->setFormat($format, $mimeTypes);
}
}
diff --git a/src/EventListener/DeserializeListener.php b/src/EventListener/DeserializeListener.php
index 1fff5848178..d890b03db7d 100644
--- a/src/EventListener/DeserializeListener.php
+++ b/src/EventListener/DeserializeListener.php
@@ -13,11 +13,14 @@
namespace ApiPlatform\Core\EventListener;
-use ApiPlatform\Core\Api\FormatMatcher;
use ApiPlatform\Core\Api\FormatsProviderInterface;
+use ApiPlatform\Core\Event\PostDeserializeEvent;
+use ApiPlatform\Core\Event\PreDeserializeEvent;
+use ApiPlatform\Core\Events;
use ApiPlatform\Core\Exception\InvalidArgumentException;
use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
use ApiPlatform\Core\Util\RequestAttributesExtractor;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
@@ -35,25 +38,27 @@ final class DeserializeListener
private $serializerContextBuilder;
private $formats = [];
private $formatsProvider;
- private $formatMatcher;
+ private $dispatcher;
/**
* @throws InvalidArgumentException
*/
- public function __construct(SerializerInterface $serializer, SerializerContextBuilderInterface $serializerContextBuilder, /* FormatsProviderInterface */ $formatsProvider)
+ public function __construct(SerializerInterface $serializer, SerializerContextBuilderInterface $serializerContextBuilder, /* FormatsProviderInterface */ $formatsProvider, EventDispatcherInterface $dispatcher = null)
{
$this->serializer = $serializer;
$this->serializerContextBuilder = $serializerContextBuilder;
if (\is_array($formatsProvider)) {
@trigger_error('Using an array as formats provider is deprecated since API Platform 2.3 and will not be possible anymore in API Platform 3', E_USER_DEPRECATED);
$this->formats = $formatsProvider;
- } else {
- if (!$formatsProvider instanceof FormatsProviderInterface) {
- throw new InvalidArgumentException(sprintf('The "$formatsProvider" argument is expected to be an implementation of the "%s" interface.', FormatsProviderInterface::class));
- }
- $this->formatsProvider = $formatsProvider;
+ return;
+ }
+ if (!$formatsProvider instanceof FormatsProviderInterface) {
+ throw new InvalidArgumentException(sprintf('The "$formatsProvider" argument is expected to be an implementation of the "%s" interface.', FormatsProviderInterface::class));
}
+
+ $this->formatsProvider = $formatsProvider;
+ $this->dispatcher = $dispatcher;
}
/**
@@ -64,10 +69,9 @@ public function onKernelRequest(GetResponseEvent $event)
$request = $event->getRequest();
$method = $request->getMethod();
if (
- 'DELETE' === $method
- || $request->isMethodSafe(false)
+ $request->isMethodSafe(false)
+ || 'DELETE' === $method
|| !($attributes = RequestAttributesExtractor::extractAttributes($request))
- || false === ($attributes['input_class'] ?? null)
|| !$attributes['receive']
|| (
'' === ($requestContent = $request->getContent())
@@ -80,7 +84,6 @@ public function onKernelRequest(GetResponseEvent $event)
if (null !== $this->formatsProvider) {
$this->formats = $this->formatsProvider->getFormatsFromAttributes($attributes);
}
- $this->formatMatcher = new FormatMatcher($this->formats);
$format = $this->getFormat($request);
$context = $this->serializerContextBuilder->createFromRequest($request, false, $attributes);
@@ -93,17 +96,26 @@ public function onKernelRequest(GetResponseEvent $event)
$context[AbstractNormalizer::OBJECT_TO_POPULATE] = $data;
}
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(Events::PRE_DESERIALIZE, new PreDeserializeEvent($requestContent));
+ }
+
$request->attributes->set(
'data',
$this->serializer->deserialize(
$requestContent, $attributes['input_class'], $format, $context
)
);
+
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(Events::POST_DESERIALIZE, new PostDeserializeEvent($requestContent));
+ }
}
/**
* Extracts the format from the Content-Type header and check that it is supported.
*
+ *
* @throws NotAcceptableHttpException
*/
private function getFormat(Request $request): string
@@ -116,7 +128,7 @@ private function getFormat(Request $request): string
throw new NotAcceptableHttpException('The "Content-Type" header must exist.');
}
- $format = $this->formatMatcher->getFormat($contentType);
+ $format = $request->getFormat($contentType);
if (null === $format || !isset($this->formats[$format])) {
$supportedMimeTypes = [];
foreach ($this->formats as $mimeTypes) {
diff --git a/src/EventListener/ReadListener.php b/src/EventListener/ReadListener.php
index 80fb62d439f..c685cb616e5 100644
--- a/src/EventListener/ReadListener.php
+++ b/src/EventListener/ReadListener.php
@@ -17,13 +17,16 @@
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
use ApiPlatform\Core\DataProvider\OperationDataProviderTrait;
use ApiPlatform\Core\DataProvider\SubresourceDataProviderInterface;
+use ApiPlatform\Core\Event\PostReadEvent;
+use ApiPlatform\Core\Event\PreReadEvent;
+use ApiPlatform\Core\Events;
use ApiPlatform\Core\Exception\InvalidIdentifierException;
use ApiPlatform\Core\Exception\RuntimeException;
use ApiPlatform\Core\Identifier\IdentifierConverterInterface;
use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
use ApiPlatform\Core\Util\RequestAttributesExtractor;
use ApiPlatform\Core\Util\RequestParser;
-use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -37,14 +40,17 @@ final class ReadListener
use OperationDataProviderTrait;
private $serializerContextBuilder;
+ /** @var EventDispatcherInterface */
+ private $dispatcher;
- public function __construct(CollectionDataProviderInterface $collectionDataProvider, ItemDataProviderInterface $itemDataProvider, SubresourceDataProviderInterface $subresourceDataProvider = null, SerializerContextBuilderInterface $serializerContextBuilder = null, IdentifierConverterInterface $identifierConverter = null)
+ public function __construct(CollectionDataProviderInterface $collectionDataProvider, ItemDataProviderInterface $itemDataProvider, SubresourceDataProviderInterface $subresourceDataProvider = null, SerializerContextBuilderInterface $serializerContextBuilder = null, IdentifierConverterInterface $identifierConverter = null, EventDispatcherInterface $dispatcher = null)
{
$this->collectionDataProvider = $collectionDataProvider;
$this->itemDataProvider = $itemDataProvider;
$this->subresourceDataProvider = $subresourceDataProvider;
$this->serializerContextBuilder = $serializerContextBuilder;
$this->identifierConverter = $identifierConverter;
+ $this->dispatcher = $dispatcher;
}
/**
@@ -89,6 +95,10 @@ public function onKernelRequest(GetResponseEvent $event)
try {
$identifiers = $this->extractIdentifiers($request->attributes->all(), $attributes);
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(Events::PRE_READ, new PreReadEvent($data));
+ }
+
if (isset($attributes['item_operation_name'])) {
$data = $this->getItemData($identifiers, $attributes, $context);
} elseif (isset($attributes['subresource_operation_name'])) {
@@ -103,6 +113,10 @@ public function onKernelRequest(GetResponseEvent $event)
throw new NotFoundHttpException('Not found, because of an invalid identifier configuration', $e);
}
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(Events::POST_READ, new PostReadEvent($data));
+ }
+
if (null === $data) {
throw new NotFoundHttpException('Not Found');
}
diff --git a/src/EventListener/SerializeListener.php b/src/EventListener/SerializeListener.php
index 803c25296cc..28be933f852 100644
--- a/src/EventListener/SerializeListener.php
+++ b/src/EventListener/SerializeListener.php
@@ -13,12 +13,14 @@
namespace ApiPlatform\Core\EventListener;
+use ApiPlatform\Core\Event\PostSerializeEvent;
+use ApiPlatform\Core\Event\PreSerializeEvent;
+use ApiPlatform\Core\Events;
use ApiPlatform\Core\Exception\RuntimeException;
use ApiPlatform\Core\Serializer\ResourceList;
use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
use ApiPlatform\Core\Util\RequestAttributesExtractor;
-use Fig\Link\GenericLinkProvider;
-use Fig\Link\Link;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
@@ -35,10 +37,14 @@ final class SerializeListener
private $serializer;
private $serializerContextBuilder;
- public function __construct(SerializerInterface $serializer, SerializerContextBuilderInterface $serializerContextBuilder)
+ /** @var EventDispatcherInterface */
+ private $dispatcher;
+
+ public function __construct(SerializerInterface $serializer, SerializerContextBuilderInterface $serializerContextBuilder, EventDispatcherInterface $dispatcher = null)
{
$this->serializer = $serializer;
$this->serializerContextBuilder = $serializerContextBuilder;
+ $this->dispatcher = $dispatcher;
}
/**
@@ -49,7 +55,7 @@ public function onKernelView(GetResponseForControllerResultEvent $event)
$controllerResult = $event->getControllerResult();
$request = $event->getRequest();
- if ($controllerResult instanceof Response || !$request->attributes->getBoolean('_api_respond', true)) {
+ if ($controllerResult instanceof Response) {
return;
}
@@ -59,43 +65,32 @@ public function onKernelView(GetResponseForControllerResultEvent $event)
return;
}
- $request->attributes->set('_api_respond', true);
$context = $this->serializerContextBuilder->createFromRequest($request, true, $attributes);
-
- if (isset($context['output_class'])) {
- if (false === $context['output_class']) {
- // If the output class is explicitly set to false, the response must be empty
- $event->setControllerResult('');
-
- return;
- }
-
- $context['resource_class'] = $context['output_class'];
- }
-
if ($included = $request->attributes->get('_api_included')) {
$context['api_included'] = $included;
}
$resources = new ResourceList();
$context['resources'] = &$resources;
-
- $resourcesToPush = new ResourceList();
- $context['resources_to_push'] = &$resourcesToPush;
+ if (isset($context['output_class'])) {
+ $context['resource_class'] = $context['output_class'];
+ }
$request->attributes->set('_api_normalization_context', $context);
- $event->setControllerResult($this->serializer->serialize($controllerResult, $request->getRequestFormat(), $context));
-
- $request->attributes->set('_resources', $request->attributes->get('_resources', []) + (array) $resources);
- if (!\count($resourcesToPush)) {
- return;
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(Events::PRE_SERIALIZE, new PreSerializeEvent($controllerResult));
}
- $linkProvider = $request->attributes->get('_links', new GenericLinkProvider());
- foreach ($resourcesToPush as $resourceToPush) {
- $linkProvider = $linkProvider->withLink(new Link('preload', $resourceToPush));
+ $serializedResult = $this->serializer->serialize($controllerResult, $request->getRequestFormat(), $context);
+
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(Events::POST_SERIALIZE, new PostSerializeEvent($serializedResult));
}
- $request->attributes->set('_links', $linkProvider);
+
+ $event->setControllerResult($serializedResult);
+
+ $request->attributes->set('_api_respond', true);
+ $request->attributes->set('_resources', $request->attributes->get('_resources', []) + (array) $resources);
}
/**
@@ -107,6 +102,10 @@ public function onKernelView(GetResponseForControllerResultEvent $event)
*/
private function serializeRawData(GetResponseForControllerResultEvent $event, Request $request, $controllerResult)
{
+ if (!$request->attributes->get('_api_respond')) {
+ return;
+ }
+
if (\is_object($controllerResult)) {
$event->setControllerResult($this->serializer->serialize($controllerResult, $request->getRequestFormat(), $request->attributes->get('_api_normalization_context', [])));
diff --git a/src/EventListener/WriteListener.php b/src/EventListener/WriteListener.php
index 7b222971e0a..8e536036d0d 100644
--- a/src/EventListener/WriteListener.php
+++ b/src/EventListener/WriteListener.php
@@ -15,11 +15,15 @@
use ApiPlatform\Core\Api\IriConverterInterface;
use ApiPlatform\Core\DataPersister\DataPersisterInterface;
+use ApiPlatform\Core\Event\PostWriteEvent;
+use ApiPlatform\Core\Event\PreWriteEvent;
+use ApiPlatform\Core\Events;
use ApiPlatform\Core\Util\RequestAttributesExtractor;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
/**
- * Bridges persistence and the API system.
+ * Bridges persistense and the API system.
*
* @author Kévin Dunglas
* @author Baptiste Meyer
@@ -28,11 +32,13 @@ final class WriteListener
{
private $dataPersister;
private $iriConverter;
+ private $dispatcher;
- public function __construct(DataPersisterInterface $dataPersister, IriConverterInterface $iriConverter = null)
+ public function __construct(DataPersisterInterface $dataPersister, IriConverterInterface $iriConverter = null, EventDispatcherInterface $dispatcher = null)
{
$this->dataPersister = $dataPersister;
$this->iriConverter = $iriConverter;
+ $this->dispatcher = $dispatcher;
}
/**
@@ -50,12 +56,20 @@ public function onKernelView(GetResponseForControllerResultEvent $event)
return;
}
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(Events::PRE_WRITE, new PreWriteEvent($controllerResult));
+ }
+
switch ($request->getMethod()) {
case 'PUT':
case 'PATCH':
case 'POST':
$persistResult = $this->dataPersister->persist($controllerResult);
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(Events::POST_WRITE, new PostWriteEvent($controllerResult));
+ }
+
if (null === $persistResult) {
@trigger_error(sprintf('Returning void from %s::persist() is deprecated since API Platform 2.3 and will not be supported in API Platform 3, an object should always be returned.', DataPersisterInterface::class), E_USER_DEPRECATED);
}
@@ -65,12 +79,17 @@ public function onKernelView(GetResponseForControllerResultEvent $event)
// Controller result must be immutable for _api_write_item_iri
// if it's class changed compared to the base class let's avoid calling the IriConverter
// especially that the Output class could be a DTO that's not referencing any route
- if (null !== $this->iriConverter && (false !== $attributes['output_class'] ?? null) && $attributes['resource_class'] === ($class = \get_class($controllerResult)) && $class === \get_class($event->getControllerResult())) {
+ if (null !== $this->iriConverter && (false !== $attributes['output_class'] ?? null) && \get_class($controllerResult) === \get_class($event->getControllerResult())) {
$request->attributes->set('_api_write_item_iri', $this->iriConverter->getIriFromItem($controllerResult));
}
break;
case 'DELETE':
$this->dataPersister->remove($controllerResult);
+
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(PostWriteEvent::NAME, new PostWriteEvent($controllerResult));
+ }
+
$event->setControllerResult(null);
break;
}
diff --git a/src/Events.php b/src/Events.php
new file mode 100644
index 00000000000..ff90019e914
--- /dev/null
+++ b/src/Events.php
@@ -0,0 +1,32 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core;
+
+final class Events
+{
+ const PRE_READ = 'api_platform.pre_read';
+ const POST_READ = 'api_platform.post_read';
+
+ const PRE_DESERIALIZE = 'api_platform.pre_deserialize';
+ const POST_DESERIALIZE = 'api_platform.post_deserialize';
+
+ const PRE_VALIDATE = 'api_platform.pre_validate';
+ const POST_VALIDATE = 'api_platform.post_validate';
+
+ const PRE_WRITE = 'api_platform.pre_write';
+ const POST_WRITE = 'api_platform.post_write';
+
+ const PRE_SERIALIZE = 'api_platform.pre_serialize';
+ const POST_SERIALIZE = 'api_platform.post_serialize';
+}
diff --git a/src/GraphQl/Resolver/Factory/CollectionResolverFactory.php b/src/GraphQl/Resolver/Factory/CollectionResolverFactory.php
index d1a6bcc7ea1..b9646c25eed 100644
--- a/src/GraphQl/Resolver/Factory/CollectionResolverFactory.php
+++ b/src/GraphQl/Resolver/Factory/CollectionResolverFactory.php
@@ -17,6 +17,10 @@
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\PaginatorInterface;
use ApiPlatform\Core\DataProvider\SubresourceDataProviderInterface;
+use ApiPlatform\Core\Event\PostReadEvent;
+use ApiPlatform\Core\Event\PostSerializeEvent;
+use ApiPlatform\Core\Event\PreReadEvent;
+use ApiPlatform\Core\Event\PreSerializeEvent;
use ApiPlatform\Core\Exception\ResourceClassNotSupportedException;
use ApiPlatform\Core\GraphQl\Resolver\FieldsToAttributesTrait;
use ApiPlatform\Core\GraphQl\Resolver\ResourceAccessCheckerTrait;
@@ -25,6 +29,7 @@
use ApiPlatform\Core\Security\ResourceAccessCheckerInterface;
use GraphQL\Error\Error;
use GraphQL\Type\Definition\ResolveInfo;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
@@ -49,8 +54,9 @@ final class CollectionResolverFactory implements ResolverFactoryInterface
private $requestStack;
private $paginationEnabled;
private $resourceMetadataFactory;
+ private $dispatcher;
- public function __construct(CollectionDataProviderInterface $collectionDataProvider, SubresourceDataProviderInterface $subresourceDataProvider, NormalizerInterface $normalizer, IdentifiersExtractorInterface $identifiersExtractor, ResourceMetadataFactoryInterface $resourceMetadataFactory, ResourceAccessCheckerInterface $resourceAccessChecker = null, RequestStack $requestStack = null, bool $paginationEnabled = false)
+ public function __construct(CollectionDataProviderInterface $collectionDataProvider, SubresourceDataProviderInterface $subresourceDataProvider, NormalizerInterface $normalizer, IdentifiersExtractorInterface $identifiersExtractor, ResourceMetadataFactoryInterface $resourceMetadataFactory, ResourceAccessCheckerInterface $resourceAccessChecker = null, RequestStack $requestStack = null, bool $paginationEnabled = false, EventDispatcherInterface $dispatcher = null)
{
$this->subresourceDataProvider = $subresourceDataProvider;
$this->collectionDataProvider = $collectionDataProvider;
@@ -60,6 +66,7 @@ public function __construct(CollectionDataProviderInterface $collectionDataProvi
$this->requestStack = $requestStack;
$this->paginationEnabled = $paginationEnabled;
$this->resourceMetadataFactory = $resourceMetadataFactory;
+ $this->dispatcher = $dispatcher;
}
public function __invoke(string $resourceClass = null, string $rootClass = null, string $operationName = null): callable
@@ -84,20 +91,42 @@ public function __invoke(string $resourceClass = null, string $rootClass = null,
if (isset($rootClass, $source[$rootProperty = $info->fieldName], $source[ItemNormalizer::ITEM_KEY])) {
$rootResolvedFields = $this->identifiersExtractor->getIdentifiersFromItem(unserialize($source[ItemNormalizer::ITEM_KEY]));
+
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(PreReadEvent::NAME, new PreReadEvent($rootResolvedFields));
+ }
+
$subresource = $this->getSubresource($rootClass, $rootResolvedFields, array_keys($rootResolvedFields), $rootProperty, $resourceClass, true, $dataProviderContext);
$collection = $subresource ?? [];
} else {
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(PreReadEvent::NAME, new PreReadEvent([]));
+ }
+
$collection = $this->collectionDataProvider->getCollection($resourceClass, null, $dataProviderContext);
}
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(PostReadEvent::NAME, new PostReadEvent($collection));
+ }
+
$this->canAccess($this->resourceAccessChecker, $resourceMetadata, $resourceClass, $info, $collection, $operationName ?? 'query');
if (!$this->paginationEnabled) {
$data = [];
+
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(PreSerializeEvent::NAME, new PreSerializeEvent($collection));
+ }
+
foreach ($collection as $index => $object) {
$data[$index] = $this->normalizer->normalize($object, ItemNormalizer::FORMAT, $dataProviderContext);
}
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(PostSerializeEvent::NAME, new PostSerializeEvent($data));
+ }
+
return $data;
}
@@ -117,6 +146,10 @@ public function __invoke(string $resourceClass = null, string $rootClass = null,
$data['totalCount'] = $totalItems;
}
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(PreSerializeEvent::NAME, new PreSerializeEvent($collection));
+ }
+
foreach ($collection as $index => $object) {
$data['edges'][$index] = [
'node' => $this->normalizer->normalize($object, ItemNormalizer::FORMAT, $dataProviderContext),
@@ -124,6 +157,10 @@ public function __invoke(string $resourceClass = null, string $rootClass = null,
];
}
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(PostSerializeEvent::NAME, new PostSerializeEvent($data['edges']));
+ }
+
return $data;
};
}
diff --git a/src/GraphQl/Resolver/Factory/ItemMutationResolverFactory.php b/src/GraphQl/Resolver/Factory/ItemMutationResolverFactory.php
index 46f77aa08b5..8c6b68a8fa3 100644
--- a/src/GraphQl/Resolver/Factory/ItemMutationResolverFactory.php
+++ b/src/GraphQl/Resolver/Factory/ItemMutationResolverFactory.php
@@ -15,6 +15,9 @@
use ApiPlatform\Core\Api\IriConverterInterface;
use ApiPlatform\Core\DataPersister\DataPersisterInterface;
+use ApiPlatform\Core\Event\PostWriteEvent;
+use ApiPlatform\Core\Event\PreValidateEvent;
+use ApiPlatform\Core\Event\PreWriteEvent;
use ApiPlatform\Core\Exception\InvalidArgumentException;
use ApiPlatform\Core\Exception\ItemNotFoundException;
use ApiPlatform\Core\GraphQl\Resolver\FieldsToAttributesTrait;
@@ -28,6 +31,7 @@
use ApiPlatform\Core\Validator\ValidatorInterface;
use GraphQL\Error\Error;
use GraphQL\Type\Definition\ResolveInfo;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
@@ -50,8 +54,9 @@ final class ItemMutationResolverFactory implements ResolverFactoryInterface
private $resourceMetadataFactory;
private $resourceAccessChecker;
private $validator;
+ private $dispatcher;
- public function __construct(IriConverterInterface $iriConverter, DataPersisterInterface $dataPersister, NormalizerInterface $normalizer, ResourceMetadataFactoryInterface $resourceMetadataFactory, ResourceAccessCheckerInterface $resourceAccessChecker = null, ValidatorInterface $validator = null)
+ public function __construct(IriConverterInterface $iriConverter, DataPersisterInterface $dataPersister, NormalizerInterface $normalizer, ResourceMetadataFactoryInterface $resourceMetadataFactory, ResourceAccessCheckerInterface $resourceAccessChecker = null, ValidatorInterface $validator = null, EventDispatcherInterface $dispatcher = null)
{
if (!$normalizer instanceof DenormalizerInterface) {
throw new InvalidArgumentException(sprintf('The normalizer must implements the "%s" interface', DenormalizerInterface::class));
@@ -63,6 +68,7 @@ public function __construct(IriConverterInterface $iriConverter, DataPersisterIn
$this->resourceMetadataFactory = $resourceMetadataFactory;
$this->resourceAccessChecker = $resourceAccessChecker;
$this->validator = $validator;
+ $this->dispatcher = $dispatcher;
}
public function __invoke(string $resourceClass = null, string $rootClass = null, string $operationName = null): callable
@@ -103,21 +109,39 @@ public function __invoke(string $resourceClass = null, string $rootClass = null,
$context = null === $item ? ['resource_class' => $resourceClass] : ['resource_class' => $resourceClass, 'object_to_populate' => $item];
$context += $resourceMetadata->getGraphqlAttribute($operationName, 'denormalization_context', [], true);
$item = $this->normalizer->denormalize($args['input'], $resourceClass, ItemNormalizer::FORMAT, $context);
+ $this->dispatcher->dispatch(PreValidateEvent::class, new PreValidateEvent($item));
$this->validate($item, $info, $resourceMetadata, $operationName);
+
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(PreWriteEvent::NAME, new PreWriteEvent($item));
+ }
+
$persistResult = $this->dataPersister->persist($item);
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(PostWriteEvent::NAME, new PostWriteEvent($item));
+ }
+
if (null === $persistResult) {
@trigger_error(sprintf('Returning void from %s::persist() is deprecated since API Platform 2.3 and will not be supported in API Platform 3, an object should always be returned.', DataPersisterInterface::class), E_USER_DEPRECATED);
}
return $this->normalizer->normalize($persistResult ?? $item, ItemNormalizer::FORMAT, $normalizationContext) + $data;
case 'delete':
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(PreWriteEvent::NAME, new PreWriteEvent($item));
+ }
+
if ($item) {
$this->dataPersister->remove($item);
$data['id'] = $args['input']['id'];
} else {
$data['id'] = null;
}
+
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(PostWriteEvent::NAME, new PostWriteEvent($item));
+ }
}
return $data;
diff --git a/src/GraphQl/Resolver/ItemResolver.php b/src/GraphQl/Resolver/ItemResolver.php
index a06dbb05192..1cabf8d44ed 100644
--- a/src/GraphQl/Resolver/ItemResolver.php
+++ b/src/GraphQl/Resolver/ItemResolver.php
@@ -14,12 +14,17 @@
namespace ApiPlatform\Core\GraphQl\Resolver;
use ApiPlatform\Core\Api\IriConverterInterface;
+use ApiPlatform\Core\Event\PostReadEvent;
+use ApiPlatform\Core\Event\PostSerializeEvent;
+use ApiPlatform\Core\Event\PreReadEvent;
+use ApiPlatform\Core\Event\PreSerializeEvent;
use ApiPlatform\Core\Exception\ItemNotFoundException;
use ApiPlatform\Core\GraphQl\Serializer\ItemNormalizer;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use ApiPlatform\Core\Security\ResourceAccessCheckerInterface;
use ApiPlatform\Core\Util\ClassInfoTrait;
use GraphQL\Type\Definition\ResolveInfo;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
/**
@@ -40,13 +45,15 @@ final class ItemResolver
private $resourceAccessChecker;
private $normalizer;
private $resourceMetadataFactory;
+ private $dispatcher;
- public function __construct(IriConverterInterface $iriConverter, NormalizerInterface $normalizer, ResourceMetadataFactoryInterface $resourceMetadataFactory, ResourceAccessCheckerInterface $resourceAccessChecker = null)
+ public function __construct(IriConverterInterface $iriConverter, NormalizerInterface $normalizer, ResourceMetadataFactoryInterface $resourceMetadataFactory, ResourceAccessCheckerInterface $resourceAccessChecker = null, EventDispatcherInterface $dispatcher = null)
{
$this->iriConverter = $iriConverter;
$this->normalizer = $normalizer;
$this->resourceMetadataFactory = $resourceMetadataFactory;
$this->resourceAccessChecker = $resourceAccessChecker;
+ $this->dispatcher = $dispatcher;
}
public function __invoke($source, $args, $context, ResolveInfo $info)
@@ -62,7 +69,15 @@ public function __invoke($source, $args, $context, ResolveInfo $info)
$baseNormalizationContext = ['attributes' => $this->fieldsToAttributes($info)];
try {
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(PreReadEvent::NAME, new PreReadEvent(null));
+ }
+
$item = $this->iriConverter->getItemFromIri($args['id'], $baseNormalizationContext);
+
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(PostReadEvent::NAME, new PostReadEvent($item));
+ }
} catch (ItemNotFoundException $e) {
return null;
}
@@ -73,6 +88,16 @@ public function __invoke($source, $args, $context, ResolveInfo $info)
$normalizationContext = $resourceMetadata->getGraphqlAttribute('query', 'normalization_context', [], true);
- return $this->normalizer->normalize($item, ItemNormalizer::FORMAT, $normalizationContext + $baseNormalizationContext);
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(PreSerializeEvent::NAME, new PreSerializeEvent($item));
+ }
+
+ $normalizedObject = $this->normalizer->normalize($item, ItemNormalizer::FORMAT, $normalizationContext + $baseNormalizationContext);
+
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(PostSerializeEvent::NAME, new PostSerializeEvent($normalizedObject));
+ }
+
+ return $normalizedObject;
}
}
diff --git a/src/Validator/EventListener/ValidateListener.php b/src/Validator/EventListener/ValidateListener.php
index 5bf3afd3485..f6705fdd8dc 100644
--- a/src/Validator/EventListener/ValidateListener.php
+++ b/src/Validator/EventListener/ValidateListener.php
@@ -14,9 +14,13 @@
namespace ApiPlatform\Core\Validator\EventListener;
use ApiPlatform\Core\Bridge\Symfony\Validator\Exception\ValidationException;
+use ApiPlatform\Core\Event\PostValidateEvent;
+use ApiPlatform\Core\Event\PreValidateEvent;
+use ApiPlatform\Core\Events;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use ApiPlatform\Core\Util\RequestAttributesExtractor;
use ApiPlatform\Core\Validator\ValidatorInterface;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
/**
@@ -28,11 +32,13 @@ final class ValidateListener
{
private $validator;
private $resourceMetadataFactory;
+ private $eventDispatcher;
- public function __construct(ValidatorInterface $validator, ResourceMetadataFactoryInterface $resourceMetadataFactory)
+ public function __construct(ValidatorInterface $validator, ResourceMetadataFactoryInterface $resourceMetadataFactory, EventDispatcherInterface $eventDispatcher = null)
{
$this->validator = $validator;
$this->resourceMetadataFactory = $resourceMetadataFactory;
+ $this->eventDispatcher = $eventDispatcher;
}
/**
@@ -56,6 +62,14 @@ public function onKernelView(GetResponseForControllerResultEvent $event)
$resourceMetadata = $this->resourceMetadataFactory->create($attributes['resource_class']);
$validationGroups = $resourceMetadata->getOperationAttribute($attributes, 'validation_groups', null, true);
+ if (null !== $this->eventDispatcher) {
+ $this->eventDispatcher->dispatch(Events::PRE_VALIDATE, new PreValidateEvent($data));
+ }
+
$this->validator->validate($data, ['groups' => $validationGroups]);
+
+ if (null !== $this->eventDispatcher) {
+ $this->eventDispatcher->dispatch(Events::POST_VALIDATE, new PostValidateEvent($data));
+ }
}
}
diff --git a/tests/EventListener/AddFormatListenerTest.php b/tests/EventListener/AddFormatListenerTest.php
index 61da5fa819d..b7b6cdafc0c 100644
--- a/tests/EventListener/AddFormatListenerTest.php
+++ b/tests/EventListener/AddFormatListenerTest.php
@@ -14,10 +14,13 @@
namespace ApiPlatform\Core\Tests\EventListener;
use ApiPlatform\Core\Api\FormatsProviderInterface;
+use ApiPlatform\Core\Event\PostAddFormatEvent;
+use ApiPlatform\Core\Event\PreAddFormatEvent;
use ApiPlatform\Core\EventListener\AddFormatListener;
+use ApiPlatform\Core\Events;
use Negotiation\Negotiator;
use PHPUnit\Framework\TestCase;
-use Prophecy\Argument;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
@@ -33,28 +36,29 @@ public function testNoResourceClass()
$request = new Request();
$eventProphecy = $this->prophesize(GetResponseEvent::class);
- $eventProphecy->getRequest()->willReturn($request);
+ $eventProphecy->getRequest()->willReturn($request)->shouldBeCalled();
$event = $eventProphecy->reveal();
$formatsProviderProphecy = $this->prophesize(FormatsProviderInterface::class);
+ $formatsProviderProphecy->getFormatsFromAttributes()->shouldNotBeCalled();
$listener = new AddFormatListener(new Negotiator(), $formatsProviderProphecy->reveal());
$listener->onKernelRequest($event);
- $this->assertNull($request->getRequestFormat(null));
+ $this->assertNull($request->getFormat('application/vnd.notexist'));
}
public function testSupportedRequestFormat()
{
- $request = new Request([], [], ['_api_resource_class' => 'Foo', '_api_collection_operation_name' => 'get']);
+ $request = new Request([], [], ['_api_resource_class' => 'Foo']);
$request->setRequestFormat('xml');
$eventProphecy = $this->prophesize(GetResponseEvent::class);
- $eventProphecy->getRequest()->willReturn($request);
+ $eventProphecy->getRequest()->willReturn($request)->shouldBeCalled();
$event = $eventProphecy->reveal();
$formatsProviderProphecy = $this->prophesize(FormatsProviderInterface::class);
- $formatsProviderProphecy->getFormatsFromAttributes(Argument::any())->willReturn(['xml' => ['text/xml']]);
+ $formatsProviderProphecy->getFormatsFromAttributes([])->willReturn(['xml' => ['text/xml']])->shouldBeCalled();
$listener = new AddFormatListener(new Negotiator(), $formatsProviderProphecy->reveal());
$listener->onKernelRequest($event);
@@ -69,11 +73,11 @@ public function testRespondFlag()
$request->setRequestFormat('xml');
$eventProphecy = $this->prophesize(GetResponseEvent::class);
- $eventProphecy->getRequest()->willReturn($request);
+ $eventProphecy->getRequest()->willReturn($request)->shouldBeCalled();
$event = $eventProphecy->reveal();
$formatsProviderProphecy = $this->prophesize(FormatsProviderInterface::class);
- $formatsProviderProphecy->getFormatsFromAttributes(Argument::any())->willReturn(['xml' => ['text/xml']]);
+ $formatsProviderProphecy->getFormatsFromAttributes([])->willReturn(['xml' => ['text/xml']])->shouldBeCalled();
$listener = new AddFormatListener(new Negotiator(), $formatsProviderProphecy->reveal());
$listener->onKernelRequest($event);
@@ -87,15 +91,14 @@ public function testUnsupportedRequestFormat()
$this->expectException(NotAcceptableHttpException::class);
$this->expectExceptionMessage('Requested format "text/xml" is not supported. Supported MIME types are "application/json".');
- $request = new Request([], [], ['_api_resource_class' => 'Foo', '_api_collection_operation_name' => 'get']);
+ $request = new Request([], [], ['_api_resource_class' => 'Foo']);
$request->setRequestFormat('xml');
$eventProphecy = $this->prophesize(GetResponseEvent::class);
- $eventProphecy->getRequest()->willReturn($request);
+ $eventProphecy->getRequest()->willReturn($request)->shouldBeCalled();
$event = $eventProphecy->reveal();
-
$formatsProviderProphecy = $this->prophesize(FormatsProviderInterface::class);
- $formatsProviderProphecy->getFormatsFromAttributes(Argument::any())->willReturn(['json' => ['application/json']]);
+ $formatsProviderProphecy->getFormatsFromAttributes([])->willReturn(['json' => ['application/json']])->shouldBeCalled();
$listener = new AddFormatListener(new Negotiator(), $formatsProviderProphecy->reveal());
$listener->onKernelRequest($event);
@@ -105,15 +108,15 @@ public function testUnsupportedRequestFormat()
public function testSupportedAcceptHeader()
{
- $request = new Request([], [], ['_api_resource_class' => 'Foo', '_api_collection_operation_name' => 'get']);
+ $request = new Request([], [], ['_api_resource_class' => 'Foo']);
$request->headers->set('Accept', 'text/html, application/xhtml+xml, application/xml, application/json;q=0.9, */*;q=0.8');
$eventProphecy = $this->prophesize(GetResponseEvent::class);
- $eventProphecy->getRequest()->willReturn($request);
+ $eventProphecy->getRequest()->willReturn($request)->shouldBeCalled();
$event = $eventProphecy->reveal();
$formatsProviderProphecy = $this->prophesize(FormatsProviderInterface::class);
- $formatsProviderProphecy->getFormatsFromAttributes(Argument::any())->willReturn(['binary' => ['application/octet-stream'], 'json' => ['application/json']]);
+ $formatsProviderProphecy->getFormatsFromAttributes([])->willReturn(['binary' => ['application/octet-stream'], 'json' => ['application/json']])->shouldBeCalled();
$listener = new AddFormatListener(new Negotiator(), $formatsProviderProphecy->reveal());
$listener->onKernelRequest($event);
@@ -121,35 +124,17 @@ public function testSupportedAcceptHeader()
$this->assertSame('json', $request->getRequestFormat());
}
- public function testSupportedAcceptHeaderSymfonyBuiltInFormat()
- {
- $request = new Request([], [], ['_api_resource_class' => 'Foo', '_api_collection_operation_name' => 'get']);
- $request->headers->set('Accept', 'application/json');
-
- $eventProphecy = $this->prophesize(GetResponseEvent::class);
- $eventProphecy->getRequest()->willReturn($request);
- $event = $eventProphecy->reveal();
-
- $formatsProviderProphecy = $this->prophesize(FormatsProviderInterface::class);
- $formatsProviderProphecy->getFormatsFromAttributes(Argument::any())->willReturn(['jsonld' => ['application/ld+json', 'application/json']]);
-
- $listener = new AddFormatListener(new Negotiator(), $formatsProviderProphecy->reveal());
- $listener->onKernelRequest($event);
-
- $this->assertSame('jsonld', $request->getRequestFormat());
- }
-
public function testAcceptAllHeader()
{
- $request = new Request([], [], ['_api_resource_class' => 'Foo', '_api_collection_operation_name' => 'get']);
+ $request = new Request([], [], ['_api_resource_class' => 'Foo']);
$request->headers->set('Accept', 'text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8');
$eventProphecy = $this->prophesize(GetResponseEvent::class);
- $eventProphecy->getRequest()->willReturn($request);
+ $eventProphecy->getRequest()->willReturn($request)->shouldBeCalled();
$event = $eventProphecy->reveal();
$formatsProviderProphecy = $this->prophesize(FormatsProviderInterface::class);
- $formatsProviderProphecy->getFormatsFromAttributes(Argument::any())->willReturn(['binary' => ['application/octet-stream'], 'json' => ['application/json']]);
+ $formatsProviderProphecy->getFormatsFromAttributes([])->willReturn(['binary' => ['application/octet-stream'], 'json' => ['application/json']])->shouldBeCalled();
$listener = new AddFormatListener(new Negotiator(), $formatsProviderProphecy->reveal());
$listener->onKernelRequest($event);
@@ -163,34 +148,15 @@ public function testUnsupportedAcceptHeader()
$this->expectException(NotAcceptableHttpException::class);
$this->expectExceptionMessage('Requested format "text/html, application/xhtml+xml, application/xml;q=0.9" is not supported. Supported MIME types are "application/octet-stream", "application/json".');
- $request = new Request([], [], ['_api_resource_class' => 'Foo', '_api_collection_operation_name' => 'get']);
+ $request = new Request([], [], ['_api_resource_class' => 'Foo']);
$request->headers->set('Accept', 'text/html, application/xhtml+xml, application/xml;q=0.9');
$eventProphecy = $this->prophesize(GetResponseEvent::class);
- $eventProphecy->getRequest()->willReturn($request);
- $event = $eventProphecy->reveal();
-
- $formatsProviderProphecy = $this->prophesize(FormatsProviderInterface::class);
- $formatsProviderProphecy->getFormatsFromAttributes(Argument::any())->willReturn(['binary' => ['application/octet-stream'], 'json' => ['application/json']]);
-
- $listener = new AddFormatListener(new Negotiator(), $formatsProviderProphecy->reveal());
- $listener->onKernelRequest($event);
- }
-
- public function testUnsupportedAcceptHeaderSymfonyBuiltInFormat()
- {
- $this->expectException(NotAcceptableHttpException::class);
- $this->expectExceptionMessage('Requested format "text/xml" is not supported. Supported MIME types are "application/json".');
-
- $request = new Request([], [], ['_api_resource_class' => 'Foo', '_api_collection_operation_name' => 'get']);
- $request->headers->set('Accept', 'text/xml');
-
- $eventProphecy = $this->prophesize(GetResponseEvent::class);
- $eventProphecy->getRequest()->willReturn($request);
+ $eventProphecy->getRequest()->willReturn($request)->shouldBeCalled();
$event = $eventProphecy->reveal();
$formatsProviderProphecy = $this->prophesize(FormatsProviderInterface::class);
- $formatsProviderProphecy->getFormatsFromAttributes(Argument::any())->willReturn(['json' => ['application/json']]);
+ $formatsProviderProphecy->getFormatsFromAttributes([])->willReturn(['binary' => ['application/octet-stream'], 'json' => ['application/json']])->shouldBeCalled();
$listener = new AddFormatListener(new Negotiator(), $formatsProviderProphecy->reveal());
$listener->onKernelRequest($event);
@@ -201,15 +167,15 @@ public function testInvalidAcceptHeader()
$this->expectException(NotAcceptableHttpException::class);
$this->expectExceptionMessage('Requested format "invalid" is not supported. Supported MIME types are "application/json".');
- $request = new Request([], [], ['_api_resource_class' => 'Foo', '_api_collection_operation_name' => 'get']);
+ $request = new Request([], [], ['_api_resource_class' => 'Foo']);
$request->headers->set('Accept', 'invalid');
$eventProphecy = $this->prophesize(GetResponseEvent::class);
- $eventProphecy->getRequest()->willReturn($request);
+ $eventProphecy->getRequest()->willReturn($request)->shouldBeCalled();
$event = $eventProphecy->reveal();
$formatsProviderProphecy = $this->prophesize(FormatsProviderInterface::class);
- $formatsProviderProphecy->getFormatsFromAttributes(Argument::any())->willReturn(['json' => ['application/json']]);
+ $formatsProviderProphecy->getFormatsFromAttributes([])->willReturn(['json' => ['application/json']])->shouldBeCalled();
$listener = new AddFormatListener(new Negotiator(), $formatsProviderProphecy->reveal());
$listener->onKernelRequest($event);
@@ -217,16 +183,16 @@ public function testInvalidAcceptHeader()
public function testAcceptHeaderTakePrecedenceOverRequestFormat()
{
- $request = new Request([], [], ['_api_resource_class' => 'Foo', '_api_collection_operation_name' => 'get']);
+ $request = new Request([], [], ['_api_resource_class' => 'Foo']);
$request->headers->set('Accept', 'application/json');
$request->setRequestFormat('xml');
$eventProphecy = $this->prophesize(GetResponseEvent::class);
- $eventProphecy->getRequest()->willReturn($request);
+ $eventProphecy->getRequest()->willReturn($request)->shouldBeCalled();
$event = $eventProphecy->reveal();
$formatsProviderProphecy = $this->prophesize(FormatsProviderInterface::class);
- $formatsProviderProphecy->getFormatsFromAttributes(Argument::any())->willReturn(['xml' => ['application/xml'], 'json' => ['application/json']]);
+ $formatsProviderProphecy->getFormatsFromAttributes([])->willReturn(['xml' => ['application/xml'], 'json' => ['application/json']])->shouldBeCalled();
$listener = new AddFormatListener(new Negotiator(), $formatsProviderProphecy->reveal());
$listener->onKernelRequest($event);
@@ -239,14 +205,14 @@ public function testInvalidRouteFormat()
$this->expectException(NotFoundHttpException::class);
$this->expectExceptionMessage('Format "invalid" is not supported');
- $request = new Request([], [], ['_api_resource_class' => 'Foo', '_api_collection_operation_name' => 'get', '_format' => 'invalid']);
+ $request = new Request([], [], ['_api_resource_class' => 'Foo', '_format' => 'invalid']);
$eventProphecy = $this->prophesize(GetResponseEvent::class);
- $eventProphecy->getRequest()->willReturn($request);
+ $eventProphecy->getRequest()->willReturn($request)->shouldBeCalled();
$event = $eventProphecy->reveal();
$formatsProviderProphecy = $this->prophesize(FormatsProviderInterface::class);
- $formatsProviderProphecy->getFormatsFromAttributes(Argument::any())->willReturn(['json' => ['application/json']]);
+ $formatsProviderProphecy->getFormatsFromAttributes([])->willReturn(['json' => ['application/json']])->shouldBeCalled();
$listener = new AddFormatListener(new Negotiator(), $formatsProviderProphecy->reveal());
$listener->onKernelRequest($event);
diff --git a/tests/EventListener/DeserializeListenerTest.php b/tests/EventListener/DeserializeListenerTest.php
index de32a3a6991..91e02d13b36 100644
--- a/tests/EventListener/DeserializeListenerTest.php
+++ b/tests/EventListener/DeserializeListenerTest.php
@@ -14,10 +14,14 @@
namespace ApiPlatform\Core\Tests\EventListener;
use ApiPlatform\Core\Api\FormatsProviderInterface;
+use ApiPlatform\Core\Event\PostDeserializeEvent;
+use ApiPlatform\Core\Event\PreDeserializeEvent;
use ApiPlatform\Core\EventListener\DeserializeListener;
+use ApiPlatform\Core\Events;
use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
@@ -168,7 +172,11 @@ public function testDeserialize(string $method, bool $populateObject)
$serializerContextBuilderProphecy = $this->prophesize(SerializerContextBuilderInterface::class);
$serializerContextBuilderProphecy->createFromRequest(Argument::type(Request::class), false, Argument::type('array'))->willReturn([])->shouldBeCalled();
- $listener = new DeserializeListener($serializerProphecy->reveal(), $serializerContextBuilderProphecy->reveal(), $formatsProviderProphecy->reveal());
+ $eventDispatcher = $this->prophesize(EventDispatcherInterface::class);
+ $eventDispatcher->dispatch(Events::PRE_DESERIALIZE, new PreDeserializeEvent($request->getContent()))->shouldBeCalled(self::once());
+ $eventDispatcher->dispatch(Events::POST_DESERIALIZE, new PostDeserializeEvent($request->getContent()))->shouldBeCalled(self::once());
+
+ $listener = new DeserializeListener($serializerProphecy->reveal(), $serializerContextBuilderProphecy->reveal(), $formatsProviderProphecy->reveal(), $eventDispatcher->reveal());
$listener->onKernelRequest($eventProphecy->reveal());
}
diff --git a/tests/EventListener/ReadListenerTest.php b/tests/EventListener/ReadListenerTest.php
index e259bf70010..f0767583469 100644
--- a/tests/EventListener/ReadListenerTest.php
+++ b/tests/EventListener/ReadListenerTest.php
@@ -16,12 +16,16 @@
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
use ApiPlatform\Core\DataProvider\SubresourceDataProviderInterface;
+use ApiPlatform\Core\Event\PostReadEvent;
+use ApiPlatform\Core\Event\PreReadEvent;
use ApiPlatform\Core\EventListener\ReadListener;
+use ApiPlatform\Core\Events;
use ApiPlatform\Core\Exception\InvalidIdentifierException;
use ApiPlatform\Core\Exception\RuntimeException;
use ApiPlatform\Core\Identifier\IdentifierConverterInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -167,7 +171,11 @@ public function testRetrieveItem()
$event = $this->prophesize(GetResponseEvent::class);
$event->getRequest()->willReturn($request)->shouldBeCalled();
- $listener = new ReadListener($collectionDataProvider->reveal(), $itemDataProvider->reveal(), $subresourceDataProvider->reveal(), null, $identifierConverter->reveal());
+ $eventDispatcher = $this->prophesize(EventDispatcherInterface::class);
+ $eventDispatcher->dispatch(Events::PRE_READ, new PreReadEvent([]))->shouldBeCalled(self::once());
+ $eventDispatcher->dispatch(Events::POST_READ, new PostReadEvent($data))->shouldBeCalled(self::once());
+
+ $listener = new ReadListener($collectionDataProvider->reveal(), $itemDataProvider->reveal(), $subresourceDataProvider->reveal(), null, $identifierConverter->reveal(), $eventDispatcher->reveal());
$listener->onKernelRequest($event->reveal());
$this->assertSame($data, $request->attributes->get('data'));
diff --git a/tests/EventListener/SerializeListenerTest.php b/tests/EventListener/SerializeListenerTest.php
index b6f43acadd7..ae876ebfe7a 100644
--- a/tests/EventListener/SerializeListenerTest.php
+++ b/tests/EventListener/SerializeListenerTest.php
@@ -13,11 +13,15 @@
namespace ApiPlatform\Core\Tests\EventListener;
+use ApiPlatform\Core\Event\PostSerializeEvent;
+use ApiPlatform\Core\Event\PreSerializeEvent;
use ApiPlatform\Core\EventListener\SerializeListener;
+use ApiPlatform\Core\Events;
use ApiPlatform\Core\Serializer\ResourceList;
use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
@@ -90,15 +94,21 @@ public function testSerializeCollectionOperation()
$request = new Request([], [], ['_api_resource_class' => 'Foo', '_api_collection_operation_name' => 'get']);
$request->setRequestFormat('xml');
+ $data = new \stdClass();
+
$eventProphecy = $this->prophesize(GetResponseForControllerResultEvent::class);
- $eventProphecy->getControllerResult()->willReturn(new \stdClass())->shouldBeCalled();
+ $eventProphecy->getControllerResult()->willReturn($data)->shouldBeCalled();
$eventProphecy->getRequest()->willReturn($request)->shouldBeCalled();
$eventProphecy->setControllerResult('bar')->shouldBeCalled();
$serializerContextBuilderProphecy = $this->prophesize(SerializerContextBuilderInterface::class);
$serializerContextBuilderProphecy->createFromRequest(Argument::type(Request::class), true, Argument::type('array'))->willReturn($expectedContext)->shouldBeCalled();
- $listener = new SerializeListener($serializerProphecy->reveal(), $serializerContextBuilderProphecy->reveal());
+ $eventDispatcher = $this->prophesize(EventDispatcherInterface::class);
+ $eventDispatcher->dispatch(Events::PRE_SERIALIZE, new PreSerializeEvent($data))->shouldBeCalled(self::once());
+ $eventDispatcher->dispatch(Events::POST_SERIALIZE, new PostSerializeEvent('bar'))->shouldBeCalled(self::once());
+
+ $listener = new SerializeListener($serializerProphecy->reveal(), $serializerContextBuilderProphecy->reveal(), $eventDispatcher->reveal());
$listener->onKernelView($eventProphecy->reveal());
}
diff --git a/tests/EventListener/WriteListenerTest.php b/tests/EventListener/WriteListenerTest.php
index 49fde689b04..d8affb8c3e6 100644
--- a/tests/EventListener/WriteListenerTest.php
+++ b/tests/EventListener/WriteListenerTest.php
@@ -15,11 +15,15 @@
use ApiPlatform\Core\Api\IriConverterInterface;
use ApiPlatform\Core\DataPersister\DataPersisterInterface;
+use ApiPlatform\Core\Event\PostWriteEvent;
+use ApiPlatform\Core\Event\PreWriteEvent;
use ApiPlatform\Core\EventListener\WriteListener;
+use ApiPlatform\Core\Events;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyTableInheritance;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyTableInheritanceChild;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
@@ -127,7 +131,11 @@ public function testOnKernelViewWithControllerResultAndPersistWithImmutableResou
$request->setMethod($httpMethod);
$request->attributes->set(sprintf('_api_%s_operation_name', 'POST' === $httpMethod ? 'collection' : 'item'), strtolower($httpMethod));
- (new WriteListener($dataPersisterProphecy->reveal(), $iriConverterProphecy->reveal()))->onKernelView($event);
+ $eventDispatcher = $this->prophesize(EventDispatcherInterface::class);
+ $eventDispatcher->dispatch(Events::PRE_WRITE, new PreWriteEvent($event->getControllerResult()))->shouldBeCalled(self::once());
+ $eventDispatcher->dispatch(Events::POST_WRITE, new PostWriteEvent($event->getControllerResult()))->shouldBeCalled(self::once());
+
+ (new WriteListener($dataPersisterProphecy->reveal(), $iriConverterProphecy->reveal(), $eventDispatcher->reveal()))->onKernelView($event);
$this->assertSame($dummy2, $event->getControllerResult());
$this->assertEquals('/dummy/1', $request->attributes->get('_api_write_item_iri'));
diff --git a/tests/Validator/EventListener/ValidateListenerTest.php b/tests/Validator/EventListener/ValidateListenerTest.php
index 6ed24118185..1b1f5ccd640 100644
--- a/tests/Validator/EventListener/ValidateListenerTest.php
+++ b/tests/Validator/EventListener/ValidateListenerTest.php
@@ -13,6 +13,9 @@
namespace ApiPlatform\Core\Tests\Validator\EventListener;
+use ApiPlatform\Core\Event\PostValidateEvent;
+use ApiPlatform\Core\Event\PreValidateEvent;
+use ApiPlatform\Core\Events;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
use ApiPlatform\Core\Tests\Fixtures\DummyEntity;
@@ -20,6 +23,7 @@
use ApiPlatform\Core\Validator\Exception\ValidationException;
use ApiPlatform\Core\Validator\ValidatorInterface;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
@@ -63,7 +67,11 @@ public function testValidatorIsCalled()
list($resourceMetadataFactory, $event) = $this->createEventObject($expectedValidationGroups, $data);
- $validationViewListener = new ValidateListener($validator, $resourceMetadataFactory);
+ $eventDispatcher = $this->prophesize(EventDispatcher::class);
+ $eventDispatcher->dispatch(Events::PRE_VALIDATE, new PreValidateEvent($data))->shouldBeCalled(self::once());
+ $eventDispatcher->dispatch(Events::POST_VALIDATE, new PostValidateEvent($data))->shouldBeCalled(self::once());
+
+ $validationViewListener = new ValidateListener($validator, $resourceMetadataFactory, $eventDispatcher->reveal());
$validationViewListener->onKernelView($event);
}