diff --git a/CHANGELOG.md b/CHANGELOG.md index 61537c4..25c1711 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,34 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added * Added `take` method to `Repository` to allow fetching resources without id. +* Added links and meta to `ItemInterface`. +N.B. This is a breaking change if you implement the `ItemInterface` yourself instead of using the supplied `Item`. +* Added `Jsonapi` class. +* Added links and meta to `OneRelationInterface` and `ManyRelationInterface`. +N.B. This is a breaking change if you implement (one of) these interfaces yourself instead of using the supplied relations. +* Added `Link` and `Links` classes. +* Added links to `Error`. + +### Changed + +* `Error::getMeta()` now returns a `Meta` instance instead of an `ErrorMeta` instance. The `Meta` class does not have the `has` and `get` methods, but uses magic overloading methods (e.g. `__get` and `__set`) just like `Item`. +N.B. This is a breaking change if you use meta on errors. +* `DocumentInterface::getLinks()` now returns a `Links` instance instead of a plain array. If no links are present, it returns `null`. All implementations have been updated to reflect these changes. +N.B. This is a minor breaking change if you use links on documents. +* `DocumentInterface::getMeta()` now returns a `Meta` instance instead of a plain array. If no meta is present, it returns `null`. All implementations have been updated to reflect these changes. +N.B. This is a minor breaking change if you use meta on documents. +* `DocumentInterface::getJsonapi()` now returns a `Jsonapi` instance instead of a plain array. If no jsonapi is present, it returns `null`. All implementations have been updated to reflect these changes. +* Parameters for `ItemInterface::setRelation()` have changed to include optional `Links` and `Meta` objects. +* `JsonApi\ErrorsParser`, `JsonApi\Hydrator` and `JsonApi\Parser` have an extra dependency in their constructor. +N.B. Make sure to add this dependency if you've overwritten `ServiceProvider::registerParser` or construct the `JsonApi\Parser` yourself. + +### Removed + +* Removed `ErrorMeta` class in favor of generic `Meta` class. + +### Fixed + +* Fixed parsing of [JSON:API object](https://jsonapi.org/format/#document-jsonapi-object) in document. ## [0.14.0] - 2019-01-23 diff --git a/README.MD b/README.MD index ce5d90e..dacd9fb 100644 --- a/README.MD +++ b/README.MD @@ -149,6 +149,56 @@ class BlogItem extends Item This package uses [Laravel Collections](https://laravel.com/docs/collections) as a wrapper for item arrays. +## Links + +All objects that can have links use an instance of `Links` to store their links. +These are in turn an instance of `Link`. + +### Example + +Given the following JSON: +``` json +{ + "data": [], + "links": { + "self": "http://example.com/test" + } +} +``` + +You can get the links this way: +``` php +/** @var $document \Swis\JsonApi\Client\Document */ +$links = $document->getLinks(); +echo $links->self->getHref(); // http://example.com/test +``` + + +## Meta + +All objects that can have meta information use an instance of `Meta` to store their metadata. +This is a simple array-like object with key-value pairs. + +### Example + +Given the following JSON: +``` json +{ + "data": [], + "meta": { + "foo": "bar" + } +} +``` + +You can get the meta this way: +``` php +/** @var $document \Swis\JsonApi\Client\Document */ +$meta = $document->getMeta(); +echo $meta->foo; // bar +``` + + ## TypeMapper All custom models must be registered with the `TypeMapper`. diff --git a/src/Document.php b/src/Document.php index 4b9710b..7df5721 100644 --- a/src/Document.php +++ b/src/Document.php @@ -5,24 +5,18 @@ use Swis\JsonApi\Client\Errors\ErrorCollection; use Swis\JsonApi\Client\Interfaces\DataInterface; use Swis\JsonApi\Client\Interfaces\DocumentInterface; +use Swis\JsonApi\Client\Traits\HasLinks; +use Swis\JsonApi\Client\Traits\HasMeta; class Document implements DocumentInterface { + use HasLinks, HasMeta; + /** * @var \Swis\JsonApi\Client\Interfaces\DataInterface */ protected $data; - /** - * @var array - */ - protected $meta = []; - - /** - * @var array - */ - protected $links = []; - /** * @var \Swis\JsonApi\Client\Errors\ErrorCollection */ @@ -34,9 +28,9 @@ class Document implements DocumentInterface protected $included; /** - * @var array + * @var \Swis\JsonApi\Client\Jsonapi|null */ - protected $jsonapi = []; + protected $jsonapi; public function __construct() { @@ -44,38 +38,6 @@ public function __construct() $this->included = new Collection(); } - /** - * @return array - */ - public function getMeta(): array - { - return $this->meta; - } - - /** - * @param array $meta - */ - public function setMeta(array $meta) - { - $this->meta = $meta; - } - - /** - * @return array - */ - public function getLinks(): array - { - return $this->links; - } - - /** - * @param array $links - */ - public function setLinks(array $links) - { - $this->links = $links; - } - /** * @return \Swis\JsonApi\Client\Errors\ErrorCollection */ @@ -129,17 +91,17 @@ public function setIncluded(Collection $included) } /** - * @return array + * @return \Swis\JsonApi\Client\Jsonapi|null */ - public function getJsonapi(): array + public function getJsonapi() { return $this->jsonapi; } /** - * @param array $jsonapi + * @param \Swis\JsonApi\Client\Jsonapi|null $jsonapi */ - public function setJsonapi(array $jsonapi) + public function setJsonapi(Jsonapi $jsonapi = null) { $this->jsonapi = $jsonapi; } @@ -186,8 +148,8 @@ public function toArray(): array { $document = []; - if (!empty($this->getLinks())) { - $document['links'] = $this->links; + if ($this->getLinks() !== null) { + $document['links'] = $this->getLinks()->toArray(); } if (!empty($this->getData())) { @@ -198,16 +160,16 @@ public function toArray(): array $document['included'] = $this->getIncluded()->toJsonApiArray(); } - if (!empty($this->getMeta())) { - $document['meta'] = $this->meta; + if ($this->getMeta() !== null) { + $document['meta'] = $this->getMeta()->toArray(); } if ($this->hasErrors()) { $document['errors'] = $this->errors->toArray(); } - if (!empty($this->getJsonapi())) { - $document['jsonapi'] = $this->jsonapi; + if ($this->getJsonapi() !== null) { + $document['jsonapi'] = $this->getJsonapi()->toArray(); } return $document; diff --git a/src/Errors/Error.php b/src/Errors/Error.php index 6247911..c0a1cb7 100644 --- a/src/Errors/Error.php +++ b/src/Errors/Error.php @@ -2,8 +2,15 @@ namespace Swis\JsonApi\Client\Errors; +use Swis\JsonApi\Client\Links; +use Swis\JsonApi\Client\Meta; +use Swis\JsonApi\Client\Traits\HasLinks; +use Swis\JsonApi\Client\Traits\HasMeta; + class Error { + use HasLinks, HasMeta; + /** * @var string|null */ @@ -34,30 +41,28 @@ class Error */ protected $source; - /** - * @var \Swis\JsonApi\Client\Errors\ErrorMeta|null - */ - protected $meta; - /** * @param string|null $id + * @param \Swis\JsonApi\Client\Links|null $links * @param string|null $status * @param string|null $code * @param string|null $title * @param string|null $detail * @param \Swis\JsonApi\Client\Errors\ErrorSource|null $source - * @param \Swis\JsonApi\Client\Errors\ErrorMeta|null $meta + * @param \Swis\JsonApi\Client\Meta|null $meta */ public function __construct( string $id = null, + Links $links = null, string $status = null, string $code = null, string $title = null, string $detail = null, ErrorSource $source = null, - ErrorMeta $meta = null + Meta $meta = null ) { $this->id = $id; + $this->links = $links; $this->status = $status; $this->code = $code; $this->title = $title; @@ -113,12 +118,4 @@ public function getSource() { return $this->source; } - - /** - * @return \Swis\JsonApi\Client\Errors\ErrorMeta|null - */ - public function getMeta() - { - return $this->meta; - } } diff --git a/src/Errors/ErrorMeta.php b/src/Errors/ErrorMeta.php deleted file mode 100644 index 1fc48a1..0000000 --- a/src/Errors/ErrorMeta.php +++ /dev/null @@ -1,47 +0,0 @@ -data = $data; - } - - /** - * @param string $key - * - * @return bool - */ - public function has(string $key): bool - { - return array_has($this->data, $key); - } - - /** - * @param string $key - * - * @return mixed - */ - public function get(string $key) - { - return array_get($this->data, $key); - } - - /** - * @return array - */ - public function asArray(): array - { - return $this->data; - } -} diff --git a/src/Interfaces/DocumentInterface.php b/src/Interfaces/DocumentInterface.php index 69f40d9..95928b7 100644 --- a/src/Interfaces/DocumentInterface.php +++ b/src/Interfaces/DocumentInterface.php @@ -23,14 +23,14 @@ public function getErrors(): ErrorCollection; public function hasErrors(): bool; /** - * @return mixed + * @return \Swis\JsonApi\Client\Meta|null */ - public function getMeta(): array; + public function getMeta(); /** - * @return mixed + * @return \Swis\JsonApi\Client\Links|null */ - public function getLinks(): array; + public function getLinks(); /** * @return mixed @@ -38,7 +38,7 @@ public function getLinks(): array; public function getIncluded(): Collection; /** - * @return mixed + * @return \Swis\JsonApi\Client\Jsonapi|null */ public function getJsonapi(); diff --git a/src/Interfaces/ItemInterface.php b/src/Interfaces/ItemInterface.php index 62ee47d..961631b 100644 --- a/src/Interfaces/ItemInterface.php +++ b/src/Interfaces/ItemInterface.php @@ -2,6 +2,9 @@ namespace Swis\JsonApi\Client\Interfaces; +use Swis\JsonApi\Client\Links; +use Swis\JsonApi\Client\Meta; + interface ItemInterface extends DataInterface { /** @@ -38,6 +41,30 @@ public function getType(): string; */ public function setType(string $type); + /** + * @param \Swis\JsonApi\Client\Links|null $links + * + * @return $this + */ + public function setLinks(Links $links = null); + + /** + * @return \Swis\JsonApi\Client\Links|null + */ + public function getLinks(); + + /** + * @param \Swis\JsonApi\Client\Meta|null $meta + * + * @return $this + */ + public function setMeta(Meta $meta = null); + + /** + * @return \Swis\JsonApi\Client\Meta|null + */ + public function getMeta(); + /** * @param array $attributes * @@ -78,12 +105,14 @@ public function getAvailableRelations(): array; /** * Set the specific relationship in the model. * - * @param string $relation - * @param DataInterface $value + * @param string $relation + * @param \Swis\JsonApi\Client\Interfaces\DataInterface $value + * @param \Swis\JsonApi\Client\Links|null $links + * @param \Swis\JsonApi\Client\Meta|null $meta * * @return static */ - public function setRelation(string $relation, DataInterface $value); + public function setRelation(string $relation, DataInterface $value, Links $links = null, Meta $meta = null); /** * @TODO: MEGA TODO. Set up a serializer for the Item so that we can remove this, getRelationships etc diff --git a/src/Interfaces/ManyRelationInterface.php b/src/Interfaces/ManyRelationInterface.php index 8f8ce4f..a2daa31 100644 --- a/src/Interfaces/ManyRelationInterface.php +++ b/src/Interfaces/ManyRelationInterface.php @@ -3,6 +3,8 @@ namespace Swis\JsonApi\Client\Interfaces; use Swis\JsonApi\Client\Collection; +use Swis\JsonApi\Client\Links; +use Swis\JsonApi\Client\Meta; interface ManyRelationInterface { @@ -39,4 +41,24 @@ public function setOmitIncluded(bool $omitIncluded); * @return bool */ public function shouldOmitIncluded(): bool; + + /** + * @param \Swis\JsonApi\Client\Links|null $links + */ + public function setLinks(Links $links = null); + + /** + * @return \Swis\JsonApi\Client\Links|null + */ + public function getLinks(); + + /** + * @param \Swis\JsonApi\Client\Meta|null $meta + */ + public function setMeta(Meta $meta = null); + + /** + * @return \Swis\JsonApi\Client\Meta|null + */ + public function getMeta(); } diff --git a/src/Interfaces/OneRelationInterface.php b/src/Interfaces/OneRelationInterface.php index 0de7375..619d673 100644 --- a/src/Interfaces/OneRelationInterface.php +++ b/src/Interfaces/OneRelationInterface.php @@ -2,6 +2,9 @@ namespace Swis\JsonApi\Client\Interfaces; +use Swis\JsonApi\Client\Links; +use Swis\JsonApi\Client\Meta; + interface OneRelationInterface { /** @@ -37,4 +40,24 @@ public function setOmitIncluded(bool $omitIncluded); * @return bool */ public function shouldOmitIncluded(): bool; + + /** + * @param \Swis\JsonApi\Client\Links|null $links + */ + public function setLinks(Links $links = null); + + /** + * @return \Swis\JsonApi\Client\Links|null + */ + public function getLinks(); + + /** + * @param \Swis\JsonApi\Client\Meta|null $meta + */ + public function setMeta(Meta $meta = null); + + /** + * @return \Swis\JsonApi\Client\Meta|null + */ + public function getMeta(); } diff --git a/src/InvalidResponseDocument.php b/src/InvalidResponseDocument.php index 92dd939..75b0f91 100644 --- a/src/InvalidResponseDocument.php +++ b/src/InvalidResponseDocument.php @@ -32,11 +32,11 @@ public function hasErrors(): bool } /** - * @return mixed + * @return \Swis\JsonApi\Client\Meta|null */ - public function getMeta(): array + public function getMeta() { - return []; + return null; } /** @@ -56,7 +56,7 @@ public function getIncluded(): Collection } /** - * @return mixed + * @return \Swis\JsonApi\Client\Jsonapi|null */ public function getJsonapi() { diff --git a/src/Item.php b/src/Item.php index f9fd432..357b7ae 100644 --- a/src/Item.php +++ b/src/Item.php @@ -11,13 +11,13 @@ use Swis\JsonApi\Client\Relations\HasOneRelation; use Swis\JsonApi\Client\Relations\MorphToManyRelation; use Swis\JsonApi\Client\Relations\MorphToRelation; +use Swis\JsonApi\Client\Traits\HasLinks; +use Swis\JsonApi\Client\Traits\HasMeta; +use Swis\JsonApi\Client\Traits\HasType; class Item extends Model implements ItemInterface { - /** - * @var string - */ - protected $type; + use HasLinks, HasMeta, HasType; /** * @var @@ -71,27 +71,17 @@ public function toJsonApiArray(): array $data['relationships'] = $relationships; } - return $data; - } - - /** - * @return string - */ - public function getType(): string - { - return $this->type; - } + $links = $this->getLinks(); + if ($links !== null) { + $data['links'] = $links->toArray(); + } - /** - * @param string $type - * - * @return static - */ - public function setType(string $type) - { - $this->type = $type; + $meta = $this->getMeta(); + if ($meta !== null) { + $data['meta'] = $meta->toArray(); + } - return $this; + return $data; } /** @@ -459,10 +449,12 @@ public function getAvailableRelations(): array * * @param string $relation * @param \Swis\JsonApi\Client\Interfaces\DataInterface $value + * @param \Swis\JsonApi\Client\Links|null $links + * @param \Swis\JsonApi\Client\Meta|null $meta * * @return static */ - public function setRelation(string $relation, DataInterface $value) + public function setRelation(string $relation, DataInterface $value, Links $links = null, Meta $meta = null) { if (method_exists($this, $relation)) { /** @var \Swis\JsonApi\Client\Interfaces\OneRelationInterface|\Swis\JsonApi\Client\Interfaces\ManyRelationInterface $relationObject */ @@ -474,6 +466,8 @@ public function setRelation(string $relation, DataInterface $value) } $relationObject->associate($value); + $relationObject->setLinks($links); + $relationObject->setMeta($meta); return $this; } diff --git a/src/JsonApi/ErrorsParser.php b/src/JsonApi/ErrorsParser.php index 1fc68ed..13eb990 100644 --- a/src/JsonApi/ErrorsParser.php +++ b/src/JsonApi/ErrorsParser.php @@ -4,15 +4,30 @@ use Art4\JsonApiClient\Error as JsonApiError; use Art4\JsonApiClient\ErrorCollection as JsonApiErrorCollection; +use Art4\JsonApiClient\ErrorLink as JsonApiErrorLink; use Art4\JsonApiClient\ErrorSource as JsonApiErrorSource; use Art4\JsonApiClient\Meta as JsonApiMeta; use Swis\JsonApi\Client\Errors\Error; use Swis\JsonApi\Client\Errors\ErrorCollection; -use Swis\JsonApi\Client\Errors\ErrorMeta; use Swis\JsonApi\Client\Errors\ErrorSource; +use Swis\JsonApi\Client\Links; +use Swis\JsonApi\Client\Meta; class ErrorsParser { + /** + * @var \Swis\JsonApi\Client\JsonApi\LinksParser + */ + private $linksParser; + + /** + * @param \Swis\JsonApi\Client\JsonApi\LinksParser $linksParser + */ + public function __construct(LinksParser $linksParser) + { + $this->linksParser = $linksParser; + } + /** * @param \Art4\JsonApiClient\ErrorCollection $errorCollection * @@ -21,14 +36,8 @@ class ErrorsParser public function parse(JsonApiErrorCollection $errorCollection) { $errors = new ErrorCollection(); - $errorCollectionArray = $errorCollection->asArray(false); - // Empty errors - if (empty($errorCollectionArray)) { - throw new \InvalidArgumentException('Error collection does not contain any errors.'); - } - - foreach ($errorCollectionArray as $error) { + foreach ($errorCollection->asArray(false) as $error) { $errors->push($this->buildError($error)); } @@ -44,15 +53,26 @@ private function buildError(JsonApiError $error): Error { return new Error( $error->has('id') ? $error->get('id') : null, + $error->has('links') ? $this->buildLinks($error->get('links')) : null, $error->has('status') ? $error->get('status') : null, $error->has('code') ? $error->get('code') : null, $error->has('title') ? $error->get('title') : null, $error->has('detail') ? $error->get('detail') : null, $error->has('source') ? $this->buildErrorSource($error->get('source')) : null, - $error->has('meta') ? $this->buildErrorMeta($error->get('meta')) : null + $error->has('meta') ? $this->buildMeta($error->get('meta')) : null ); } + /** + * @param \Art4\JsonApiClient\ErrorLink $errorLink + * + * @return \Swis\JsonApi\Client\Links + */ + private function buildLinks(JsonApiErrorLink $errorLink): Links + { + return $this->linksParser->parse($errorLink->asArray(false)); + } + /** * @param \Art4\JsonApiClient\ErrorSource $errorSource * @@ -69,10 +89,10 @@ private function buildErrorSource(JsonApiErrorSource $errorSource): ErrorSource /** * @param \Art4\JsonApiClient\Meta $meta * - * @return \Swis\JsonApi\Client\Errors\ErrorMeta + * @return \Swis\JsonApi\Client\Meta */ - private function buildErrorMeta(JsonApiMeta $meta): ErrorMeta + private function buildMeta(JsonApiMeta $meta): Meta { - return new ErrorMeta($meta->asArray(false)); + return new Meta($meta->asArray(true)); } } diff --git a/src/JsonApi/Hydrator.php b/src/JsonApi/Hydrator.php index ec40fe7..5999715 100644 --- a/src/JsonApi/Hydrator.php +++ b/src/JsonApi/Hydrator.php @@ -11,6 +11,7 @@ use Swis\JsonApi\Client\Interfaces\ItemInterface; use Swis\JsonApi\Client\Interfaces\TypeMapperInterface; use Swis\JsonApi\Client\Item; +use Swis\JsonApi\Client\Meta; class Hydrator { @@ -19,12 +20,19 @@ class Hydrator */ protected $typeMapper; + /** + * @var \Swis\JsonApi\Client\JsonApi\LinksParser + */ + protected $linksParser; + /** * @param \Swis\JsonApi\Client\Interfaces\TypeMapperInterface $typeMapper + * @param \Swis\JsonApi\Client\JsonApi\LinksParser $linksParser */ - public function __construct(TypeMapperInterface $typeMapper) + public function __construct(TypeMapperInterface $typeMapper, LinksParser $linksParser) { $this->typeMapper = $typeMapper; + $this->linksParser = $linksParser; } /** @@ -42,6 +50,14 @@ public function hydrateItem(ResourceItemInterface $jsonApiItem): ItemInterface $item->fill($jsonApiItem->get('attributes')->asArray(true)); } + if ($jsonApiItem->has('links')) { + $item->setLinks($this->linksParser->parse($jsonApiItem->get('links')->asArray(false))); + } + + if ($jsonApiItem->has('meta')) { + $item->setMeta(new Meta($jsonApiItem->get('meta')->asArray(true))); + } + return $item; } @@ -93,6 +109,16 @@ function (ResourceItemInterface $jsonApiItem) use ($keyedItems) { $data = $relationship->get('data'); $method = camel_case($name); + $links = null; + if ($relationship->has('links')) { + $links = $this->linksParser->parse($relationship->get('links')->asArray(false)); + } + + $meta = null; + if ($relationship->has('meta')) { + $meta = new Meta($relationship->get('meta')->asArray(true)); + } + if ($data instanceof ResourceIdentifierInterface) { $includedItem = $this->getItem($keyedItems, $data); @@ -100,11 +126,11 @@ function (ResourceItemInterface $jsonApiItem) use ($keyedItems) { continue; } - $item->setRelation($method, $includedItem); + $item->setRelation($method, $includedItem, $links, $meta); } elseif ($data instanceof ResourceIdentifierCollectionInterface) { $collection = $this->getCollection($keyedItems, $data); - $item->setRelation($method, $collection); + $item->setRelation($method, $collection, $links, $meta); } } } diff --git a/src/JsonApi/LinksParser.php b/src/JsonApi/LinksParser.php new file mode 100644 index 0000000..07148ca --- /dev/null +++ b/src/JsonApi/LinksParser.php @@ -0,0 +1,52 @@ +buildLink($link); + }, + $links + ) + ); + } + + /** + * @param \Art4\JsonApiClient\Link|string $link + * + * @return \Swis\JsonApi\Client\Link + */ + private function buildLink($link): Link + { + if (is_string($link)) { + return new Link($link); + } + + return new Link($link->get('href'), $link->has('meta') ? $this->buildMeta($link->get('meta')) : null); + } + + /** + * @param \Art4\JsonApiClient\Meta $meta + * + * @return \Swis\JsonApi\Client\Meta + */ + private function buildMeta(JsonApiMeta $meta): Meta + { + return new Meta($meta->asArray(true)); + } +} diff --git a/src/JsonApi/Parser.php b/src/JsonApi/Parser.php index 1d5a813..c5782b7 100644 --- a/src/JsonApi/Parser.php +++ b/src/JsonApi/Parser.php @@ -13,6 +13,8 @@ use Swis\JsonApi\Client\Interfaces\DocumentInterface; use Swis\JsonApi\Client\Interfaces\ParserInterface; use Swis\JsonApi\Client\ItemDocument; +use Swis\JsonApi\Client\Jsonapi; +use Swis\JsonApi\Client\Meta; class Parser implements ParserInterface { @@ -31,16 +33,27 @@ class Parser implements ParserInterface */ private $errorsParser; + /** + * @var \Swis\JsonApi\Client\JsonApi\LinksParser + */ + private $linksParser; + /** * @param \Art4\JsonApiClient\Utils\Manager $manager * @param \Swis\JsonApi\Client\JsonApi\Hydrator $hydrator * @param \Swis\JsonApi\Client\JsonApi\ErrorsParser $errorsParser + * @param \Swis\JsonApi\Client\JsonApi\LinksParser $linksParser */ - public function __construct(Art4JsonApiClientManager $manager, Hydrator $hydrator, ErrorsParser $errorsParser) - { + public function __construct( + Art4JsonApiClientManager $manager, + Hydrator $hydrator, + ErrorsParser $errorsParser, + LinksParser $linksParser + ) { $this->manager = $manager; $this->hydrator = $hydrator; $this->errorsParser = $errorsParser; + $this->linksParser = $linksParser; } /** @@ -71,6 +84,7 @@ public function deserialize(string $json): DocumentInterface $document->setLinks($this->parseLinks($jsonApiDocument)); $document->setErrors($this->parseErrors($jsonApiDocument)); $document->setMeta($this->parseMeta($jsonApiDocument)); + $document->setJsonapi($this->parseJsonapi($jsonApiDocument)); return $document; } @@ -177,15 +191,15 @@ private function getJsonApiDocumentIncluded(Art4JsonApiDocumentInterface $docume /** * @param \Art4\JsonApiClient\DocumentInterface $document * - * @return array + * @return \Swis\JsonApi\Client\Links|null */ - private function parseLinks(Art4JsonApiDocumentInterface $document): array + private function parseLinks(Art4JsonApiDocumentInterface $document) { if (!$document->has('links')) { - return []; + return null; } - return $document->get('links')->asArray(true); + return $this->linksParser->parse($document->get('links')->asArray(false)); } /** @@ -205,15 +219,34 @@ private function parseErrors(Art4JsonApiDocumentInterface $document): ErrorColle /** * @param \Art4\JsonApiClient\DocumentInterface $document * - * @return array + * @return \Swis\JsonApi\Client\Meta|null */ - private function parseMeta(Art4JsonApiDocumentInterface $document): array + private function parseMeta(Art4JsonApiDocumentInterface $document) { if (!$document->has('meta')) { - return []; + return null; + } + + return new Meta($document->get('meta')->asArray(true)); + } + + /** + * @param \Art4\JsonApiClient\DocumentInterface $document + * + * @return \Swis\JsonApi\Client\Jsonapi|null + */ + private function parseJsonapi(Art4JsonApiDocumentInterface $document) + { + if (!$document->has('jsonapi')) { + return null; } - return $document->get('meta')->asArray(true); + $jsonApi = $document->get('jsonapi'); + + return new Jsonapi( + $jsonApi->has('version') ? $jsonApi->get('version') : null, + $jsonApi->has('meta') ? new Meta($jsonApi->get('meta')->asArray(true)) : null + ); } /** diff --git a/src/Jsonapi.php b/src/Jsonapi.php new file mode 100644 index 0000000..a13540e --- /dev/null +++ b/src/Jsonapi.php @@ -0,0 +1,51 @@ +version = $version; + $this->meta = $meta; + } + + /** + * @return string|null + */ + public function getVersion() + { + return $this->version; + } + + /** + * @return array + */ + public function toArray(): array + { + $array = []; + + if ($this->getVersion() !== null) { + $array['version'] = $this->getVersion(); + } + + if ($this->getMeta() !== null) { + $array['meta'] = $this->getMeta()->toArray(); + } + + return $array; + } +} diff --git a/src/Link.php b/src/Link.php new file mode 100644 index 0000000..e90cceb --- /dev/null +++ b/src/Link.php @@ -0,0 +1,51 @@ +href = $href; + $this->meta = $meta; + } + + /** + * @return string + */ + public function getHref(): string + { + return $this->href; + } + + /** + * {@inheritdoc} + * + * @return array + */ + public function toArray() + { + $array = [ + 'href' => $this->getHref(), + ]; + + if ($this->getMeta() !== null) { + $array['meta'] = $this->getMeta()->toArray(); + } + + return $array; + } +} diff --git a/src/Links.php b/src/Links.php new file mode 100644 index 0000000..d4c1a66 --- /dev/null +++ b/src/Links.php @@ -0,0 +1,143 @@ +links = $links; + } + + /** + * @param $key + * + * @return bool + */ + public function __isset($key) + { + return $this->offsetExists($key); + } + + /** + * @param $key + * + * @return mixed + */ + public function __get($key) + { + return $this->offsetGet($key); + } + + /** + * @param $key + */ + public function __unset($key) + { + $this->offsetUnset($key); + } + + /** + * @param $key + * @param $value + */ + public function __set($key, $value) + { + $this->offsetSet($key, $value); + } + + /** + * {@inheritdoc} + * + * @param mixed $offset + * + * @return bool + */ + public function offsetExists($offset) + { + return isset($this->links[$offset]); + } + + /** + * {@inheritdoc} + * + * @param mixed $offset + * + * @return mixed + */ + public function offsetGet($offset) + { + return $this->links[$offset] ?? null; + } + + /** + * {@inheritdoc} + * + * @param mixed $offset + * @param mixed $value + */ + public function offsetSet($offset, $value) + { + $this->links[$offset] = $value; + } + + /** + * {@inheritdoc} + * + * @param mixed $offset + */ + public function offsetUnset($offset) + { + unset($this->links[$offset]); + } + + /** + * {@inheritdoc} + * + * @return array + */ + public function toArray() + { + return array_map( + function (Link $link) { + return $link->toArray(); + }, + $this->links + ); + } + + /** + * {@inheritdoc} + * + * @param int $options + * + * @return false|string + */ + public function toJson($options = 0) + { + return json_encode($this->jsonSerialize(), $options); + } + + /** + * {@inheritdoc} + * + * @return array|mixed + */ + public function jsonSerialize() + { + return $this->toArray(); + } +} diff --git a/src/Meta.php b/src/Meta.php new file mode 100644 index 0000000..2bfcfbc --- /dev/null +++ b/src/Meta.php @@ -0,0 +1,138 @@ +data = $data; + } + + /** + * @param $key + * + * @return bool + */ + public function __isset($key) + { + return $this->offsetExists($key); + } + + /** + * @param $key + * + * @return mixed + */ + public function __get($key) + { + return $this->offsetGet($key); + } + + /** + * @param $key + */ + public function __unset($key) + { + $this->offsetUnset($key); + } + + /** + * @param $key + * @param $value + */ + public function __set($key, $value) + { + $this->offsetSet($key, $value); + } + + /** + * {@inheritdoc} + * + * @param mixed $offset + * + * @return bool + */ + public function offsetExists($offset) + { + return isset($this->data[$offset]); + } + + /** + * {@inheritdoc} + * + * @param mixed $offset + * + * @return mixed + */ + public function offsetGet($offset) + { + return $this->data[$offset] ?? null; + } + + /** + * {@inheritdoc} + * + * @param mixed $offset + * @param mixed $value + */ + public function offsetSet($offset, $value) + { + $this->data[$offset] = $value; + } + + /** + * {@inheritdoc} + * + * @param mixed $offset + */ + public function offsetUnset($offset) + { + unset($this->data[$offset]); + } + + /** + * {@inheritdoc} + * + * @return array + */ + public function toArray() + { + return $this->data; + } + + /** + * {@inheritdoc} + * + * @param int $options + * + * @return false|string + */ + public function toJson($options = 0) + { + return json_encode($this->jsonSerialize(), $options); + } + + /** + * {@inheritdoc} + * + * @return array|mixed + */ + public function jsonSerialize() + { + return $this->toArray(); + } +} diff --git a/src/Providers/ServiceProvider.php b/src/Providers/ServiceProvider.php index 9e09f38..c6acc61 100644 --- a/src/Providers/ServiceProvider.php +++ b/src/Providers/ServiceProvider.php @@ -17,6 +17,7 @@ use Swis\JsonApi\Client\Interfaces\TypeMapperInterface; use Swis\JsonApi\Client\JsonApi\ErrorsParser; use Swis\JsonApi\Client\JsonApi\Hydrator; +use Swis\JsonApi\Client\JsonApi\LinksParser; use Swis\JsonApi\Client\JsonApi\Parser; use Swis\JsonApi\Client\TypeMapper; @@ -58,8 +59,9 @@ protected function registerParser() function (Application $app) { return new Parser( new JsonApiClientManger(), - new Hydrator($app->make(TypeMapperInterface::class)), - new ErrorsParser() + new Hydrator($app->make(TypeMapperInterface::class), new LinksParser()), + new ErrorsParser(new LinksParser()), + new LinksParser() ); } ); diff --git a/src/Relations/AbstractRelation.php b/src/Relations/AbstractRelation.php index fd6a1f1..72bb91a 100644 --- a/src/Relations/AbstractRelation.php +++ b/src/Relations/AbstractRelation.php @@ -2,8 +2,13 @@ namespace Swis\JsonApi\Client\Relations; +use Swis\JsonApi\Client\Traits\HasLinks; +use Swis\JsonApi\Client\Traits\HasMeta; + abstract class AbstractRelation { + use HasLinks, HasMeta; + /** * @var \Swis\JsonApi\Client\Interfaces\DataInterface|null */ diff --git a/src/Relations/HasManyRelation.php b/src/Relations/HasManyRelation.php index 920a509..12f708c 100644 --- a/src/Relations/HasManyRelation.php +++ b/src/Relations/HasManyRelation.php @@ -3,7 +3,7 @@ namespace Swis\JsonApi\Client\Relations; use Swis\JsonApi\Client\Interfaces\TypedRelationInterface; -use Swis\JsonApi\Client\Relations\Traits\HasType; +use Swis\JsonApi\Client\Traits\HasType; class HasManyRelation extends AbstractManyRelation implements TypedRelationInterface { diff --git a/src/Relations/HasOneRelation.php b/src/Relations/HasOneRelation.php index c26bbc2..e8fa570 100644 --- a/src/Relations/HasOneRelation.php +++ b/src/Relations/HasOneRelation.php @@ -3,7 +3,7 @@ namespace Swis\JsonApi\Client\Relations; use Swis\JsonApi\Client\Interfaces\TypedRelationInterface; -use Swis\JsonApi\Client\Relations\Traits\HasType; +use Swis\JsonApi\Client\Traits\HasType; class HasOneRelation extends AbstractOneRelation implements TypedRelationInterface { diff --git a/src/Traits/HasLinks.php b/src/Traits/HasLinks.php new file mode 100644 index 0000000..8a66a96 --- /dev/null +++ b/src/Traits/HasLinks.php @@ -0,0 +1,33 @@ +links = $links; + + return $this; + } + + /** + * @return \Swis\JsonApi\Client\Links|null + */ + public function getLinks() + { + return $this->links; + } +} diff --git a/src/Traits/HasMeta.php b/src/Traits/HasMeta.php new file mode 100644 index 0000000..cfd4fcd --- /dev/null +++ b/src/Traits/HasMeta.php @@ -0,0 +1,33 @@ +meta = $meta; + + return $this; + } + + /** + * @return \Swis\JsonApi\Client\Meta|null + */ + public function getMeta() + { + return $this->meta; + } +} diff --git a/src/Relations/Traits/HasType.php b/src/Traits/HasType.php similarity index 88% rename from src/Relations/Traits/HasType.php rename to src/Traits/HasType.php index 5552e0e..5c09806 100644 --- a/src/Relations/Traits/HasType.php +++ b/src/Traits/HasType.php @@ -1,6 +1,6 @@ assertEquals([], $document->toArray()); $document->setLinks( - [ - 'self' => 'http://example.com/articles', - 'next' => 'http://example.com/articles?page[offset]=2', - 'last' => 'http://example.com/articles?page[offset]=10', - ] + new Links( + [ + 'self' => new Link( + 'http://example.com/articles', + new Meta( + [ + 'copyright' => 'Copyright 2015 Example Corp.', + ] + ) + ), + 'next' => new Link('http://example.com/articles?page[offset]=2'), + 'last' => new Link('http://example.com/articles?page[offset]=10'), + ] + ) ); $document->setData( (new Item(['title' => 'JSON:API paints my bikeshed!'])) @@ -60,7 +73,13 @@ public function it_returns_only_filled_properties_in_toArray() ] ) ); - $document->setMeta(['copyright' => 'Copyright 2015 Example Corp.']); + $document->setMeta( + new Meta( + [ + 'copyright' => 'Copyright 2015 Example Corp.', + ] + ) + ); $document->setErrors( new ErrorCollection( [ @@ -68,14 +87,32 @@ public function it_returns_only_filled_properties_in_toArray() ] ) ); - $document->setJsonapi(['version' => '1.0']); + $document->setJsonapi( + new Jsonapi( + '1.0', + new Meta( + [ + 'copyright' => 'Copyright 2015 Example Corp.', + ] + ) + ) + ); $this->assertEquals( [ - 'links' => [ - 'self' => 'http://example.com/articles', - 'next' => 'http://example.com/articles?page[offset]=2', - 'last' => 'http://example.com/articles?page[offset]=10', + 'links' => [ + 'self' => [ + 'href' => 'http://example.com/articles', + 'meta' => [ + 'copyright' => 'Copyright 2015 Example Corp.', + ], + ], + 'next' => [ + 'href' => 'http://example.com/articles?page[offset]=2', + ], + 'last' => [ + 'href' => 'http://example.com/articles?page[offset]=10', + ], ], 'data' => [ 'type' => 'articles', @@ -105,6 +142,9 @@ public function it_returns_only_filled_properties_in_toArray() ], 'jsonapi' => [ 'version' => '1.0', + 'meta' => [ + 'copyright' => 'Copyright 2015 Example Corp.', + ], ], ], $document->toArray() diff --git a/tests/ItemTest.php b/tests/ItemTest.php index 7cb3cc6..abca9d6 100644 --- a/tests/ItemTest.php +++ b/tests/ItemTest.php @@ -4,6 +4,9 @@ use Swis\JsonApi\Client\Collection; use Swis\JsonApi\Client\Item; +use Swis\JsonApi\Client\Link; +use Swis\JsonApi\Client\Links; +use Swis\JsonApi\Client\Meta; use Swis\JsonApi\Client\Tests\Mocks\Items\RelatedItem; use Swis\JsonApi\Client\Tests\Mocks\Items\WithGetMutatorItem; use Swis\JsonApi\Client\Tests\Mocks\Items\WithHiddenItem; @@ -372,4 +375,66 @@ public function is_adds_empty_morphtomany_relation_in_to_json_api_array() $item->toJsonApiArray() ); } + + /** + * @test + */ + public function is_adds_links_in_to_json_api_array() + { + $item = new Item(); + $item->setType('testType'); + $item->setId(1); + $item->setLinks( + new Links( + [ + 'self' => new Link( + 'http://example.com/testType/1', + new Meta(['foo' => 'bar']) + ), + 'other' => new Link('http://example.com/testType/1/other'), + ] + ) + ); + + $this->assertEquals( + [ + 'type' => 'testType', + 'id' => 1, + 'links' => [ + 'self' => [ + 'href' => 'http://example.com/testType/1', + 'meta' => [ + 'foo' => 'bar', + ], + ], + 'other' => [ + 'href' => 'http://example.com/testType/1/other', + ], + ], + ], + $item->toJsonApiArray() + ); + } + + /** + * @test + */ + public function is_adds_meta_in_to_json_api_array() + { + $item = new Item(); + $item->setType('testType'); + $item->setId(1); + $item->setMeta(new Meta(['foo' => 'bar'])); + + $this->assertEquals( + [ + 'type' => 'testType', + 'id' => 1, + 'meta' => [ + 'foo' => 'bar', + ], + ], + $item->toJsonApiArray() + ); + } } diff --git a/tests/JsonApi/ErrorsParserTest.php b/tests/JsonApi/ErrorsParserTest.php index 6a1f33d..451f3d5 100644 --- a/tests/JsonApi/ErrorsParserTest.php +++ b/tests/JsonApi/ErrorsParserTest.php @@ -7,22 +7,20 @@ use Swis\JsonApi\Client\Errors\ErrorCollection; use Swis\JsonApi\Client\Errors\ErrorSource; use Swis\JsonApi\Client\JsonApi\ErrorsParser; +use Swis\JsonApi\Client\JsonApi\LinksParser; +use Swis\JsonApi\Client\Links; +use Swis\JsonApi\Client\Meta; use Swis\JsonApi\Client\Tests\AbstractTest; class ErrorsParserTest extends AbstractTest { - /** @var \Swis\JsonApi\Client\JsonApi\ErrorsParser */ - public static $parser; - - public static function setUpBeforeClass() - { - self::$parser = new ErrorsParser(); - } - - /** @test */ - public function it_converts_jsonapierrorcollection_to_errorcollection() + /** + * @test + */ + public function it_converts_jsonapilinks_to_links() { - $errorCollection = self::$parser->parse($this->getValidJsonApiErrorCollection()); + $parser = new ErrorsParser(new LinksParser()); + $errorCollection = $parser->parse($this->getJsonApiErrorCollection()); $this->assertInstanceOf(ErrorCollection::class, $errorCollection); $this->assertEquals(2, $errorCollection->count()); @@ -30,14 +28,18 @@ public function it_converts_jsonapierrorcollection_to_errorcollection() $errorCollection->each( function (Error $error) { $this->assertInstanceOf(Error::class, $error); + $this->assertInstanceOf(Links::class, $error->getLinks()); + $this->assertInstanceOf(Meta::class, $error->getMeta()); $this->assertInstanceOf(ErrorSource::class, $error->getSource()); + $this->assertEquals('http://example.com/docs/error/json_client_content_id_in_object_not_equal_to_id_parameter', $error->getLinks()['about']->getHref()); $this->assertEquals('400', $error->getStatus()); $this->assertEquals('json_client_content_id_in_object_not_equal_to_id_parameter', $error->getCode()); $this->assertEquals('I refuse to save a sport with this id. ✟', $error->getTitle()); $this->assertEquals("id is '666', id is '666'", $error->getDetail()); $this->assertEquals('', $error->getSource()->getPointer()); $this->assertEquals('666', $error->getSource()->getParameter()); + $this->assertEquals('Copyright 2015 Example Corp.', $error->getMeta()->copyright); } ); @@ -48,60 +50,50 @@ function (Error $error) { /** * @return \Art4\JsonApiClient\ErrorCollection */ - protected function getValidJsonApiErrorCollection() + protected function getJsonApiErrorCollection() { $errors = [ 'errors' => [ - [ - 'id' => '1', - 'status' => '400', - 'code' => 'json_client_content_id_in_object_not_equal_to_id_parameter', - 'title' => 'I refuse to save a sport with this id. ✟', - 'detail' => "id is '666', id is '666'", - 'source' => [ - 'pointer' => '', - 'parameter' => '666', - ], + [ + 'id' => '1', + 'links' => [ + 'about' => 'http://example.com/docs/error/json_client_content_id_in_object_not_equal_to_id_parameter', ], - [ - 'id' => '2', - 'status' => '400', - 'code' => 'json_client_content_id_in_object_not_equal_to_id_parameter', - 'title' => 'I refuse to save a sport with this id. ✟', - 'detail' => "id is '666', id is '666'", - 'source' => [ - 'pointer' => '', - 'parameter' => '666', - ], + 'status' => '400', + 'code' => 'json_client_content_id_in_object_not_equal_to_id_parameter', + 'title' => 'I refuse to save a sport with this id. ✟', + 'detail' => "id is '666', id is '666'", + 'source' => [ + 'pointer' => '', + 'parameter' => '666', + ], + 'meta' => [ + 'copyright' => 'Copyright 2015 Example Corp.', ], ], - ]; - - $manager = new Manager(); - $jsonApiItem = $manager->parse(json_encode($errors)); - - return $jsonApiItem->get('errors'); - } - - /** - * @return \Art4\JsonApiClient\ErrorCollection - */ - protected function getInValidJsonApiErrorCollection() - { - $errors = [ - 'errors' => [ - [ - 'id' => '1', - 'status' => '400', - 'code' => 'json_client_content_id_in_object_not_equal_to_id_parameter', - 'title' => 'I refuse to save a sport with this id. ✟', - 'detail' => "id is '666', id is '666'", - 'source' => [ - 'pointer' => '', - 'parameter' => '666', + [ + 'id' => '2', + 'links' => [ + 'about' => [ + 'href' => 'http://example.com/docs/error/json_client_content_id_in_object_not_equal_to_id_parameter', + 'meta' => [ + 'foo' => 'bar', ], + ], + ], + 'status' => '400', + 'code' => 'json_client_content_id_in_object_not_equal_to_id_parameter', + 'title' => 'I refuse to save a sport with this id. ✟', + 'detail' => "id is '666', id is '666'", + 'source' => [ + 'pointer' => '', + 'parameter' => '666', + ], + 'meta' => [ + 'copyright' => 'Copyright 2015 Example Corp.', ], ], + ], ]; $manager = new Manager(); diff --git a/tests/JsonApi/HydratorTest.php b/tests/JsonApi/HydratorTest.php index 16a0e25..375db69 100644 --- a/tests/JsonApi/HydratorTest.php +++ b/tests/JsonApi/HydratorTest.php @@ -7,6 +7,10 @@ use Swis\JsonApi\Client\Collection; use Swis\JsonApi\Client\Item; use Swis\JsonApi\Client\JsonApi\Hydrator; +use Swis\JsonApi\Client\JsonApi\LinksParser; +use Swis\JsonApi\Client\Link; +use Swis\JsonApi\Client\Links; +use Swis\JsonApi\Client\Meta; use Swis\JsonApi\Client\Relations\HasOneRelation; use Swis\JsonApi\Client\Relations\MorphToManyRelation; use Swis\JsonApi\Client\Relations\MorphToRelation; @@ -43,7 +47,7 @@ public function it_hydrates_the_correct_item_from_mapping() */ protected function getHydrator(): Hydrator { - return new Hydrator($this->getTypeMapperMock()); + return new Hydrator($this->getTypeMapperMock(), new LinksParser()); } /** @@ -86,12 +90,21 @@ protected function getJsonApiItemMock($type, $id) 'type' => 'child', 'id' => '2', ], + 'links' => [ + 'self' => 'http://example.com/'.$type.'/'.$id.'/relationships/child', + ], + 'meta' => [ + 'foo' => 'bar', + ], ], 'morph' => [ 'data' => [ 'type' => 'child', 'id' => '3', ], + 'meta' => [ + 'foo' => 'bar', + ], ], 'morphmany' => [ 'data' => [ @@ -108,8 +121,17 @@ protected function getJsonApiItemMock($type, $id) 'id' => '6', ], ], + 'links' => [ + 'self' => 'http://example.com/'.$type.'/'.$id.'/relationships/morphmany', + ], ], ], + 'links' => [ + 'self' => 'http://example.com/master/1', + ], + 'meta' => [ + 'foo' => 'bar', + ], ], 'included' => [ [ @@ -209,7 +231,7 @@ public function it_hydrates_relationships() $typeMapper = new TypeMapper(); $typeMapper->setMapping('child', ChildItem::class); $typeMapper->setMapping('master', MasterItem::class); - $hydrator = new Hydrator($typeMapper); + $hydrator = new Hydrator($typeMapper, new LinksParser()); $childJsonApiItem = $this->getJsonApiChildItemMock(2, 'master', 1); $childItem = $hydrator->hydrateItem($childJsonApiItem); @@ -226,11 +248,15 @@ public function it_hydrates_relationships() static::assertInstanceOf(MasterItem::class, $masterItem); static::assertInstanceOf(HasOneRelation::class, $masterItem->getRelationship('child')); + static::assertInstanceOf(Links::class, $masterItem->getRelationship('child')->getLinks()); + static::assertInstanceOf(Meta::class, $masterItem->getRelationship('child')->getMeta()); static::assertSame($childItem, $masterItem->getRelationship('child')->getIncluded()); static::assertEquals('child', $masterItem->getRelationship('child')->getIncluded()->getType()); static::assertEquals(2, $masterItem->getRelationship('child')->getIncluded()->getId()); static::assertSame($masterItem, $masterItem->getRelationship('child')->getIncluded()->getRelationship('parent')->getIncluded()); + static::assertSame('http://example.com/master/1/relationships/child', $masterItem->getRelationship('child')->getLinks()->self->getHref()); + static::assertSame('bar', $masterItem->getRelationship('child')->getMeta()->foo); } /** @@ -242,7 +268,7 @@ public function it_does_not_hydrate_relationships_without_data() /** @var \Swis\JsonApi\Client\Interfaces\TypeMapperInterface $typeMapper */ $typeMapper = new TypeMapper(); $typeMapper->setMapping('master', MasterItem::class); - $hydrator = new Hydrator($typeMapper); + $hydrator = new Hydrator($typeMapper, new LinksParser()); $data = [ 'data' => [ @@ -345,7 +371,7 @@ public function it_hydrates_a_morph_to_relation() $typeMapper = new TypeMapper(); $typeMapper->setMapping('child', ChildItem::class); $typeMapper->setMapping('master', MasterItem::class); - $hydrator = new Hydrator($typeMapper); + $hydrator = new Hydrator($typeMapper, new LinksParser()); $childJsonApiItem = $this->getJsonApiChildItemMock(3); $childItem = $hydrator->hydrateItem($childJsonApiItem); @@ -376,7 +402,7 @@ public function it_hydrates_a_morph_to_many_relation() $typeMapper = new TypeMapper(); $typeMapper->setMapping('child', ChildItem::class); $typeMapper->setMapping('master', MasterItem::class); - $hydrator = new Hydrator($typeMapper); + $hydrator = new Hydrator($typeMapper, new LinksParser()); $childJsonApiItem = $this->getJsonApiChildItemMock(4); $childItem = $hydrator->hydrateItem($childJsonApiItem); @@ -406,7 +432,7 @@ public function it_hydrates_an_unknown_relation_as_morph_to() /** @var \Swis\JsonApi\Client\Interfaces\TypeMapperInterface $typeMapper */ $typeMapper = new TypeMapper(); $typeMapper->setMapping('item-without-relationships', WithoutRelationshipsItem::class); - $hydrator = new Hydrator($typeMapper); + $hydrator = new Hydrator($typeMapper, new LinksParser()); $childJsonApiItem = $this->getJsonApiChildItemMock(3); $childItem = $hydrator->hydrateItem($childJsonApiItem); @@ -436,7 +462,7 @@ public function it_hydrates_an_unknown_relation_as_morph_to_many() /** @var \Swis\JsonApi\Client\Interfaces\TypeMapperInterface $typeMapper */ $typeMapper = new TypeMapper(); $typeMapper->setMapping('item-without-relationships', WithoutRelationshipsItem::class); - $hydrator = new Hydrator($typeMapper); + $hydrator = new Hydrator($typeMapper, new LinksParser()); $childJsonApiItem = $this->getJsonApiChildItemMock(4); $childItem = $hydrator->hydrateItem($childJsonApiItem); @@ -456,4 +482,34 @@ public function it_hydrates_an_unknown_relation_as_morph_to_many() static::assertEquals(4, $masterItem->morphmany[0]->getId()); } + + /** + * @test + */ + public function it_hydrates_links() + { + $hydrator = $this->getHydrator(); + + $jsonApiItem = $this->getJsonApiItemMock('master', 1); + $item = $hydrator->hydrateItem($jsonApiItem); + + static::assertInstanceOf(Links::class, $item->getLinks()); + + static::assertEquals(new Links(['self' => new Link('http://example.com/master/1')]), $item->getLinks()); + } + + /** + * @test + */ + public function it_hydrates_meta() + { + $hydrator = $this->getHydrator(); + + $jsonApiItem = $this->getJsonApiItemMock('master', 1); + $item = $hydrator->hydrateItem($jsonApiItem); + + static::assertInstanceOf(Meta::class, $item->getMeta()); + + static::assertEquals(new Meta(['foo' => 'bar']), $item->getMeta()); + } } diff --git a/tests/JsonApi/LinksParserTest.php b/tests/JsonApi/LinksParserTest.php new file mode 100644 index 0000000..9fc7795 --- /dev/null +++ b/tests/JsonApi/LinksParserTest.php @@ -0,0 +1,73 @@ +parse($this->getJsonApiLinks()->asArray(false)); + + $this->assertInstanceOf(Links::class, $links); + $this->assertCount(3, $links->toArray()); + + /** @var \Swis\JsonApi\Client\Link $link */ + $link = $links->self; + $this->assertInstanceOf(Link::class, $link); + $this->assertEquals('http://example.com/articles', $link->getHref()); + $this->assertInstanceOf(Meta::class, $link->getMeta()); + $this->assertEquals(new Meta(['copyright' => 'Copyright 2015 Example Corp.']), $link->getMeta()); + + /** @var \Swis\JsonApi\Client\Link $link */ + $link = $links->next; + $this->assertInstanceOf(Link::class, $link); + $this->assertEquals('http://example.com/articles?page[offset]=2', $link->getHref()); + $this->assertNull($link->getMeta()); + + /** @var \Swis\JsonApi\Client\Link $link */ + $link = $links->last; + $this->assertInstanceOf(Link::class, $link); + $this->assertEquals('http://example.com/articles?page[offset]=10', $link->getHref()); + $this->assertNull($link->getMeta()); + } + + /** + * @return \Art4\JsonApiClient\ErrorCollection + */ + protected function getJsonApiLinks() + { + $links = [ + 'links' => [ + 'self' => [ + 'href' => 'http://example.com/articles', + 'meta' => [ + 'copyright' => 'Copyright 2015 Example Corp.', + ], + ], + 'next' => [ + 'href' => 'http://example.com/articles?page[offset]=2', + ], + 'last' => [ + 'href' => 'http://example.com/articles?page[offset]=10', + ], + ], + 'data' => [], + ]; + + $manager = new Manager(); + $jsonApiItem = $manager->parse(json_encode($links)); + + return $jsonApiItem->get('links'); + } +} diff --git a/tests/LinksTest.php b/tests/LinksTest.php new file mode 100644 index 0000000..4643cc2 --- /dev/null +++ b/tests/LinksTest.php @@ -0,0 +1,127 @@ + $link]); + + $this->assertEquals($link, $links->self); + $this->assertNull($links->related); + } + + public function testOffsetGet() + { + $link = new Link('http://example.com/self'); + $links = new Links(['self' => $link]); + + $this->assertEquals($link, $links['self']); + $this->assertNull($links['related']); + } + + public function test__isset() + { + $link = new Link('http://example.com/self'); + $links = new Links(['self' => $link]); + + $this->assertTrue(isset($links->self)); + $this->assertFalse(isset($links->related)); + } + + public function testOffsetExists() + { + $link = new Link('http://example.com/self'); + $links = new Links(['self' => $link]); + + $this->assertTrue(isset($links['self'])); + $this->assertFalse(isset($links['related'])); + } + + public function test__set() + { + $link = new Link('http://example.com/self'); + $links = new Links([]); + + $links->self = $link; + + $this->assertEquals($link, $links->self); + } + + public function testOffsetSet() + { + $link = new Link('http://example.com/self'); + $links = new Links([]); + + $links['self'] = $link; + + $this->assertEquals($link, $links['self']); + } + + public function test__unset() + { + $link = new Link('http://example.com/self'); + $links = new Links(['self' => $link]); + + unset($links->self); + + $this->assertNull($links->self); + } + + public function testOffsetUnset() + { + $link = new Link('http://example.com/self'); + $links = new Links(['self' => $link]); + + unset($links['self']); + + $this->assertNull($links['self']); + } + + public function testToArray() + { + $link = new Link('http://example.com/self'); + $links = new Links(['self' => $link]); + + $this->assertEquals( + [ + 'self' => [ + 'href' => 'http://example.com/self', + ], + ], + $links->toArray() + ); + } + + public function testToJson() + { + $link = new Link('http://example.com/self'); + $links = new Links(['self' => $link]); + + $this->assertEquals( + '{"self":{"href":"http:\/\/example.com\/self"}}', + $links->toJson() + ); + } + + public function testJsonSerialize() + { + $link = new Link('http://example.com/self'); + $links = new Links(['self' => $link]); + + $this->assertEquals( + [ + 'self' => [ + 'href' => 'http://example.com/self', + ], + ], + $links->toArray() + ); + } +} diff --git a/tests/MetaTest.php b/tests/MetaTest.php new file mode 100644 index 0000000..aa15073 --- /dev/null +++ b/tests/MetaTest.php @@ -0,0 +1,107 @@ + 'bar']); + + $this->assertEquals('bar', $meta->foo); + $this->assertNull($meta->other); + } + + public function testOffsetGet() + { + $meta = new Meta(['foo' => 'bar']); + + $this->assertEquals('bar', $meta['foo']); + $this->assertNull($meta['other']); + } + + public function test__isset() + { + $meta = new Meta(['foo' => 'bar']); + + $this->assertTrue(isset($meta->foo)); + $this->assertFalse(isset($meta->other)); + } + + public function testOffsetExists() + { + $meta = new Meta(['foo' => 'bar']); + + $this->assertTrue(isset($meta['foo'])); + $this->assertFalse(isset($meta['other'])); + } + + public function test__set() + { + $meta = new Meta([]); + + $meta->foo = 'bar'; + + $this->assertEquals('bar', $meta->foo); + } + + public function testOffsetSet() + { + $meta = new Meta([]); + + $meta['foo'] = 'bar'; + + $this->assertEquals('bar', $meta['foo']); + } + + public function test__unset() + { + $meta = new Meta(['foo' => 'bar']); + + unset($meta->foo); + + $this->assertNull($meta->foo); + } + + public function testOffsetUnset() + { + $meta = new Meta(['foo' => 'bar']); + + unset($meta['foo']); + + $this->assertNull($meta['foo']); + } + + public function testToArray() + { + $meta = new Meta(['foo' => 'bar']); + + $this->assertEquals( + ['foo' => 'bar'], + $meta->toArray() + ); + } + + public function testToJson() + { + $meta = new Meta(['foo' => 'bar']); + + $this->assertEquals( + '{"foo":"bar"}', + $meta->toJson() + ); + } + + public function testJsonSerialize() + { + $meta = new Meta(['foo' => 'bar']); + + $this->assertEquals( + ['foo' => 'bar'], + $meta->toArray() + ); + } +}