diff --git a/plugins/collection-view/README.md b/plugins/collection-view/README.md index 0c5b047..58982df 100644 --- a/plugins/collection-view/README.md +++ b/plugins/collection-view/README.md @@ -157,3 +157,37 @@ return [ ] ]; ``` + +## Events + +CollectionView dispatches two events. Read the CakePHP docs for how to +[register listeners](https://book.cakephp.org/5/en/core-libraries/events.html#registering-listeners). + +### MixerApi.CollectionView.beforeSerialize + +The event contains the Serializer as the subject and the `$type` of data (i.e. "xml" or "json") and is dispatched just +before serialization. + +```php +use \Cake\Event\Event; +use \Cake\Event\EventManager; +use \MixerApi\CollectionView\Serializer + +EventManager::instance()->on(Serializer::BEFORE_SERIALIZE_EVENT, function (Event $event, string $type) { + /** @var Serializer $serializer */ + $serializer = $event->getSubject(); + $data = $serializer->getData(); // modify if you want or other stuff + $serializer->setData($data); +}) +``` + +### MixerApi.CollectionView.afterSerialize + +The event contains the Serializer as the subject, the `$type` of data (i.e. "xml" or "json") and the serialized data +as `$data`. This is dispatched just after serialization. + +```php +EventManager::instance()->on(Serializer::BEFORE_SERIALIZE_EVENT, function (Event $event, string $type, string $data) { + // whatever you want to do here +}) +``` diff --git a/plugins/collection-view/src/Serializer.php b/plugins/collection-view/src/Serializer.php index 769f450..deae5aa 100644 --- a/plugins/collection-view/src/Serializer.php +++ b/plugins/collection-view/src/Serializer.php @@ -7,6 +7,8 @@ use Cake\Core\Configure; use Cake\Datasource\Paging\PaginatedResultSet; use Cake\Datasource\ResultSetInterface; +use Cake\Event\Event; +use Cake\Event\EventManager; use Cake\Http\ServerRequest; use Cake\Utility\Xml; use Cake\View\Helper\PaginatorHelper; @@ -14,16 +16,11 @@ /** * Serializes the CollectionView into either JSON or XML. - * - * @uses \Adbar\Dot - * @uses \Cake\Core\Configure - * @uses \Cake\Utility\Xml */ class Serializer { - private ?ServerRequest $request; - - private ?PaginatorHelper $paginator; + public const BEFORE_SERIALIZE_EVENT = 'MixerApi.CollectionView.beforeSerialize'; + public const AFTER_SERIALIZE_EVENT = 'MixerApi.CollectionView.afterSerialize'; /** * serialized data @@ -44,10 +41,11 @@ class Serializer * @param \Cake\Http\ServerRequest|null $request optional ServerRequest * @param \Cake\View\Helper\PaginatorHelper|null $paginator optional PaginatorHelper */ - public function __construct(mixed $serialize, ?ServerRequest $request = null, ?PaginatorHelper $paginator = null) - { - $this->request = $request; - $this->paginator = $paginator; + public function __construct( + mixed $serialize, + private ?ServerRequest $request = null, + private ?PaginatorHelper $paginator = null + ) { $this->config = Configure::read('CollectionView'); if ($serialize instanceof ResultSetInterface || $serialize instanceof PaginatedResultSet) { @@ -66,12 +64,21 @@ public function __construct(mixed $serialize, ?ServerRequest $request = null, ?P */ public function asJson(int $jsonOptions = 0): string { + EventManager::instance()->dispatch(new Event(self::BEFORE_SERIALIZE_EVENT, $this, [ + 'type' => 'json', + ])); + $json = json_encode($this->data, $jsonOptions); if ($json === false) { throw new RuntimeException(json_last_error_msg(), json_last_error()); } + EventManager::instance()->dispatch(new Event(self::AFTER_SERIALIZE_EVENT, $this, [ + 'type' => 'json', + 'data' => $json, + ])); + return $json; } @@ -85,7 +92,18 @@ public function asJson(int $jsonOptions = 0): string */ public function asXml(array $options, string $rootNode = 'response'): string { - return Xml::fromArray([$rootNode => $this->data], $options)->saveXML(); + EventManager::instance()->dispatch(new Event(self::BEFORE_SERIALIZE_EVENT, $this, [ + 'type' => 'xml', + ])); + + $xml = Xml::fromArray([$rootNode => $this->data], $options)->saveXML(); + + EventManager::instance()->dispatch(new Event(self::AFTER_SERIALIZE_EVENT, $this, [ + 'type' => 'xml', + 'data' => $xml, + ])); + + return $xml; } /** @@ -96,6 +114,31 @@ public function getData(): mixed return $this->data; } + /** + * @param mixed $data The data to be serialized + * @return void + */ + public function setData(mixed $data): void + { + $this->data = $data; + } + + /** + * @return \Cake\Http\ServerRequest|null + */ + public function getRequest(): ?ServerRequest + { + return $this->request; + } + + /** + * @return \Cake\View\Helper\PaginatorHelper|null + */ + public function getPaginatorHelper(): ?PaginatorHelper + { + return $this->paginator; + } + /** * @param \Cake\Datasource\ResultSetInterface|\Cake\Datasource\Paging\PaginatedResultSet $resultSet the data to be converted into a HAL array * @return array diff --git a/plugins/collection-view/tests/TestCase/SerializerTest.php b/plugins/collection-view/tests/TestCase/SerializerTest.php index bcae1cc..c8f96ac 100644 --- a/plugins/collection-view/tests/TestCase/SerializerTest.php +++ b/plugins/collection-view/tests/TestCase/SerializerTest.php @@ -2,16 +2,16 @@ namespace MixerApi\CollectionView\Test\TestCase; -use Cake\Controller\ComponentRegistry; use Cake\Datasource\FactoryLocator; use Cake\Datasource\Paging\PaginatedResultSet; +use Cake\Event\EventList; +use Cake\Event\EventManager; use Cake\Http\Response; use Cake\Http\ServerRequest; use Cake\Routing\RouteBuilder; use Cake\Routing\Router; use Cake\TestSuite\TestCase; use Cake\View\Helper\PaginatorHelper; -use Cake\View\View; use MixerApi\CollectionView\Configuration; use MixerApi\CollectionView\View\JsonCollectionView; use MixerApi\CollectionView\Serializer; @@ -20,7 +20,7 @@ class SerializerTest extends TestCase { /** - * @var string[] + * @inheritdoc */ public array $fixtures = [ 'plugin.MixerApi/CollectionView.Actors', @@ -34,6 +34,8 @@ public function setUp(): void public function test_as_json(): void { + EventManager::instance()->trackEvents(true)->setEventList(new EventList()); + $actor = FactoryLocator::get('Table')->get('Actors'); $result = new PaginatedResultSet($actor->find()->limit(1)->all(), [ @@ -85,6 +87,8 @@ public function test_as_json(): void $obj = json_decode($json); $this->assertIsObject($obj); + $this->assertEventFired(Serializer::BEFORE_SERIALIZE_EVENT); + $this->assertEventFired(Serializer::AFTER_SERIALIZE_EVENT); $this->assertEquals(20, $obj->collection->count); $this->assertEquals(60, $obj->collection->total); $this->assertEquals('/', $obj->collection->url); @@ -93,6 +97,7 @@ public function test_as_json(): void public function test_as_xml(): void { + EventManager::instance()->trackEvents(true)->setEventList(new EventList()); $actor = FactoryLocator::get('Table')->get('Actors'); $result = new PaginatedResultSet($actor->find()->limit(1)->all(), [ @@ -142,6 +147,8 @@ public function test_as_xml(): void $this->assertIsString($xml); $simpleXml = simplexml_load_string($xml); + $this->assertEventFired(Serializer::BEFORE_SERIALIZE_EVENT); + $this->assertEventFired(Serializer::AFTER_SERIALIZE_EVENT); $this->assertInstanceOf(SimpleXMLElement::class, $simpleXml); $this->assertEquals('/', $simpleXml->collection->url); $this->assertInstanceOf(SimpleXMLElement::class, $simpleXml->data); diff --git a/plugins/hal-view/README.md b/plugins/hal-view/README.md index 762e158..bdbbb15 100644 --- a/plugins/hal-view/README.md +++ b/plugins/hal-view/README.md @@ -245,3 +245,36 @@ use Cake\Http\ServerRequest; use Cake\View\Helper\PaginatorHelper; $json = (new JsonSerializer($data, new ServerRequest(), new PaginatorHelper()))->asJson(); ``` + +## Events + +HalView dispatches two events. Read the CakePHP docs for how to +[register listeners](https://book.cakephp.org/5/en/core-libraries/events.html#registering-listeners). + +### MixerApi.HalView.beforeSerialize + +The event contains the Serializer as the subject and is dispatched just before serialization. + +```php +use \Cake\Event\Event; +use \Cake\Event\EventManager; +use \MixerApi\HalView\JsonSerializer; + +EventManager::instance()->on(JsonSerializer::BEFORE_SERIALIZE_EVENT, function (Event $event) { + /** @var Serializer $serializer */ + $serializer = $event->getSubject(); + $data = $serializer->getData(); // modify if you want or other stuff + $serializer->setData($data); +}) +``` + +### MixerApi.HalView.afterSerialize + +The event contains the Serializer as the subject and the serialized data as `$data`. This is dispatched just after +serialization. + +```php +EventManager::instance()->on(JsonSerializer::AFTER_SERIALIZE_EVENT, function (Event $event, string $data) { + // whatever you want to do here +}) +``` diff --git a/plugins/hal-view/src/JsonSerializer.php b/plugins/hal-view/src/JsonSerializer.php index 75d00a5..f0f4a13 100644 --- a/plugins/hal-view/src/JsonSerializer.php +++ b/plugins/hal-view/src/JsonSerializer.php @@ -6,6 +6,8 @@ use Cake\Datasource\EntityInterface; use Cake\Datasource\Paging\PaginatedResultSet; use Cake\Datasource\ResultSetInterface; +use Cake\Event\Event; +use Cake\Event\EventManager; use Cake\Http\ServerRequest; use Cake\ORM\Entity; use Cake\Utility\Inflector; @@ -19,16 +21,11 @@ * Creates a HAL+JSON resource * * @link https://tools.ietf.org/html/draft-kelly-json-hal-06 - * @uses \Cake\Utility\Inflector - * @uses \MixerApi\Core\View\SerializableAssociation - * @uses ReflectionClass */ class JsonSerializer { - private ?ServerRequest $request; - - private ?PaginatorHelper $paginator; - + public const BEFORE_SERIALIZE_EVENT = 'MixerApi.HalView.beforeSerialize'; + public const AFTER_SERIALIZE_EVENT = 'MixerApi.HalView.afterSerialize'; private mixed $data; /** @@ -38,11 +35,11 @@ class JsonSerializer * @param \Cake\Http\ServerRequest|null $request optional ServerRequest * @param \Cake\View\Helper\PaginatorHelper|null $paginator optional PaginatorHelper */ - public function __construct(mixed $serialize, ?ServerRequest $request = null, ?PaginatorHelper $paginator = null) - { - $this->request = $request; - $this->paginator = $paginator; - + public function __construct( + mixed $serialize, + private ?ServerRequest $request = null, + private ?PaginatorHelper $paginator = null + ) { $hal = $this->recursion($serialize); if ($hal instanceof ResultSetInterface || $hal instanceof PaginatedResultSet) { @@ -63,25 +60,56 @@ public function __construct(mixed $serialize, ?ServerRequest $request = null, ?P */ public function asJson(int $jsonOptions = 0): string { + EventManager::instance()->dispatch(new Event(self::BEFORE_SERIALIZE_EVENT, $this)); + $json = json_encode($this->data, $jsonOptions); if ($json === false) { throw new RuntimeException(json_last_error_msg(), json_last_error()); } + EventManager::instance()->dispatch(new Event(self::AFTER_SERIALIZE_EVENT, $this, [ + 'data' => $json, + ])); + return $json; } /** * Get HAL data as an array * - * @return array|null + * @return mixed */ - public function getData(): ?array + public function getData(): mixed { return $this->data; } + /** + * @param mixed $data The data to be serialized + * @return void + */ + public function setData(mixed $data): void + { + $this->data = $data; + } + + /** + * @return \Cake\Http\ServerRequest|null + */ + public function getRequest(): ?ServerRequest + { + return $this->request; + } + + /** + * @return \Cake\View\Helper\PaginatorHelper|null + */ + public function getPaginatorHelper(): ?PaginatorHelper + { + return $this->paginator; + } + /** * Recursive method for converting mixed data into HAL. This method converts instances of Cake\ORM\Entity into * HAL resources, but does not serialize the data. diff --git a/plugins/hal-view/tests/TestCase/JsonSerializerTest.php b/plugins/hal-view/tests/TestCase/JsonSerializerTest.php index 02f776c..6cac6bb 100644 --- a/plugins/hal-view/tests/TestCase/JsonSerializerTest.php +++ b/plugins/hal-view/tests/TestCase/JsonSerializerTest.php @@ -4,6 +4,8 @@ use Cake\Datasource\FactoryLocator; use Cake\Datasource\Paging\PaginatedResultSet; +use Cake\Event\EventList; +use Cake\Event\EventManager; use Cake\Http\Response; use Cake\Http\ServerRequest; use Cake\ORM\Table; @@ -71,6 +73,8 @@ public function setUp(): void public function test_collection(): void { + EventManager::instance()->trackEvents(true)->setEventList(new EventList()); + $actor = FactoryLocator::get('Table')->get('Actors'); $result = new PaginatedResultSet($actor->find()->applyOptions(['contain' => 'Films'])->limit(1)->all(), [ 'sort' => null, @@ -101,6 +105,8 @@ public function test_collection(): void $this->assertIsString($json); $this->assertIsObject(json_decode($json)); + $this->assertEventFired(JsonSerializer::BEFORE_SERIALIZE_EVENT); + $this->assertEventFired(JsonSerializer::AFTER_SERIALIZE_EVENT); } public function test_item(): void diff --git a/plugins/json-ld-view/README.md b/plugins/json-ld-view/README.md index c7f520e..9c7309c 100644 --- a/plugins/json-ld-view/README.md +++ b/plugins/json-ld-view/README.md @@ -345,3 +345,36 @@ use Cake\Http\ServerRequest; use Cake\View\Helper\PaginatorHelper; $json = (new JsonSerializer($data, new ServerRequest(), new PaginatorHelper()))->asJson(); ``` + +## Events + +JsonLdView dispatches two events. Read the CakePHP docs for how to +[register listeners](https://book.cakephp.org/5/en/core-libraries/events.html#registering-listeners). + +### MixerApi.JsonLdView.beforeSerialize + +The event contains the Serializer as the subject and is dispatched just before serialization. + +```php +use \Cake\Event\Event; +use \Cake\Event\EventManager; +use \MixerApi\JsonLdView\JsonSerializer; + +EventManager::instance()->on(JsonSerializer::BEFORE_SERIALIZE_EVENT, function (Event $event) { + /** @var Serializer $serializer */ + $serializer = $event->getSubject(); + $data = $serializer->getData(); // modify if you want or other stuff + $serializer->setData($data); +}) +``` + +### MixerApi.JsonLdView.afterSerialize + +The event contains the Serializer as the subject and the serialized data as `$data`. This is dispatched just after +serialization. + +```php +EventManager::instance()->on(JsonSerializer::AFTER_SERIALIZE_EVENT, function (Event $event, string $data) { + // whatever you want to do here +}) +``` diff --git a/plugins/json-ld-view/src/JsonSerializer.php b/plugins/json-ld-view/src/JsonSerializer.php index 8524f43..b434933 100644 --- a/plugins/json-ld-view/src/JsonSerializer.php +++ b/plugins/json-ld-view/src/JsonSerializer.php @@ -7,6 +7,8 @@ use Cake\Datasource\EntityInterface; use Cake\Datasource\Paging\PaginatedResultSet; use Cake\Datasource\ResultSetInterface; +use Cake\Event\Event; +use Cake\Event\EventManager; use Cake\Http\ServerRequest; use Cake\ORM\Entity; use Cake\View\Helper\PaginatorHelper; @@ -17,15 +19,13 @@ /** * Creates a JSON-LD resource * - * @uses \MixerApi\Core\View\SerializableAssociation * @link https://json-ld.org/ * @link https://lists.w3.org/Archives/Public/public-hydra/2015Oct/0163.html */ class JsonSerializer { - private ?ServerRequest $request; - - private ?PaginatorHelper $paginator; + public const BEFORE_SERIALIZE_EVENT = 'MixerApi.JsonLdView.beforeSerialize'; + public const AFTER_SERIALIZE_EVENT = 'MixerApi.JsonLdView.afterSerialize'; /** * JSON-LD data array @@ -53,11 +53,11 @@ class JsonSerializer * @param \Cake\Http\ServerRequest|null $request optional ServerRequest * @param \Cake\View\Helper\PaginatorHelper|null $paginator optional PaginatorHelper */ - public function __construct(mixed $serialize, ?ServerRequest $request = null, ?PaginatorHelper $paginator = null) - { - $this->request = $request; - $this->paginator = $paginator; - + public function __construct( + mixed $serialize, + private ?ServerRequest $request = null, + private ?PaginatorHelper $paginator = null + ) { $jsonLd = $this->recursion($serialize); $this->config = Configure::read('JsonLdView'); if (isset($this->config['isHydra']) && $this->config['isHydra']) { @@ -82,12 +82,18 @@ public function __construct(mixed $serialize, ?ServerRequest $request = null, ?P */ public function asJson(int $jsonOptions = 0): string { + EventManager::instance()->dispatch(new Event(self::BEFORE_SERIALIZE_EVENT, $this)); + $json = json_encode($this->data, $jsonOptions); if ($json === false) { throw new RuntimeException(json_last_error_msg(), json_last_error()); } + EventManager::instance()->dispatch(new Event(self::AFTER_SERIALIZE_EVENT, $this, [ + 'data' => $json, + ])); + return $json; } @@ -101,6 +107,31 @@ public function getData(): ?array return $this->data; } + /** + * @param mixed $data The data to be serialized + * @return void + */ + public function setData(mixed $data): void + { + $this->data = $data; + } + + /** + * @return \Cake\Http\ServerRequest|null + */ + public function getRequest(): ?ServerRequest + { + return $this->request; + } + + /** + * @return \Cake\View\Helper\PaginatorHelper|null + */ + public function getPaginatorHelper(): ?PaginatorHelper + { + return $this->paginator; + } + /** * Recursive method for converting mixed data into JSON-LD. This method converts instances of Cake\ORM\Entity into * JSON-LD resources, but does not serialize the data. diff --git a/plugins/json-ld-view/tests/TestCase/JsonSerializerTest.php b/plugins/json-ld-view/tests/TestCase/JsonSerializerTest.php index 9a40d73..abb0bf7 100644 --- a/plugins/json-ld-view/tests/TestCase/JsonSerializerTest.php +++ b/plugins/json-ld-view/tests/TestCase/JsonSerializerTest.php @@ -4,6 +4,8 @@ use Cake\Datasource\FactoryLocator; use Cake\Datasource\Paging\PaginatedResultSet; +use Cake\Event\EventList; +use Cake\Event\EventManager; use Cake\Http\Response; use Cake\Http\ServerRequest; use Cake\ORM\Table; @@ -13,7 +15,6 @@ use Cake\View\Helper\PaginatorHelper; use Cake\View\View; use MixerApi\JsonLdView\JsonSerializer; -use MixerApi\JsonLdView\View\JsonLdView; class JsonSerializerTest extends TestCase { @@ -66,6 +67,8 @@ public function setUp(): void */ public function test_collection(string $resultType): void { + EventManager::instance()->trackEvents(true)->setEventList(new EventList()); + /** @var Table $actor */ $actor = FactoryLocator::get('Table')->get('Actors'); $result = $actor->find()->contain('Films')->limit(1)->all(); @@ -96,6 +99,8 @@ public function test_collection(string $resultType): void $this->assertIsString($json); $this->assertIsObject(json_decode($json)); + $this->assertEventFired(JsonSerializer::BEFORE_SERIALIZE_EVENT); + $this->assertEventFired(JsonSerializer::AFTER_SERIALIZE_EVENT); } public static function dataProviderForCollectionTypes(): array