From aa8e3ccb75823f7cfff0cba7f54ed9d4642a14bc Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Thu, 4 Jan 2018 15:21:19 -0800 Subject: [PATCH 01/37] WIP --- composer.json | 2 +- src/DataDocument.php | 16 + src/Document.php | 156 ---------- src/Document/Container.php | 33 -- src/Document/DataDocumentMember.php | 7 + src/Document/DocumentMember.php | 7 + src/Document/Error.php | 78 +---- src/Document/Error/About.php | 16 + src/Document/Error/Code.php | 27 ++ src/Document/Error/Detail.php | 27 ++ src/Document/Error/ErrorMember.php | 8 + src/Document/Error/Id.php | 24 ++ src/Document/Error/Source.php | 25 ++ src/Document/Error/Source/Parameter.php | 22 ++ src/Document/Error/Source/Pointer.php | 22 ++ src/Document/Error/Source/SourceMember.php | 9 + src/Document/Error/Status.php | 24 ++ src/Document/Error/Title.php | 28 ++ src/Document/JsonApi.php | 22 ++ src/Document/JsonApi/JsonApiMember.php | 11 + src/Document/JsonApi/Version.php | 19 ++ src/Document/JsonSerializableValue.php | 19 ++ src/Document/Link/AboutLink.php | 23 ++ src/Document/Link/FirstLink.php | 17 + src/Document/Link/Href.php | 22 ++ src/Document/Link/Link.php | 7 + src/Document/Link/LinkObject.php | 15 + src/Document/Link/LinkObjectMember.php | 11 + src/Document/Link/LinkSet.php | 23 ++ src/Document/Link/MemberLink.php | 15 + src/Document/Link/PaginationLink.php | 14 + src/Document/Link/RelatedLink.php | 17 + src/Document/Link/SelfLink.php | 17 + src/Document/Link/Url.php | 14 + src/Document/Links.php | 14 + src/Document/LinksMember.php | 8 + src/Document/LinksTrait.php | 18 -- src/Document/Member.php | 9 + src/Document/MemberCollection.php | 16 + src/Document/Meta.php | 29 ++ src/Document/MetaTrait.php | 14 - .../PrimaryData/MultiIdentifierData.php | 32 -- .../PrimaryData/MultiResourceData.php | 31 -- src/Document/PrimaryData/NullData.php | 4 +- src/Document/PrimaryData/PrimaryData.php | 24 ++ .../PrimaryData/PrimaryDataInterface.php | 11 - .../PrimaryData/SingleIdentifierData.php | 27 -- .../PrimaryData/SingleResourceData.php | 26 -- .../Resource/Linkage/LinkageInterface.php | 13 - .../Resource/Linkage/MultiLinkage.php | 32 -- src/Document/Resource/Linkage/NullLinkage.php | 19 -- .../Resource/Linkage/SingleLinkage.php | 27 -- src/Document/Resource/Relationship.php | 66 ---- src/Document/Resource/ResourceIdentifier.php | 59 ---- src/Document/Resource/ResourceObject.php | 96 +----- src/MetaDocument.php | 16 + src/NullDocument.php | 15 + src/functions.php | 18 +- test/BaseTestCase.php | 2 +- test/Document/CompoundDocumentTest.php | 294 ------------------ test/Document/DocumentTest.php | 186 ----------- test/Document/ErrorTest.php | 51 --- .../Resource/Relationship/LinkageTest.php | 119 ------- .../Relationship/RelationshipTest.php | 95 ------ test/Document/Resource/ResourceFieldsTest.php | 76 ----- .../Resource/ResourceIdentifierTest.php | 44 --- test/Document/Resource/ResourceObjectTest.php | 110 ------- test/DocumentTest.php | 89 ++++++ test/ErrorTest.php | 65 ++++ test/IntegrationTest.php | 58 ---- test/JsonApiTest.php | 26 ++ test/MemberNamesTest.php | 117 ------- 72 files changed, 852 insertions(+), 1901 deletions(-) create mode 100644 src/DataDocument.php delete mode 100644 src/Document.php delete mode 100644 src/Document/Container.php create mode 100644 src/Document/DataDocumentMember.php create mode 100644 src/Document/DocumentMember.php create mode 100644 src/Document/Error/About.php create mode 100644 src/Document/Error/Code.php create mode 100644 src/Document/Error/Detail.php create mode 100644 src/Document/Error/ErrorMember.php create mode 100644 src/Document/Error/Id.php create mode 100644 src/Document/Error/Source.php create mode 100644 src/Document/Error/Source/Parameter.php create mode 100644 src/Document/Error/Source/Pointer.php create mode 100644 src/Document/Error/Source/SourceMember.php create mode 100644 src/Document/Error/Status.php create mode 100644 src/Document/Error/Title.php create mode 100644 src/Document/JsonApi.php create mode 100644 src/Document/JsonApi/JsonApiMember.php create mode 100644 src/Document/JsonApi/Version.php create mode 100644 src/Document/JsonSerializableValue.php create mode 100644 src/Document/Link/AboutLink.php create mode 100644 src/Document/Link/FirstLink.php create mode 100644 src/Document/Link/Href.php create mode 100644 src/Document/Link/Link.php create mode 100644 src/Document/Link/LinkObject.php create mode 100644 src/Document/Link/LinkObjectMember.php create mode 100644 src/Document/Link/LinkSet.php create mode 100644 src/Document/Link/MemberLink.php create mode 100644 src/Document/Link/PaginationLink.php create mode 100644 src/Document/Link/RelatedLink.php create mode 100644 src/Document/Link/SelfLink.php create mode 100644 src/Document/Link/Url.php create mode 100644 src/Document/Links.php create mode 100644 src/Document/LinksMember.php delete mode 100644 src/Document/LinksTrait.php create mode 100644 src/Document/Member.php create mode 100644 src/Document/MemberCollection.php create mode 100644 src/Document/Meta.php delete mode 100644 src/Document/MetaTrait.php delete mode 100644 src/Document/PrimaryData/MultiIdentifierData.php delete mode 100644 src/Document/PrimaryData/MultiResourceData.php create mode 100644 src/Document/PrimaryData/PrimaryData.php delete mode 100644 src/Document/PrimaryData/PrimaryDataInterface.php delete mode 100644 src/Document/PrimaryData/SingleIdentifierData.php delete mode 100644 src/Document/PrimaryData/SingleResourceData.php delete mode 100644 src/Document/Resource/Linkage/LinkageInterface.php delete mode 100644 src/Document/Resource/Linkage/MultiLinkage.php delete mode 100644 src/Document/Resource/Linkage/NullLinkage.php delete mode 100644 src/Document/Resource/Linkage/SingleLinkage.php delete mode 100644 src/Document/Resource/Relationship.php delete mode 100644 src/Document/Resource/ResourceIdentifier.php create mode 100644 src/MetaDocument.php create mode 100644 src/NullDocument.php delete mode 100644 test/Document/CompoundDocumentTest.php delete mode 100644 test/Document/DocumentTest.php delete mode 100644 test/Document/ErrorTest.php delete mode 100644 test/Document/Resource/Relationship/LinkageTest.php delete mode 100644 test/Document/Resource/Relationship/RelationshipTest.php delete mode 100644 test/Document/Resource/ResourceFieldsTest.php delete mode 100644 test/Document/Resource/ResourceIdentifierTest.php delete mode 100644 test/Document/Resource/ResourceObjectTest.php create mode 100644 test/DocumentTest.php create mode 100644 test/ErrorTest.php delete mode 100644 test/IntegrationTest.php create mode 100644 test/JsonApiTest.php delete mode 100644 test/MemberNamesTest.php diff --git a/composer.json b/composer.json index 9982e20..7ceb1ef 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ } ], "require": { - "php": ">=7.1" + "php": ">=7." }, "require-dev": { "phpunit/phpunit": "^6.0", diff --git a/src/DataDocument.php b/src/DataDocument.php new file mode 100644 index 0000000..413cb09 --- /dev/null +++ b/src/DataDocument.php @@ -0,0 +1,16 @@ +setMeta($meta); - return $doc; - } - - public static function fromErrors(Error ...$errors): self - { - $doc = new self; - $doc->errors = $errors; - return $doc; - } - - public static function fromResource(ResourceObject $resource): self - { - $doc = new self; - $doc->data = new SingleResourceData($resource); - return $doc; - } - - public static function fromResources(ResourceObject ...$resources): self - { - $doc = new self; - $doc->data = new MultiResourceData(...$resources); - return $doc; - } - - public static function fromIdentifier(ResourceIdentifier $identifier): self - { - $doc = new self; - $doc->data = new SingleIdentifierData($identifier); - return $doc; - } - - public static function fromIdentifiers(ResourceIdentifier... $identifiers): self - { - $doc = new self; - $doc->data = new MultiIdentifierData(...$identifiers); - return $doc; - } - - public static function nullDocument(): self - { - $doc = new self; - $doc->data = new NullData(); - return $doc; - } - - public function setApiVersion(string $version = self::DEFAULT_API_VERSION) - { - $this->api['version'] = $version; - } - - public function setApiMeta(iterable $meta): void - { - $this->api['meta'] = new Container($meta); - } - - public function setIncluded(ResourceObject ...$resources): void - { - if (null === $this->data) { - throw new \DomainException('Document with no data cannot contain included resources'); - } - foreach ($resources as $resource) { - if (isset($this->included[(string) $resource->toIdentifier()])) { - throw new \DomainException("Resource {$resource->toIdentifier()} is already included"); - } - $this->included[(string) $resource->toIdentifier()] = $resource; - } - } - - public function markSparse(): void - { - $this->sparse = true; - } - - public function jsonSerialize() - { - $this->enforceFullLinkage(); - return filterNulls([ - 'data' => $this->data, - 'errors' => $this->errors, - 'meta' => $this->meta, - 'jsonapi' => $this->api, - 'links' => $this->links, - 'included' => $this->included ? array_values($this->included) : null, - ]); - } - - private function enforceFullLinkage(): void - { - if ($this->sparse || empty($this->included)) { - return; - } - foreach ($this->included as $included) { - if ($this->data->hasLinkTo($included)) { - continue; - } - foreach ($this->included as $anotherIncluded) { - if ($anotherIncluded->identifies($included)) { - continue 2; - } - } - throw new \DomainException("Full linkage is required for {$included->toIdentifier()}"); - } - } -} diff --git a/src/Document/Container.php b/src/Document/Container.php deleted file mode 100644 index 694f504..0000000 --- a/src/Document/Container.php +++ /dev/null @@ -1,33 +0,0 @@ - $v) { - $this->set((string) $k, $v); - } - } - } - - public function set(string $name, $value) - { - if (! isValidMemberName($name)) { - throw new \OutOfBoundsException("Invalid member name '$name'"); - } - $this->data[$name] = $value; - } - - public function jsonSerialize() - { - return (object) $this->data; - } -} diff --git a/src/Document/DataDocumentMember.php b/src/Document/DataDocumentMember.php new file mode 100644 index 0000000..223bdf9 --- /dev/null +++ b/src/Document/DataDocumentMember.php @@ -0,0 +1,7 @@ +id = $id; - } - - public function setId(string $id) - { - $this->id = $id; - } - - public function setAboutLink(string $link) - { - $this->links['about'] = $link; - } - - public function setStatus(string $status) - { - $this->status = $status; - } - - public function setCode(string $code) - { - $this->code = $code; - } - - public function setTitle(string $title) - { - $this->title = $title; - } - - public function setDetail(string $detail) - { - $this->detail = $detail; - } - - public function setSourcePointer(string $pointer) - { - $this->source['pointer'] = $pointer; - } - - public function setSourceParameter(string $parameter) - { - $this->source['parameter'] = $parameter; - } - - public function jsonSerialize() + public function __construct(ErrorMember ...$errorMembers) { - return filterNulls([ - 'id' => $this->id, - 'links' => $this->links, - 'status' => $this->status, - 'code' => $this->code, - 'title' => $this->title, - 'detail' => $this->detail, - 'source' => $this->source, - 'meta' => $this->meta, - ]) ?: (object) []; + parent::__construct(...$errorMembers); } -} +} \ No newline at end of file diff --git a/src/Document/Error/About.php b/src/Document/Error/About.php new file mode 100644 index 0000000..1deea4a --- /dev/null +++ b/src/Document/Error/About.php @@ -0,0 +1,16 @@ +value = $value; + } + + public function jsonSerialize() + { + return $this->value; + } +} \ No newline at end of file diff --git a/src/Document/Link/AboutLink.php b/src/Document/Link/AboutLink.php new file mode 100644 index 0000000..8f6f8d9 --- /dev/null +++ b/src/Document/Link/AboutLink.php @@ -0,0 +1,23 @@ +links = $this->links ?: new Container(); - $this->links->set($name, $meta ? ['meta' => new Container($meta), 'href' => $url] : $url); - } -} diff --git a/src/Document/Member.php b/src/Document/Member.php new file mode 100644 index 0000000..81257cd --- /dev/null +++ b/src/Document/Member.php @@ -0,0 +1,9 @@ +toName()] = $m; + } + parent::__construct((object)$collection); + } +} \ No newline at end of file diff --git a/src/Document/Meta.php b/src/Document/Meta.php new file mode 100644 index 0000000..fb7e337 --- /dev/null +++ b/src/Document/Meta.php @@ -0,0 +1,29 @@ +meta = new Container($meta); - } -} diff --git a/src/Document/PrimaryData/MultiIdentifierData.php b/src/Document/PrimaryData/MultiIdentifierData.php deleted file mode 100644 index 008fee2..0000000 --- a/src/Document/PrimaryData/MultiIdentifierData.php +++ /dev/null @@ -1,32 +0,0 @@ -identifiers = $identifiers; - } - - public function hasLinkTo(ResourceObject $resource): bool - { - foreach ($this->identifiers as $identifier) { - if ($identifier->identifies($resource)) { - return true; - } - } - return false; - } - - public function jsonSerialize() - { - return $this->identifiers; - } -} diff --git a/src/Document/PrimaryData/MultiResourceData.php b/src/Document/PrimaryData/MultiResourceData.php deleted file mode 100644 index a607ee9..0000000 --- a/src/Document/PrimaryData/MultiResourceData.php +++ /dev/null @@ -1,31 +0,0 @@ -resources = $resources; - } - - public function hasLinkTo(ResourceObject $resource): bool - { - foreach ($this->resources as $myResource) { - if ($myResource->identifies($resource)) { - return true; - } - } - return false; - } - - public function jsonSerialize() - { - return $this->resources; - } -} diff --git a/src/Document/PrimaryData/NullData.php b/src/Document/PrimaryData/NullData.php index 9f577cd..384d988 100644 --- a/src/Document/PrimaryData/NullData.php +++ b/src/Document/PrimaryData/NullData.php @@ -5,7 +5,7 @@ use JsonApiPhp\JsonApi\Document\Resource\ResourceObject; -final class NullData implements PrimaryDataInterface +class NullData extends PrimaryData { public function hasLinkTo(ResourceObject $resource): bool { @@ -16,4 +16,4 @@ public function jsonSerialize() { return null; } -} +} \ No newline at end of file diff --git a/src/Document/PrimaryData/PrimaryData.php b/src/Document/PrimaryData/PrimaryData.php new file mode 100644 index 0000000..7d76b44 --- /dev/null +++ b/src/Document/PrimaryData/PrimaryData.php @@ -0,0 +1,24 @@ +identifier = $identifier; - } - - public function hasLinkTo(ResourceObject $resource): bool - { - return $this->identifier->identifies($resource); - } - - public function jsonSerialize() - { - return $this->identifier; - } -} diff --git a/src/Document/PrimaryData/SingleResourceData.php b/src/Document/PrimaryData/SingleResourceData.php deleted file mode 100644 index b81279b..0000000 --- a/src/Document/PrimaryData/SingleResourceData.php +++ /dev/null @@ -1,26 +0,0 @@ -resource = $resource; - } - - public function hasLinkTo(ResourceObject $resource): bool - { - return $this->resource->identifies($resource); - } - - public function jsonSerialize() - { - return $this->resource; - } -} diff --git a/src/Document/Resource/Linkage/LinkageInterface.php b/src/Document/Resource/Linkage/LinkageInterface.php deleted file mode 100644 index 466a762..0000000 --- a/src/Document/Resource/Linkage/LinkageInterface.php +++ /dev/null @@ -1,13 +0,0 @@ -identifiers = $identifiers; - } - - public function isLinkedTo(ResourceObject $resource): bool - { - foreach ($this->identifiers as $identifier) { - if ($identifier->identifies($resource)) { - return true; - } - } - return false; - } - - public function jsonSerialize() - { - return $this->identifiers; - } -} diff --git a/src/Document/Resource/Linkage/NullLinkage.php b/src/Document/Resource/Linkage/NullLinkage.php deleted file mode 100644 index a44c2ba..0000000 --- a/src/Document/Resource/Linkage/NullLinkage.php +++ /dev/null @@ -1,19 +0,0 @@ -identifier = $identifier; - } - - public function isLinkedTo(ResourceObject $resource): bool - { - return $this->identifier->identifies($resource); - } - - public function jsonSerialize() - { - return $this->identifier; - } -} diff --git a/src/Document/Resource/Relationship.php b/src/Document/Resource/Relationship.php deleted file mode 100644 index da2bdd3..0000000 --- a/src/Document/Resource/Relationship.php +++ /dev/null @@ -1,66 +0,0 @@ -setMeta($meta); - return $r; - } - - public static function fromSelfLink(string $url, iterable $meta = null): self - { - $r = new self; - $r->setLink('self', $url, $meta); - return $r; - } - - public static function fromRelatedLink(string $url, iterable $meta = null): self - { - $r = new self; - $r->setLink('related', $url, $meta); - return $r; - } - - public static function fromLinkage(LinkageInterface $linkage): self - { - $r = new self; - $r->linkage = $linkage; - return $r; - } - - public function hasLinkageTo(ResourceObject $resource): bool - { - return ($this->linkage && $this->linkage->isLinkedTo($resource)); - } - - public function jsonSerialize() - { - return filterNulls([ - 'data' => $this->linkage, - 'links' => $this->links, - 'meta' => $this->meta, - ]); - } -} diff --git a/src/Document/Resource/ResourceIdentifier.php b/src/Document/Resource/ResourceIdentifier.php deleted file mode 100644 index 77e5cba..0000000 --- a/src/Document/Resource/ResourceIdentifier.php +++ /dev/null @@ -1,59 +0,0 @@ -type = $type; - $this->id = $id; - if ($meta) { - $this->meta = new Container($meta); - } - } - - public function jsonSerialize() - { - return filterNulls([ - 'type' => $this->type, - 'id' => $this->id, - 'meta' => $this->meta, - ]); - } - - public function identifies(ResourceObject $resource): bool - { - return $resource->toIdentifier()->equals($this); - } - - private function equals(ResourceIdentifier $that) - { - return $this->type === $that->type && $this->id === $that->id; - } - - public function __toString(): string - { - return "$this->type:$this->id"; - } -} diff --git a/src/Document/Resource/ResourceObject.php b/src/Document/Resource/ResourceObject.php index 0cfeabf..351e5bc 100644 --- a/src/Document/Resource/ResourceObject.php +++ b/src/Document/Resource/ResourceObject.php @@ -3,99 +3,7 @@ namespace JsonApiPhp\JsonApi\Document\Resource; -use JsonApiPhp\JsonApi\Document\Container; -use JsonApiPhp\JsonApi\Document\LinksTrait; -use function JsonApiPhp\JsonApi\filterNulls; -use function JsonApiPhp\JsonApi\isValidMemberName; -use function JsonApiPhp\JsonApi\isValidResourceType; - -final class ResourceObject implements \JsonSerializable +class ResourceObject { - use LinksTrait; - - private $type; - private $id; - private $meta; - private $attributes; - - /** - * @var Relationship[] - */ - private $relationships; - - public function __construct(string $type, string $id = null) - { - if (! isValidResourceType($type)) { - throw new \OutOfBoundsException("Invalid resource type '$type'"); - } - $this->type = $type; - $this->id = $id; - } - - public function setMeta(iterable $meta) - { - $this->meta = new Container($meta); - } - - public function setAttribute(string $name, $value) - { - if ($this->isReservedName($name)) { - throw new \DomainException("Can not use a reserved name '$name'"); - } - if (! isValidMemberName($name)) { - throw new \OutOfBoundsException("Invalid member name '$name'"); - } - if (isset($this->relationships[$name])) { - throw new \DomainException("Field '$name' already exists in relationships"); - } - $this->attributes[$name] = $value; - } - - public function setRelationship(string $name, Relationship $relationship) - { - if ($this->isReservedName($name)) { - throw new \DomainException("Can not use a reserved name '$name'"); - } - if (! isValidMemberName($name)) { - throw new \OutOfBoundsException("Invalid member name '$name'"); - } - if (isset($this->attributes[$name])) { - throw new \DomainException("Field '$name' already exists in attributes"); - } - $this->relationships[$name] = $relationship; - } - - public function toIdentifier(): ResourceIdentifier - { - return new ResourceIdentifier($this->type, $this->id); - } - - public function jsonSerialize() - { - return filterNulls([ - 'type' => $this->type, - 'id' => $this->id, - 'attributes' => $this->attributes, - 'relationships' => $this->relationships, - 'links' => $this->links, - 'meta' => $this->meta, - ]); - } - - public function identifies(ResourceObject $resource): bool - { - if ($this->relationships) { - foreach ($this->relationships as $relationship) { - if ($relationship->hasLinkageTo($resource)) { - return true; - } - } - } - return false; - } - private function isReservedName(string $name): bool - { - return in_array($name, ['id', 'type']); - } -} +} \ No newline at end of file diff --git a/src/MetaDocument.php b/src/MetaDocument.php new file mode 100644 index 0000000..964cc53 --- /dev/null +++ b/src/MetaDocument.php @@ -0,0 +1,16 @@ +setAttribute('first-name', 'Dan'); - $dan->setAttribute('last-name', 'Gebhardt'); - $dan->setAttribute('twitter', 'dgeb'); - $dan->setLink('self', 'http://example.com/people/9'); - - $comment05 = new ResourceObject('comments', '5'); - $comment05->setAttribute('body', 'First!'); - $comment05->setLink('self', 'http://example.com/comments/5'); - $comment05->setRelationship( - 'author', - Relationship::fromLinkage(new SingleLinkage(new ResourceIdentifier('people', '2'))) - ); - - $comment12 = new ResourceObject('comments', '12'); - $comment12->setAttribute('body', 'I like XML better'); - $comment12->setLink('self', 'http://example.com/comments/12'); - $comment12->setRelationship( - 'author', - Relationship::fromLinkage(new SingleLinkage($dan->toIdentifier())) - ); - - $author = Relationship::fromLinkage(new SingleLinkage($dan->toIdentifier())); - $author->setLink('self', 'http://example.com/articles/1/relationships/author'); - $author->setLink('related', 'http://example.com/articles/1/author'); - - $comments = Relationship::fromLinkage(new MultiLinkage($comment05->toIdentifier(), $comment12->toIdentifier())); - $comments->setLink('self', 'http://example.com/articles/1/relationships/comments'); - $comments->setLink('related', 'http://example.com/articles/1/comments'); - - $article = new ResourceObject('articles', '1'); - $article->setAttribute('title', 'JSON API paints my bikeshed!'); - $article->setLink('self', 'http://example.com/articles/1'); - $article->setRelationship('author', $author); - $article->setRelationship('comments', $comments); - - $doc = Document::fromResources($article); - $doc->setIncluded($dan, $comment05, $comment12); - $doc->setLink('self', 'http://example.com/articles'); - $doc->setLink('next', 'http://example.com/articles?page[offset]=2'); - $doc->setLink('last', 'http://example.com/articles?page[offset]=10'); - - $this->assertEncodesTo( - ' - { - "links": { - "self": "http://example.com/articles", - "next": "http://example.com/articles?page[offset]=2", - "last": "http://example.com/articles?page[offset]=10" - }, - "data": [{ - "type": "articles", - "id": "1", - "attributes": { - "title": "JSON API paints my bikeshed!" - }, - "links": { - "self": "http://example.com/articles/1" - }, - "relationships": { - "author": { - "links": { - "self": "http://example.com/articles/1/relationships/author", - "related": "http://example.com/articles/1/author" - }, - "data": { "type": "people", "id": "9" } - }, - "comments": { - "links": { - "self": "http://example.com/articles/1/relationships/comments", - "related": "http://example.com/articles/1/comments" - }, - "data": [ - { "type": "comments", "id": "5" }, - { "type": "comments", "id": "12" } - ] - } - } - }], - "included": [{ - "type": "people", - "id": "9", - "attributes": { - "first-name": "Dan", - "last-name": "Gebhardt", - "twitter": "dgeb" - }, - "links": { - "self": "http://example.com/people/9" - } - }, { - "type": "comments", - "id": "5", - "attributes": { - "body": "First!" - }, - "relationships": { - "author": { - "data": { "type": "people", "id": "2" } - } - }, - "links": { - "self": "http://example.com/comments/5" - } - }, { - "type": "comments", - "id": "12", - "attributes": { - "body": "I like XML better" - }, - "relationships": { - "author": { - "data": { "type": "people", "id": "9" } - } - }, - "links": { - "self": "http://example.com/comments/12" - } - }] - } - ', - $doc - ); - } - - /** - * @expectedException \DomainException - * @expectedExceptionMessage Full linkage is required for apples:1 - * @dataProvider documentsWithoutFullLinkage - * @param Document $doc - */ - public function testFullLinkageIsRequired(Document $doc) - { - $doc->setIncluded(new ResourceObject('apples', '1')); - json_encode($doc); - } - - public function documentsWithoutFullLinkage(): array - { - return [ - [Document::nullDocument()], - [Document::fromIdentifier(new ResourceIdentifier('oranges', '1'))], - [Document::fromIdentifiers(new ResourceIdentifier('oranges', '1'), new ResourceIdentifier('oranges', '2'))], - [Document::fromResource(new ResourceObject('oranges', '1'))], - [Document::fromResources(new ResourceObject('oranges', '1'), new ResourceObject('oranges', '1'))], - ]; - } - - /** - * A compound document must be explicitly marked as sparse. In this case full linkage is not required. - */ - public function testFullLinkageIsNotRequiredIfSparse() - { - $doc = Document::nullDocument(); - $doc->markSparse(); - $doc->setIncluded(new ResourceObject('apples', '1')); - $this->assertEncodesTo( - ' - { - "data": null, - "included": [ - { - "type": "apples", - "id": "1" - } - ] - } - ', - $doc - ); - } - - /** - * Compound documents require “full linkage”, meaning that every included resource MUST be identified - * by at least one resource identifier object in the same document. - * These resource identifier objects could either be primary data or represent resource linkage - * contained within primary or included resources. - */ - public function testIncludedResourceMayBeIdentifiedByPrimaryData() - { - $apple = new ResourceObject('apples', '1'); - $apple->setAttribute('color', 'red'); - $doc = Document::fromIdentifier($apple->toIdentifier()); - $doc->setIncluded($apple); - $this->assertJson(json_encode($doc)); - } - - public function testIncludedResourceMayBeIdentifiedByLinkageInPrimaryData() - { - $author = new ResourceObject('people', '9'); - $author->setAttribute('first-name', 'Dan'); - - $article = new ResourceObject('articles', '1'); - $article->setAttribute('title', 'JSON API paints my bikeshed!'); - $article->setRelationship( - 'author', - Relationship::fromLinkage(new SingleLinkage($author->toIdentifier())) - ); - - $doc = Document::fromResource($article); - $doc->setIncluded($author); - $this->assertJson(json_encode($doc)); - } - - public function testIncludedResourceMayBeIdentifiedByAnotherLinkedResource() - { - $writer = new ResourceObject('writers', '3'); - $writer->setAttribute('name', 'Eric Evans'); - - $book = new ResourceObject('books', '2'); - $book->setAttribute('name', 'Domain Driven Design'); - $book->setRelationship( - 'author', - Relationship::fromLinkage(new SingleLinkage($writer->toIdentifier())) - ); - - $cart = new ResourceObject('shopping-carts', '1'); - $cart->setRelationship( - 'contents', - Relationship::fromLinkage(new MultiLinkage($book->toIdentifier())) - ); - - $this->assertTrue($book->identifies($writer)); - - $doc = Document::fromResource($cart); - $doc->setIncluded($book, $writer); - $this->assertJson(json_encode($doc)); - } - - /** - * A compound document MUST NOT include more than one resource object for each type and id pair. - * @expectedException \DomainException - * @expectedExceptionMessage Resource apples:1 is already included - */ - public function testCanNotBeManyIncludedResourcesWithEqualIdentifiers() - { - $apple = new ResourceObject('apples', '1'); - $apple->setAttribute('color', 'red'); - $doc = Document::fromIdentifier($apple->toIdentifier()); - $doc->setIncluded($apple, $apple); - $this->assertJson(json_encode($doc)); - } - - /** - * If a document does not contain a top-level data key, the included member MUST NOT be present either. - * @expectedException \DomainException - * @expectedExceptionMessage Document with no data cannot contain included resources - */ - public function testIncludedMustOnlyBePresentWithData() - { - $doc = Document::fromMeta(['foo' => 'bar']); - $doc->setIncluded(new ResourceObject('apples', '1')); - } -} diff --git a/test/Document/DocumentTest.php b/test/Document/DocumentTest.php deleted file mode 100644 index 705f769..0000000 --- a/test/Document/DocumentTest.php +++ /dev/null @@ -1,186 +0,0 @@ -assertEncodesTo( - ' - { - "meta": { - "foo": "bar" - } - } - ', - Document::fromMeta(['foo' => 'bar']) - ); - } - - /** - * A valid document may contain just an array of errors. - * The array of errors may even be empty, the documentation does not explicitly restrict it. - */ - public function testDocumentMayContainJustErrors() - { - $this->assertEncodesTo( - ' - { - "errors": [ - { - "id": "first" - } - ] - } - ', - Document::fromErrors(new Error('first')) - ); - - $this->assertEncodesTo( - ' - { - "errors": [] - } - ', - Document::fromErrors() - ); - } - - /** - * A valid document may contain just a primary data object. - * The primary data object is represented by ResourceInterface (@see ResourceObjectTest for details). - * Here is how a document can be created from different kinds of resources: - * - null resource - * - resource identifier - * - full-fledged resource object - * - an array of resource objects/identifiers - */ - public function testDocumentMayContainJustData() - { - $this->assertEncodesTo( - ' - { - "data": null - } - ', - Document::nullDocument(), - 'The simplest document possible contains null' - ); - - $this->assertEncodesTo( - ' - { - "data": { - "type": "books", - "id": "abc123" - } - } - ', - Document::fromIdentifier(new ResourceIdentifier('books', 'abc123')), - 'Resource identifier can be used as primary data' - ); - - $apple = new ResourceObject('apples', '007'); - $apple->setAttribute('color', 'red'); - $this->assertEncodesTo( - ' - { - "data": { - "type": "apples", - "id": "007", - "attributes": { - "color": "red" - } - } - } - ', - Document::fromResource($apple), - 'Full-fledged resource object' - ); - - $this->assertEncodesTo( - ' - { - "data": [ - { - "type": "books", - "id": "12" - }, - { - "type": "carrots", - "id": "42" - } - ] - } - ', - Document::fromIdentifiers( - new ResourceIdentifier('books', '12'), - new ResourceIdentifier('carrots', '42') - ), - 'An array of resource identifiers' - ); - } - - /** - * When a document is created, it is possible to add more stuff to it: - * - API details - * - meta - * - links (@see LinkageTest for details) - */ - public function testDocumentCanHaveExtraProperties() - { - $doc = Document::fromIdentifier( - new ResourceIdentifier('apples', '42') - ); - $doc->setApiVersion('1.0'); - $doc->setApiMeta(['a' => 'b']); - $doc->setMeta(['test' => 'test']); - $doc->setLink('self', 'http://example.com/self'); - $doc->setLink('related', 'http://example.com/rel', ['foo' => 'bar']); - $this->assertEncodesTo( - ' - { - "data": { - "type": "apples", - "id": "42" - }, - "meta": { - "test": "test" - }, - "jsonapi": { - "version": "1.0", - "meta": { - "a": "b" - } - }, - "links": { - "self": "http://example.com/self", - "related": { - "href": "http://example.com/rel", - "meta": { - "foo": "bar" - } - } - } - } - ', - $doc - ); - } -} diff --git a/test/Document/ErrorTest.php b/test/Document/ErrorTest.php deleted file mode 100644 index 995a8e9..0000000 --- a/test/Document/ErrorTest.php +++ /dev/null @@ -1,51 +0,0 @@ -assertEncodesTo('{}', new Error()); - } - - public function testErrorWithFullSetOfProperties() - { - $e = new Error(); - $e->setId('test_id'); - $e->setAboutLink('http://localhost'); - $e->setStatus('404'); - $e->setCode('OMG'); - $e->setTitle('Error'); - $e->setDetail('Nothing is found'); - $e->setSourcePointer('/data'); - $e->setSourceParameter('test_param'); - $e->setMeta(['foo' => 'bar']); - - $this->assertEncodesTo( - ' - { - "id": "test_id", - "links": { - "about":"http://localhost" - }, - "status": "404", - "code": "OMG", - "title": "Error", - "detail": "Nothing is found", - "source": { - "pointer": "/data", - "parameter": "test_param" - }, - "meta": { - "foo":"bar" - } - } - ', - $e - ); - } -} diff --git a/test/Document/Resource/Relationship/LinkageTest.php b/test/Document/Resource/Relationship/LinkageTest.php deleted file mode 100644 index 21671c6..0000000 --- a/test/Document/Resource/Relationship/LinkageTest.php +++ /dev/null @@ -1,119 +0,0 @@ -assertEncodesTo( - 'null', - new NullLinkage() - ); - } - - public function testCanCreateEmptyArrayLinkage() - { - $this->assertEncodesTo( - '[]', - new MultiLinkage() - ); - } - - public function testCanCreateFromSingleResourceId() - { - $this->assertEncodesTo( - ' - { - "type": "books", - "id": "abc" - } - ', - new SingleLinkage(new ResourceIdentifier('books', 'abc')) - ); - } - - public function testCanCreateFromArrayOfResourceIds() - { - $this->assertEncodesTo( - ' - [ - { - "type": "books", - "id": "abc" - }, - { - "type": "squirrels", - "id": "123" - } - ] - ', - new MultiLinkage( - new ResourceIdentifier('books', 'abc'), - new ResourceIdentifier('squirrels', '123') - ) - ); - } - - public function testNullLinkageIsLinkedToNothing() - { - $apple = new ResourceObject('apples', '1'); - $this->assertFalse((new NullLinkage())->isLinkedTo($apple)); - } - - public function testEmptyArrayLinkageIsLinkedToNothing() - { - $apple = new ResourceObject('apples', '1'); - $this->assertFalse((new MultiLinkage())->isLinkedTo($apple)); - } - - public function testSingleLinkageIsLinkedOnlyToItself() - { - $apple = new ResourceObject('apples', '1'); - $orange = new ResourceObject('oranges', '1'); - - $linkage = new SingleLinkage($apple->toIdentifier()); - - $this->assertTrue($linkage->isLinkedTo($apple)); - $this->assertFalse($linkage->isLinkedTo($orange)); - } - - public function testMultiLinkageIsLinkedOnlyToItsMembers() - { - $apple = new ResourceObject('apples', '1'); - $orange = new ResourceObject('oranges', '1'); - $banana = new ResourceObject('bananas', '1'); - - $linkage = new MultiLinkage($apple->toIdentifier(), $orange->toIdentifier()); - - $this->assertTrue($linkage->isLinkedTo($apple)); - $this->assertTrue($linkage->isLinkedTo($orange)); - $this->assertFalse($linkage->isLinkedTo($banana)); - } -} diff --git a/test/Document/Resource/Relationship/RelationshipTest.php b/test/Document/Resource/Relationship/RelationshipTest.php deleted file mode 100644 index fc406af..0000000 --- a/test/Document/Resource/Relationship/RelationshipTest.php +++ /dev/null @@ -1,95 +0,0 @@ -assertEncodesTo( - ' - { - "links": { - "self": "http://localhost" - } - } - ', - Relationship::fromSelfLink('http://localhost') - ); - } - - public function testCanCreateFromRelatedLink() - { - $this->assertEncodesTo( - ' - { - "links": { - "related": "http://localhost" - } - } - ', - Relationship::fromRelatedLink('http://localhost') - ); - } - - public function testCanCreateFromLinkage() - { - $this->assertEncodesTo( - ' - { - "data": null - } - ', - Relationship::fromLinkage(new NullLinkage()) - ); - } - - public function testCanCreateFromMeta() - { - $this->assertEncodesTo( - ' - { - "meta": { - "a": "b" - } - } - ', - Relationship::fromMeta(['a' => 'b']) - ); - } -} diff --git a/test/Document/Resource/ResourceFieldsTest.php b/test/Document/Resource/ResourceFieldsTest.php deleted file mode 100644 index 9e25d6f..0000000 --- a/test/Document/Resource/ResourceFieldsTest.php +++ /dev/null @@ -1,76 +0,0 @@ -setAttribute('foo', 'bar'); - $res->setRelationship('foo', Relationship::fromMeta(['a' => 'b'])); - } - - /** - * @expectedException \DomainException - * @expectedExceptionMessage Field 'foo' already exists in relationships - */ - public function testCanNotSetAttributeIfRelationshipExists() - { - $res = new ResourceObject('books', '1'); - $res->setRelationship('foo', Relationship::fromMeta(['a' => 'b'])); - $res->setAttribute('foo', 'bar'); - } - - /** - * @param string $name - * @expectedException \DomainException - * @expectedExceptionMessage Can not use a reserved name - * @dataProvider reservedAttributeNames - */ - public function testAttributeCanNotHaveReservedNames(string $name) - { - $res = new ResourceObject('books', 'abc'); - $res->setAttribute($name, 1); - } - - /** - * @param string $name - * @expectedException \DomainException - * @expectedExceptionMessage Can not use a reserved name - * @dataProvider reservedAttributeNames - */ - public function testRelationshipCanNotHaveReservedNames(string $name) - { - $res = new ResourceObject('books', 'abc'); - $res->setRelationship($name, Relationship::fromMeta(['a' => 'b'])); - } - - public function reservedAttributeNames(): array - { - return [ - ['id'], - ['type'], - ]; - } -} diff --git a/test/Document/Resource/ResourceIdentifierTest.php b/test/Document/Resource/ResourceIdentifierTest.php deleted file mode 100644 index 9911355..0000000 --- a/test/Document/Resource/ResourceIdentifierTest.php +++ /dev/null @@ -1,44 +0,0 @@ -assertEncodesTo( - ' - { - "type": "books", - "id": "1" - } - ', - new ResourceIdentifier('books', '1') - ); - } - - public function testResourceIdentifierMayContainMeta() - { - $this->assertEncodesTo( - ' - { - "type": "books", - "id": "1", - "meta": { - "foo":"bar" - } - } - ', - new ResourceIdentifier('books', '1', ['foo' => 'bar']) - ); - } -} diff --git a/test/Document/Resource/ResourceObjectTest.php b/test/Document/Resource/ResourceObjectTest.php deleted file mode 100644 index 978a77b..0000000 --- a/test/Document/Resource/ResourceObjectTest.php +++ /dev/null @@ -1,110 +0,0 @@ -assertEncodesTo('{"type": "books"}', new ResourceObject('books')); - } - - /** - * In addition, a resource object MAY contain any of these top-level members: - * - * - attributes: an attributes object representing some of the resource’s data. - * - * - relationships: a relationships object describing relationships - * between the resource and other JSON API resources. - * - * - links: a links object containing links related to the resource. - * - * - meta: a meta object containing non-standard meta-information about a resource - * that can not be represented as an attribute or relationship. - */ - public function testResourceObjectMayContainAttributes() - { - $apple = new ResourceObject('apples', '1'); - $apple->setAttribute('color', 'red'); - $this->assertEncodesTo('{"type":"apples", "id":"1", "attributes":{"color":"red"}}', $apple); - } - - public function testResourceObjectMayContainRelationships() - { - $article = new ResourceObject('articles', '1'); - $user = new ResourceIdentifier('users', '42'); - $article->setRelationship('author', Relationship::fromLinkage(new SingleLinkage($user))); - $this->assertEncodesTo( - ' - { - "type":"articles", - "id":"1", - "relationships":{ - "author":{ - "data":{ - "type":"users", - "id":"42" - } - } - } - } - ', - $article - ); - } - - public function testResourceObjectMayContainLinks() - { - $article = new ResourceObject('articles', '1'); - $article->setLink('self', 'https://example.com'); - $this->assertEncodesTo( - ' - { - "type":"articles", - "id":"1", - "links":{ - "self":"https://example.com" - } - } - ', - $article - ); - } - - public function testResourceObjectMayContainMeta() - { - $article = new ResourceObject('articles', '1'); - $article->setMeta(['tags' => ['cool', 'new']]); - $this->assertEncodesTo( - ' - { - "type":"articles", - "id":"1", - "meta":{ - "tags":[ - "cool", - "new" - ] - } - } - ', - $article - ); - } -} diff --git a/test/DocumentTest.php b/test/DocumentTest.php new file mode 100644 index 0000000..7728e2a --- /dev/null +++ b/test/DocumentTest.php @@ -0,0 +1,89 @@ +assertEncodesTo( + ' + { + "meta": { + "foo": "bar" + } + } + ', + new MetaDocument(new Meta(['foo' => 'bar'])) + ); + } + + /** + * A meta document may contain jsonapi member + */ + public function testMetaDocumentMayContainJsonApiMember() + { + $this->assertEncodesTo( + ' + { + "meta": { + "foo": "bar" + }, + "jsonapi": { + "version": "1.0" + } + } + ', + new MetaDocument( + new Meta(['foo' => 'bar']), + new JsonApi(new Version('1.0')) + ) + ); + } + + public function testNullDocument() + { + $this->assertEncodesTo('{"data": null}', new NullDocument()); + } + + public function testNullDocumentWithExtraMembers() + { + $this->assertEncodesTo( + ' + { + "data": null, + "meta": {"foo": "bar"}, + "jsonapi": { + "version": "1.0" + }, + "links": { + "self": "http://self" + } + } + ', + new NullDocument( + new Meta((object)['foo' => 'bar']), + new JsonApi(new Version('1.0')), + new Links( + new SelfLink( + new Url('http://self') + ) + ) + ) + ); + } + +} \ No newline at end of file diff --git a/test/ErrorTest.php b/test/ErrorTest.php new file mode 100644 index 0000000..bc28e45 --- /dev/null +++ b/test/ErrorTest.php @@ -0,0 +1,65 @@ +assertEncodesTo('{}', new Error()); + } + + public function testErrorWithFullSetOfProperties() + { + $this->assertEncodesTo( + ' + { + "id": "test_id", + "links": { + "about":"http://localhost" + }, + "status": "404", + "code": "OMG", + "title": "Error", + "detail": "Nothing is found", + "source": { + "pointer": "/data", + "parameter": "test_param" + }, + "meta": { + "foo":"bar" + } + } + ', + new Error( + new Id('test_id'), + new About( + new Url('http://localhost') + ), + new Status('404'), + new Code('OMG'), + new Title('Error'), + new Detail('Nothing is found'), + new Source( + new Pointer('/data'), + new Parameter('test_param') + ), + new Meta((object) ['foo' => 'bar']) + ) + ); + } +} \ No newline at end of file diff --git a/test/IntegrationTest.php b/test/IntegrationTest.php deleted file mode 100644 index a884d86..0000000 --- a/test/IntegrationTest.php +++ /dev/null @@ -1,58 +0,0 @@ -setLink('self', '/articles/1/relationships/author'); - $author->setLink('related', '/articles/1/author'); - $articles->setRelationship('author', $author); - $articles->setAttribute('title', 'Rails is Omakase'); - $doc = Document::fromResource($articles); - - $this->assertEquals( - $json, - json_encode($doc, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) - ); - } -} diff --git a/test/JsonApiTest.php b/test/JsonApiTest.php new file mode 100644 index 0000000..a5dda16 --- /dev/null +++ b/test/JsonApiTest.php @@ -0,0 +1,26 @@ +assertEncodesTo( + ' + { + "version": "1.0", + "meta": { + "foo": "bar" + } + } + ', + new JsonApi(new Version('1.0'), new Meta(['foo' => 'bar'])) + ); + } +} diff --git a/test/MemberNamesTest.php b/test/MemberNamesTest.php deleted file mode 100644 index 196c356..0000000 --- a/test/MemberNamesTest.php +++ /dev/null @@ -1,117 +0,0 @@ -invalidAttributeNames() as $attributeName) { - foreach ($this->memberNameCallbacks() as $memberNameCallback) { - yield [$attributeName, $memberNameCallback]; - } - foreach ($this->linksCallbacks() as $linksCallback) { - yield [$attributeName, $linksCallback]; - } - } - } - - public function invalidAttributesAndResourceTypeCallbacks() - { - foreach ($this->invalidAttributeNames() as $attributeName) { - foreach ($this->resourceTypeCallbacks() as $resourceTypeCallback) { - yield [$attributeName, $resourceTypeCallback]; - } - } - } - - private function invalidAttributeNames(): array - { - return [ - '_abcde', - 'abcd_', - 'abc$EDS', - '#abcde', - 'abcde(', - 'b_', - '_a', - '$ab_c-d', - '-abc', - ]; - } - - /** - * @return callable[] - */ - private function memberNameCallbacks() - { - return [ - function ($name) { - (new ResourceObject('apples', '0'))->setRelationship( - $name, - Relationship::fromSelfLink('https://example.com') - ); - }, - function ($name) { - (new ResourceObject('apples', '0'))->setAttribute($name, 'foo'); - }, - ]; - } - - private function resourceTypeCallbacks() - { - yield function ($type) { - new ResourceIdentifier($type, 'foo'); - }; - yield function ($type) { - new ResourceObject($type, 'foo'); - }; - } - - private function linksCallbacks() - { - $objects = [ - Document::nullDocument(), - Relationship::fromSelfLink('https://example.com'), - new ResourceObject('apples', '0'), - ]; - foreach ($objects as $object) { - yield function ($name) use ($object) { - $object->setLink($name, 'https://example.com'); - }; - } - } -} From 84b71502e43b0468571abc68fa9e01448ee8b31a Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Wed, 21 Feb 2018 15:05:28 -0800 Subject: [PATCH 02/37] Removed doc2test --- .doc2test.ini | 2 -- .travis.yml | 1 - composer.json | 5 ++--- 3 files changed, 2 insertions(+), 6 deletions(-) delete mode 100644 .doc2test.ini diff --git a/.doc2test.ini b/.doc2test.ini deleted file mode 100644 index 550103c..0000000 --- a/.doc2test.ini +++ /dev/null @@ -1,2 +0,0 @@ -source = ./README.md -destination = ./doc-test diff --git a/.travis.yml b/.travis.yml index 55b45fc..6bbcb4b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,4 +9,3 @@ before_script: script: - vendor/bin/php-cs-fixer fix -v --dry-run - phpunit --coverage-clover build/logs/clover.xml - - vendor/bin/doc2test && vendor/bin/phpunit -c doc-test/phpunit.xml diff --git a/composer.json b/composer.json index 7ceb1ef..60cbebf 100644 --- a/composer.json +++ b/composer.json @@ -11,12 +11,11 @@ } ], "require": { - "php": ">=7." + "php": ">=7.1" }, "require-dev": { "phpunit/phpunit": "^6.0", - "friendsofphp/php-cs-fixer": "^2.2", - "doc2test/doc2test": "dev-master" + "friendsofphp/php-cs-fixer": "^2.2" }, "autoload": { "psr-4": { From b94bd840fe5a410d54a7bdd24e1e6368ab24cac4 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Wed, 21 Feb 2018 20:00:42 -0800 Subject: [PATCH 03/37] Tests pass --- composer.json | 2 +- src/DataDocument.php | 9 +-- src/DataDocumentMember.php | 7 ++ src/Document/DataDocumentMember.php | 7 -- src/Document/DocumentMember.php | 7 -- src/Document/Error.php | 15 ---- src/Document/Error/ErrorMember.php | 8 -- src/Document/Error/Source.php | 25 ------ src/Document/Error/Source/SourceMember.php | 9 --- src/Document/JsonApi.php | 15 ++-- .../JsonApi/JsonApiDocumentMember.php | 11 +++ src/Document/JsonApi/JsonApiMember.php | 11 --- src/Document/JsonApi/Version.php | 4 +- src/Document/Link/AboutLink.php | 8 +- ...{MemberLink.php => DocumentMemberLink.php} | 4 +- src/Document/Link/FirstLink.php | 2 +- src/Document/Link/Href.php | 6 +- src/Document/Link/LinkObject.php | 2 +- .../Link/LinkObjectDocumentMember.php | 11 +++ src/Document/Link/LinkObjectMember.php | 11 --- src/Document/Link/LinkSet.php | 15 ++-- src/Document/Link/PaginationLink.php | 4 +- src/Document/Link/RelatedLink.php | 4 +- src/Document/Link/SelfLink.php | 4 +- src/Document/Links.php | 1 + src/Document/LinksMember.php | 4 +- src/Document/Member.php | 9 --- src/Document/MemberCollection.php | 16 ---- src/Document/Meta.php | 16 ++-- src/Document/PrimaryData/PrimaryData.php | 24 ------ src/DocumentMember.php | 9 +++ src/{Document => }/Error/About.php | 2 +- src/{Document => }/Error/Code.php | 7 +- src/{Document => }/Error/Detail.php | 6 +- src/Error/Error.php | 15 ++++ src/Error/ErrorMember.php | 8 ++ src/Error/Errors.php | 22 ++++++ src/{Document => }/Error/Id.php | 4 +- src/Error/Source.php | 22 ++++++ src/{Document => }/Error/Source/Parameter.php | 8 +- src/{Document => }/Error/Source/Pointer.php | 8 +- src/Error/Source/SourceMember.php | 10 +++ src/{Document => }/Error/Status.php | 4 +- src/{Document => }/Error/Title.php | 22 +++--- src/ErrorDocument.php | 20 +++++ src/ErrorDocumentMember.php | 9 +++ src/MetaDocument.php | 9 +-- src/NullDocument.php | 15 ---- src/{Document => }/PrimaryData/NullData.php | 10 +-- src/PrimaryData/PrimaryData.php | 20 +++++ src/TopLevelDocumentMember.php | 7 ++ src/functions.php | 8 ++ test/DocumentTest.php | 79 ++++++++++++++++--- test/ErrorTest.php | 20 ++--- test/JsonApiTest.php | 4 +- 55 files changed, 358 insertions(+), 261 deletions(-) create mode 100644 src/DataDocumentMember.php delete mode 100644 src/Document/DataDocumentMember.php delete mode 100644 src/Document/DocumentMember.php delete mode 100644 src/Document/Error.php delete mode 100644 src/Document/Error/ErrorMember.php delete mode 100644 src/Document/Error/Source.php delete mode 100644 src/Document/Error/Source/SourceMember.php create mode 100644 src/Document/JsonApi/JsonApiDocumentMember.php delete mode 100644 src/Document/JsonApi/JsonApiMember.php rename src/Document/Link/{MemberLink.php => DocumentMemberLink.php} (63%) create mode 100644 src/Document/Link/LinkObjectDocumentMember.php delete mode 100644 src/Document/Link/LinkObjectMember.php delete mode 100644 src/Document/Member.php delete mode 100644 src/Document/MemberCollection.php delete mode 100644 src/Document/PrimaryData/PrimaryData.php create mode 100644 src/DocumentMember.php rename src/{Document => }/Error/About.php (88%) rename src/{Document => }/Error/Code.php (63%) rename src/{Document => }/Error/Detail.php (71%) create mode 100644 src/Error/Error.php create mode 100644 src/Error/ErrorMember.php create mode 100644 src/Error/Errors.php rename src/{Document => }/Error/Id.php (81%) create mode 100644 src/Error/Source.php rename src/{Document => }/Error/Source/Parameter.php (68%) rename src/{Document => }/Error/Source/Pointer.php (68%) create mode 100644 src/Error/Source/SourceMember.php rename src/{Document => }/Error/Status.php (82%) rename src/{Document => }/Error/Title.php (52%) create mode 100644 src/ErrorDocument.php create mode 100644 src/ErrorDocumentMember.php delete mode 100644 src/NullDocument.php rename src/{Document => }/PrimaryData/NullData.php (68%) create mode 100644 src/PrimaryData/PrimaryData.php create mode 100644 src/TopLevelDocumentMember.php diff --git a/composer.json b/composer.json index 60cbebf..6ad6230 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ } ], "require": { - "php": ">=7.1" + "php": ">=7.2" }, "require-dev": { "phpunit/phpunit": "^6.0", diff --git a/src/DataDocument.php b/src/DataDocument.php index 413cb09..8cd98eb 100644 --- a/src/DataDocument.php +++ b/src/DataDocument.php @@ -3,14 +3,13 @@ namespace JsonApiPhp\JsonApi; -use JsonApiPhp\JsonApi\Document\DataDocumentMember; -use JsonApiPhp\JsonApi\Document\MemberCollection; -use JsonApiPhp\JsonApi\Document\PrimaryData\PrimaryData; +use JsonApiPhp\JsonApi\Document\JsonSerializableValue; +use JsonApiPhp\JsonApi\PrimaryData\PrimaryData; -class DataDocument extends MemberCollection +class DataDocument extends JsonSerializableValue { public function __construct(PrimaryData $data, DataDocumentMember ...$documentMembers) { - parent::__construct($data, ...$documentMembers); + parent::__construct(indexedByName($data, ...$documentMembers)); } } \ No newline at end of file diff --git a/src/DataDocumentMember.php b/src/DataDocumentMember.php new file mode 100644 index 0000000..40a5096 --- /dev/null +++ b/src/DataDocumentMember.php @@ -0,0 +1,7 @@ +toName()] = $m; - } - parent::__construct((object)$collection); - } -} \ No newline at end of file diff --git a/src/Document/Meta.php b/src/Document/Meta.php index fb7e337..315bb5c 100644 --- a/src/Document/Meta.php +++ b/src/Document/Meta.php @@ -3,16 +3,18 @@ namespace JsonApiPhp\JsonApi\Document; -use JsonApiPhp\JsonApi\Document\Error\ErrorMember; -use JsonApiPhp\JsonApi\Document\JsonApi\JsonApiMember; -use JsonApiPhp\JsonApi\Document\Link\LinkObjectMember; +use JsonApiPhp\JsonApi\DataDocumentMember; +use JsonApiPhp\JsonApi\Document\JsonApi\JsonApiDocumentMember; +use JsonApiPhp\JsonApi\Document\Link\LinkObjectDocumentMember; +use JsonApiPhp\JsonApi\Error\ErrorMember; +use JsonApiPhp\JsonApi\TopLevelDocumentMember; -class Meta +final class Meta extends JsonSerializableValue - implements ErrorMember, LinkObjectMember, DocumentMember, JsonApiMember + implements ErrorMember, LinkObjectDocumentMember, TopLevelDocumentMember, JsonApiDocumentMember, DataDocumentMember { /** - * @param mixed $meta Meta object + * @param array|object $meta */ public function __construct($meta) { @@ -22,7 +24,7 @@ public function __construct($meta) /** * @return string Key to use for merging */ - final public function toName(): string + final public function name(): string { return 'meta'; } diff --git a/src/Document/PrimaryData/PrimaryData.php b/src/Document/PrimaryData/PrimaryData.php deleted file mode 100644 index 7d76b44..0000000 --- a/src/Document/PrimaryData/PrimaryData.php +++ /dev/null @@ -1,24 +0,0 @@ -title = $title; } - final public function toName(): string + final public function name(): string { return 'title'; } + + public function jsonSerialize(): string + { + return $this->title; + } } \ No newline at end of file diff --git a/src/ErrorDocument.php b/src/ErrorDocument.php new file mode 100644 index 0000000..c7c669e --- /dev/null +++ b/src/ErrorDocument.php @@ -0,0 +1,20 @@ +name()] = $m; + return $c; + }, []); } \ No newline at end of file diff --git a/test/DocumentTest.php b/test/DocumentTest.php index 7728e2a..778d80f 100644 --- a/test/DocumentTest.php +++ b/test/DocumentTest.php @@ -3,21 +3,26 @@ namespace JsonApiPhp\JsonApi\Test; +use JsonApiPhp\JsonApi\DataDocument; use JsonApiPhp\JsonApi\Document\JsonApi; use JsonApiPhp\JsonApi\Document\JsonApi\Version; use JsonApiPhp\JsonApi\Document\Link\SelfLink; use JsonApiPhp\JsonApi\Document\Link\Url; use JsonApiPhp\JsonApi\Document\Links; use JsonApiPhp\JsonApi\Document\Meta; +use JsonApiPhp\JsonApi\Error\Error; +use JsonApiPhp\JsonApi\Error\Id; +use JsonApiPhp\JsonApi\ErrorDocument; use JsonApiPhp\JsonApi\MetaDocument; use JsonApiPhp\JsonApi\NullDocument; +use JsonApiPhp\JsonApi\PrimaryData\NullData; class DocumentTest extends BaseTestCase { /** - * A valid document may contain just a meta object. + * A valid document may contain just a meta object */ - public function testDocumentMayContainJustMeta() + public function testMetaDocument() { $this->assertEncodesTo( ' @@ -34,7 +39,7 @@ public function testDocumentMayContainJustMeta() /** * A meta document may contain jsonapi member */ - public function testMetaDocumentMayContainJsonApiMember() + public function testMetaDocumentWithExtraMembers() { $this->assertEncodesTo( ' @@ -49,17 +54,17 @@ public function testMetaDocumentMayContainJsonApiMember() ', new MetaDocument( new Meta(['foo' => 'bar']), - new JsonApi(new Version('1.0')) + new JsonApi('1.0') ) ); } - public function testNullDocument() + public function testDataDocument() { - $this->assertEncodesTo('{"data": null}', new NullDocument()); + $this->assertEncodesTo('{"data": null}', new DataDocument(new NullData())); } - public function testNullDocumentWithExtraMembers() + public function testDataDocumentWithExtraMembers() { $this->assertEncodesTo( ' @@ -74,9 +79,10 @@ public function testNullDocumentWithExtraMembers() } } ', - new NullDocument( - new Meta((object)['foo' => 'bar']), - new JsonApi(new Version('1.0')), + new DataDocument( + new NullData(), + new Meta(['foo' => 'bar']), + new JsonApi('1.0'), new Links( new SelfLink( new Url('http://self') @@ -86,4 +92,57 @@ public function testNullDocumentWithExtraMembers() ); } + public function testErrorDocumentMayContainJustErrors() + { + $this->assertEncodesTo( + ' + { + "errors": [ + { + "id": "first error" + }, + { + "id": "second error" + } + ] + } + ', + new ErrorDocument( + [ + new Error(new Id('first error')), + new Error(new Id('second error')), + ] + ) + ); + } + + public function testErrorDocumentMayContainExtraMembers() + { + $this->assertEncodesTo( + ' + { + "errors": [ + { + "id": "first error" + }, + { + "id": "second error" + } + ], + "meta": {"foo": "bar"}, + "jsonapi": { + "version": "1.0" + } + } + ', + new ErrorDocument( + [ + new Error(new Id('first error')), + new Error(new Id('second error')), + ], + new Meta(['foo' => 'bar']), + new JsonApi('1.0') + ) + ); + } } \ No newline at end of file diff --git a/test/ErrorTest.php b/test/ErrorTest.php index bc28e45..303d886 100644 --- a/test/ErrorTest.php +++ b/test/ErrorTest.php @@ -3,17 +3,17 @@ namespace JsonApiPhp\JsonApi\Document; -use JsonApiPhp\JsonApi\Document\Error\AboutLink; -use JsonApiPhp\JsonApi\Document\Error\Code; -use JsonApiPhp\JsonApi\Document\Error\Detail; -use JsonApiPhp\JsonApi\Document\Error\Id; -use JsonApiPhp\JsonApi\Document\Error\About; -use JsonApiPhp\JsonApi\Document\Error\Source; -use JsonApiPhp\JsonApi\Document\Error\Status; -use JsonApiPhp\JsonApi\Document\Error\Title; -use JsonApiPhp\JsonApi\Document\Error\Source\Parameter; -use JsonApiPhp\JsonApi\Document\Error\Source\Pointer; use JsonApiPhp\JsonApi\Document\Link\Url; +use JsonApiPhp\JsonApi\Error\About; +use JsonApiPhp\JsonApi\Error\Code; +use JsonApiPhp\JsonApi\Error\Detail; +use JsonApiPhp\JsonApi\Error\Error; +use JsonApiPhp\JsonApi\Error\Id; +use JsonApiPhp\JsonApi\Error\Source; +use JsonApiPhp\JsonApi\Error\Source\Parameter; +use JsonApiPhp\JsonApi\Error\Source\Pointer; +use JsonApiPhp\JsonApi\Error\Status; +use JsonApiPhp\JsonApi\Error\Title; use JsonApiPhp\JsonApi\Test\BaseTestCase; class ErrorTest extends BaseTestCase diff --git a/test/JsonApiTest.php b/test/JsonApiTest.php index a5dda16..3e5e4bb 100644 --- a/test/JsonApiTest.php +++ b/test/JsonApiTest.php @@ -9,7 +9,7 @@ class JsonApiTest extends BaseTestCase { - public function testJsonApiMeyContainVersionAndMeta() + public function testJsonApiMayContainVersionAndMeta() { $this->assertEncodesTo( ' @@ -20,7 +20,7 @@ public function testJsonApiMeyContainVersionAndMeta() } } ', - new JsonApi(new Version('1.0'), new Meta(['foo' => 'bar'])) + new JsonApi('1.0', new Meta(['foo' => 'bar'])) ); } } From 2c6ca987e11d499f16892153b9918e5c7f8bc7dc Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Wed, 21 Feb 2018 21:51:35 -0800 Subject: [PATCH 04/37] Tests pass --- src/DataDocument.php | 2 +- src/Document/Attachable.php | 9 ++++++ src/Document/JsonApi.php | 18 ++++++------ .../JsonApi/JsonApiDocumentMember.php | 11 -------- src/Document/JsonApi/Version.php | 19 ------------- src/Document/Link/AboutLink.php | 25 ----------------- src/Document/Link/DocumentMemberLink.php | 15 ---------- src/Document/Link/FirstLink.php | 17 ----------- src/Document/Link/Href.php | 22 --------------- src/Document/Link/Link.php | 7 ----- src/Document/Link/LinkObject.php | 15 ---------- .../Link/LinkObjectDocumentMember.php | 11 -------- src/Document/Link/LinkSet.php | 26 ----------------- src/Document/Link/PaginationLink.php | 14 ---------- src/Document/Link/RelatedLink.php | 17 ----------- src/Document/Link/SelfLink.php | 17 ----------- src/Document/Links.php | 15 ---------- src/Document/LinksMember.php | 10 ------- src/Document/Meta.php | 10 +++---- src/DocumentMember.php | 6 ++-- src/Error/About.php | 16 ----------- src/Error/AboutLink.php | 15 ++++++++++ src/Error/Code.php | 4 +-- src/Error/Detail.php | 4 +-- src/Error/Error.php | 4 +-- src/Error/Errors.php | 4 +-- src/Error/Id.php | 4 +-- src/Error/Source.php | 9 ++++-- src/Error/Source/Parameter.php | 4 +-- src/Error/Source/Pointer.php | 4 +-- src/Error/Status.php | 6 ++-- src/Error/Title.php | 19 ++++--------- src/ErrorDocument.php | 2 +- src/Link/Link.php | 9 ++++++ src/Link/LinkObject.php | 23 +++++++++++++++ src/Link/NamedLink.php | 28 +++++++++++++++++++ src/Link/SelfLink.php | 14 ++++++++++ src/{Document => }/Link/Url.php | 6 ++-- src/MetaDocument.php | 2 +- src/PrimaryData/PrimaryData.php | 4 +-- src/functions.php | 13 +++++---- test/DocumentTest.php | 13 +++------ test/ErrorTest.php | 8 +++--- 43 files changed, 171 insertions(+), 330 deletions(-) create mode 100644 src/Document/Attachable.php delete mode 100644 src/Document/JsonApi/JsonApiDocumentMember.php delete mode 100644 src/Document/JsonApi/Version.php delete mode 100644 src/Document/Link/AboutLink.php delete mode 100644 src/Document/Link/DocumentMemberLink.php delete mode 100644 src/Document/Link/FirstLink.php delete mode 100644 src/Document/Link/Href.php delete mode 100644 src/Document/Link/Link.php delete mode 100644 src/Document/Link/LinkObject.php delete mode 100644 src/Document/Link/LinkObjectDocumentMember.php delete mode 100644 src/Document/Link/LinkSet.php delete mode 100644 src/Document/Link/PaginationLink.php delete mode 100644 src/Document/Link/RelatedLink.php delete mode 100644 src/Document/Link/SelfLink.php delete mode 100644 src/Document/Links.php delete mode 100644 src/Document/LinksMember.php delete mode 100644 src/Error/About.php create mode 100644 src/Error/AboutLink.php create mode 100644 src/Link/Link.php create mode 100644 src/Link/LinkObject.php create mode 100644 src/Link/NamedLink.php create mode 100644 src/Link/SelfLink.php rename src/{Document => }/Link/Url.php (64%) diff --git a/src/DataDocument.php b/src/DataDocument.php index 8cd98eb..bdcbc7f 100644 --- a/src/DataDocument.php +++ b/src/DataDocument.php @@ -10,6 +10,6 @@ class DataDocument extends JsonSerializableValue { public function __construct(PrimaryData $data, DataDocumentMember ...$documentMembers) { - parent::__construct(indexedByName($data, ...$documentMembers)); + parent::__construct(combine($data, ...$documentMembers)); } } \ No newline at end of file diff --git a/src/Document/Attachable.php b/src/Document/Attachable.php new file mode 100644 index 0000000..2124025 --- /dev/null +++ b/src/Document/Attachable.php @@ -0,0 +1,9 @@ + $version + ]; + if ($meta) { + $meta->attachTo($jsonapi); + } + + parent::__construct($jsonapi); } - /** - * @return string Key to use for merging - */ - public function name(): string + public function attachTo(object $o): void { - return 'jsonapi'; + $o->jsonapi = $this; } } \ No newline at end of file diff --git a/src/Document/JsonApi/JsonApiDocumentMember.php b/src/Document/JsonApi/JsonApiDocumentMember.php deleted file mode 100644 index 6eeb6de..0000000 --- a/src/Document/JsonApi/JsonApiDocumentMember.php +++ /dev/null @@ -1,11 +0,0 @@ -meta = $this; } } \ No newline at end of file diff --git a/src/DocumentMember.php b/src/DocumentMember.php index ab139f9..c375ea7 100644 --- a/src/DocumentMember.php +++ b/src/DocumentMember.php @@ -3,7 +3,9 @@ namespace JsonApiPhp\JsonApi; -interface DocumentMember extends \JsonSerializable +use JsonApiPhp\JsonApi\Document\Attachable; + +interface DocumentMember + extends \JsonSerializable, Attachable { - public function name(): string; } \ No newline at end of file diff --git a/src/Error/About.php b/src/Error/About.php deleted file mode 100644 index ee049fa..0000000 --- a/src/Error/About.php +++ /dev/null @@ -1,16 +0,0 @@ -code = $this; } } \ No newline at end of file diff --git a/src/Error/Detail.php b/src/Error/Detail.php index 4b7dbef..00b02c6 100644 --- a/src/Error/Detail.php +++ b/src/Error/Detail.php @@ -18,8 +18,8 @@ public function __construct(string $detail) parent::__construct($detail); } - final public function name(): string + public function attachTo(object $o): void { - return 'detail'; + $o->detail = $this; } } \ No newline at end of file diff --git a/src/Error/Error.php b/src/Error/Error.php index 89d8aa7..c73a9ec 100644 --- a/src/Error/Error.php +++ b/src/Error/Error.php @@ -4,12 +4,12 @@ namespace JsonApiPhp\JsonApi\Error; use JsonApiPhp\JsonApi\Document\JsonSerializableValue; -use function JsonApiPhp\JsonApi\indexedByName; +use function JsonApiPhp\JsonApi\combine; class Error extends JsonSerializableValue { public function __construct(ErrorMember ...$errorMembers) { - parent::__construct(indexedByName(...$errorMembers)); + parent::__construct(combine(...$errorMembers)); } } \ No newline at end of file diff --git a/src/Error/Errors.php b/src/Error/Errors.php index 095198b..093a77e 100644 --- a/src/Error/Errors.php +++ b/src/Error/Errors.php @@ -15,8 +15,8 @@ public function __construct(Error $error, Error ...$errors) parent::__construct(func_get_args()); } - public function name(): string + public function attachTo(object $o): void { - return 'errors'; + $o->errors = $this; } } \ No newline at end of file diff --git a/src/Error/Id.php b/src/Error/Id.php index f44dd42..df3fbe1 100644 --- a/src/Error/Id.php +++ b/src/Error/Id.php @@ -17,8 +17,8 @@ public function __construct(string $id) parent::__construct($id); } - final public function name(): string + public function attachTo(object $o): void { - return 'id'; + $o->id = $this; } } \ No newline at end of file diff --git a/src/Error/Source.php b/src/Error/Source.php index fc8096e..bf56a06 100644 --- a/src/Error/Source.php +++ b/src/Error/Source.php @@ -5,18 +5,23 @@ use JsonApiPhp\JsonApi\Document\JsonSerializableValue; use JsonApiPhp\JsonApi\Error\Source\SourceMember; -use function JsonApiPhp\JsonApi\indexedByName; +use function JsonApiPhp\JsonApi\combine; class Source extends JsonSerializableValue implements ErrorMember { public function __construct(SourceMember ...$sourceMembers) { - parent::__construct(indexedByName(...$sourceMembers)); + parent::__construct(combine(...$sourceMembers)); } final public function name(): string { return 'source'; } + + public function attachTo(object $o): void + { + $o->source = $this; + } } \ No newline at end of file diff --git a/src/Error/Source/Parameter.php b/src/Error/Source/Parameter.php index 541a5c5..0a0a4c2 100644 --- a/src/Error/Source/Parameter.php +++ b/src/Error/Source/Parameter.php @@ -17,8 +17,8 @@ public function __construct(string $parameter) parent::__construct($parameter); } - public function name(): string + public function attachTo(object $o): void { - return 'parameter'; + $o->parameter = $this; } } \ No newline at end of file diff --git a/src/Error/Source/Pointer.php b/src/Error/Source/Pointer.php index 3c23983..5bb8e92 100644 --- a/src/Error/Source/Pointer.php +++ b/src/Error/Source/Pointer.php @@ -17,8 +17,8 @@ public function __construct(string $pointer) parent::__construct($pointer); } - public function name(): string + public function attachTo(object $o): void { - return 'pointer'; + $o->pointer = $this; } } \ No newline at end of file diff --git a/src/Error/Status.php b/src/Error/Status.php index 2393162..827819e 100644 --- a/src/Error/Status.php +++ b/src/Error/Status.php @@ -5,7 +5,7 @@ use JsonApiPhp\JsonApi\Document\JsonSerializableValue; -class Status +final class Status extends JsonSerializableValue implements ErrorMember { @@ -17,8 +17,8 @@ public function __construct(string $status) parent::__construct($status); } - final public function name(): string + public function attachTo(object $o): void { - return 'status'; + $o->status = $this; } } \ No newline at end of file diff --git a/src/Error/Title.php b/src/Error/Title.php index 3f5d5ec..7f4a718 100644 --- a/src/Error/Title.php +++ b/src/Error/Title.php @@ -3,30 +3,23 @@ namespace JsonApiPhp\JsonApi\Error; +use JsonApiPhp\JsonApi\Document\JsonSerializableValue; + class Title + extends JsonSerializableValue implements ErrorMember { - /** - * @var string - */ - private $title; - /** * @param string $title a short, human-readable summary of the problem that SHOULD NOT change from occurrence * to occurrence of the problem, except for purposes of localization */ public function __construct(string $title) { - $this->title = $title; - } - - final public function name(): string - { - return 'title'; + parent::__construct($title); } - public function jsonSerialize(): string + public function attachTo(object $o): void { - return $this->title; + $o->title = $this; } } \ No newline at end of file diff --git a/src/ErrorDocument.php b/src/ErrorDocument.php index c7c669e..d25b673 100644 --- a/src/ErrorDocument.php +++ b/src/ErrorDocument.php @@ -15,6 +15,6 @@ class ErrorDocument */ public function __construct(array $errors, TopLevelDocumentMember ...$members) { - parent::__construct(indexedByName(new Errors(...$errors), ...$members)); + parent::__construct(combine(new Errors(...$errors), ...$members)); } } \ No newline at end of file diff --git a/src/Link/Link.php b/src/Link/Link.php new file mode 100644 index 0000000..5ca8fdd --- /dev/null +++ b/src/Link/Link.php @@ -0,0 +1,9 @@ + $href + ]; + if ($meta) { + $meta->attachTo($link); + } + parent::__construct($link); + } +} \ No newline at end of file diff --git a/src/Link/NamedLink.php b/src/Link/NamedLink.php new file mode 100644 index 0000000..25b3711 --- /dev/null +++ b/src/Link/NamedLink.php @@ -0,0 +1,28 @@ +name = $name; + } + + function attachTo(object $o) + { + if (!isset($o->links)) { + $o->links = (object)[]; + } + $o->links->{$this->name} = $this; + } +} \ No newline at end of file diff --git a/src/Link/SelfLink.php b/src/Link/SelfLink.php new file mode 100644 index 0000000..1c7af0d --- /dev/null +++ b/src/Link/SelfLink.php @@ -0,0 +1,14 @@ +data = $this; } } \ No newline at end of file diff --git a/src/functions.php b/src/functions.php index 4ba6446..347456f 100644 --- a/src/functions.php +++ b/src/functions.php @@ -3,15 +3,18 @@ namespace JsonApiPhp\JsonApi; +use JsonApiPhp\JsonApi\Document\Attachable; + function isValidMemberName(string $name): bool { return preg_match('/^(?=[^-_ ])[a-zA-Z0-9\x{0080}-\x{FFFF}-_ ]*(?<=[^-_ ])$/u', $name) === 1; } -function indexedByName(DocumentMember ...$members): object +function combine(Attachable ...$things): object { - return (object)array_reduce($members, function (array $c, DocumentMember $m) { - $c[$m->name()] = $m; - return $c; - }, []); + $obj = (object)[]; + foreach ($things as $thing) { + $thing->attachTo($obj); + } + return $obj; } \ No newline at end of file diff --git a/test/DocumentTest.php b/test/DocumentTest.php index 778d80f..e5b57ec 100644 --- a/test/DocumentTest.php +++ b/test/DocumentTest.php @@ -5,16 +5,13 @@ use JsonApiPhp\JsonApi\DataDocument; use JsonApiPhp\JsonApi\Document\JsonApi; -use JsonApiPhp\JsonApi\Document\JsonApi\Version; -use JsonApiPhp\JsonApi\Document\Link\SelfLink; -use JsonApiPhp\JsonApi\Document\Link\Url; -use JsonApiPhp\JsonApi\Document\Links; +use JsonApiPhp\JsonApi\Link\SelfLink; +use JsonApiPhp\JsonApi\Link\Url; use JsonApiPhp\JsonApi\Document\Meta; use JsonApiPhp\JsonApi\Error\Error; use JsonApiPhp\JsonApi\Error\Id; use JsonApiPhp\JsonApi\ErrorDocument; use JsonApiPhp\JsonApi\MetaDocument; -use JsonApiPhp\JsonApi\NullDocument; use JsonApiPhp\JsonApi\PrimaryData\NullData; class DocumentTest extends BaseTestCase @@ -83,10 +80,8 @@ public function testDataDocumentWithExtraMembers() new NullData(), new Meta(['foo' => 'bar']), new JsonApi('1.0'), - new Links( - new SelfLink( - new Url('http://self') - ) + new SelfLink( + new Url('http://self') ) ) ); diff --git a/test/ErrorTest.php b/test/ErrorTest.php index 303d886..f0c436b 100644 --- a/test/ErrorTest.php +++ b/test/ErrorTest.php @@ -3,8 +3,7 @@ namespace JsonApiPhp\JsonApi\Document; -use JsonApiPhp\JsonApi\Document\Link\Url; -use JsonApiPhp\JsonApi\Error\About; +use JsonApiPhp\JsonApi\Error\AboutLink; use JsonApiPhp\JsonApi\Error\Code; use JsonApiPhp\JsonApi\Error\Detail; use JsonApiPhp\JsonApi\Error\Error; @@ -14,6 +13,7 @@ use JsonApiPhp\JsonApi\Error\Source\Pointer; use JsonApiPhp\JsonApi\Error\Status; use JsonApiPhp\JsonApi\Error\Title; +use JsonApiPhp\JsonApi\Link\Url; use JsonApiPhp\JsonApi\Test\BaseTestCase; class ErrorTest extends BaseTestCase @@ -47,7 +47,7 @@ public function testErrorWithFullSetOfProperties() ', new Error( new Id('test_id'), - new About( + new AboutLink( new Url('http://localhost') ), new Status('404'), @@ -58,7 +58,7 @@ public function testErrorWithFullSetOfProperties() new Pointer('/data'), new Parameter('test_param') ), - new Meta((object) ['foo' => 'bar']) + new Meta(['foo' => 'bar']) ) ); } From 6ea09263120260755e1953ccff7805f86ab17a8b Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Wed, 21 Feb 2018 22:19:08 -0800 Subject: [PATCH 05/37] Tests pass --- src/Document/AttachableValue.php | 22 ++++++++++++++++++++++ src/Document/JsonApi.php | 9 ++------- src/Document/Meta.php | 9 ++------- src/Error/Code.php | 11 +++-------- src/Error/Detail.php | 12 +++--------- src/Error/Error.php | 15 ++++++++++++--- src/Error/Errors.php | 11 +++-------- src/Error/Id.php | 11 +++-------- src/Error/Source.php | 17 ++++------------- src/Error/Source/Parameter.php | 11 +++-------- src/Error/Source/Pointer.php | 11 +++-------- src/Error/Status.php | 11 +++-------- src/Error/Title.php | 11 +++-------- src/ErrorDocument.php | 9 ++------- src/Link/NamedLink.php | 13 ++++--------- src/MandatoryErrorDocumentMember.php | 9 +++++++++ src/PrimaryData/NullData.php | 13 +++++-------- src/PrimaryData/PrimaryData.php | 15 +++------------ src/TopLevelDocumentMember.php | 2 +- test/DocumentTest.php | 16 ++++++---------- 20 files changed, 96 insertions(+), 142 deletions(-) create mode 100644 src/Document/AttachableValue.php create mode 100644 src/MandatoryErrorDocumentMember.php diff --git a/src/Document/AttachableValue.php b/src/Document/AttachableValue.php new file mode 100644 index 0000000..84d4e6a --- /dev/null +++ b/src/Document/AttachableValue.php @@ -0,0 +1,22 @@ +name = $name; + } + + function attachTo(object $o) + { + $o->{$this->name} = $this; + } +} \ No newline at end of file diff --git a/src/Document/JsonApi.php b/src/Document/JsonApi.php index e8a0658..f9d9767 100644 --- a/src/Document/JsonApi.php +++ b/src/Document/JsonApi.php @@ -7,7 +7,7 @@ use JsonApiPhp\JsonApi\TopLevelDocumentMember; final class JsonApi - extends JsonSerializableValue + extends AttachableValue implements TopLevelDocumentMember, DataDocumentMember { public function __construct(string $version, Meta $meta = null) @@ -19,11 +19,6 @@ public function __construct(string $version, Meta $meta = null) $meta->attachTo($jsonapi); } - parent::__construct($jsonapi); - } - - public function attachTo(object $o): void - { - $o->jsonapi = $this; + parent::__construct('jsonapi', $jsonapi); } } \ No newline at end of file diff --git a/src/Document/Meta.php b/src/Document/Meta.php index 912c880..7106982 100644 --- a/src/Document/Meta.php +++ b/src/Document/Meta.php @@ -11,7 +11,7 @@ use JsonApiPhp\JsonApi\TopLevelDocumentMember; final class Meta - extends JsonSerializableValue + extends AttachableValue implements ErrorMember, TopLevelDocumentMember, DataDocumentMember { /** @@ -19,11 +19,6 @@ final class Meta */ public function __construct($meta) { - parent::__construct((object) $meta); - } - - public function attachTo(object $o): void - { - $o->meta = $this; + parent::__construct('meta', (object) $meta); } } \ No newline at end of file diff --git a/src/Error/Code.php b/src/Error/Code.php index d946718..18e4096 100644 --- a/src/Error/Code.php +++ b/src/Error/Code.php @@ -3,10 +3,10 @@ namespace JsonApiPhp\JsonApi\Error; -use JsonApiPhp\JsonApi\Document\JsonSerializableValue; +use JsonApiPhp\JsonApi\Document\AttachableValue; class Code - extends JsonSerializableValue + extends AttachableValue implements ErrorMember { /** @@ -14,11 +14,6 @@ class Code */ public function __construct(string $code) { - parent::__construct($code); - } - - public function attachTo(object $o): void - { - $o->code = $this; + parent::__construct('code', $code); } } \ No newline at end of file diff --git a/src/Error/Detail.php b/src/Error/Detail.php index 00b02c6..944637a 100644 --- a/src/Error/Detail.php +++ b/src/Error/Detail.php @@ -3,11 +3,10 @@ namespace JsonApiPhp\JsonApi\Error; -use JsonApiPhp\JsonApi\Document\JsonSerializableValue; -use function JsonApiPhp\JsonApi\mergeAll; +use JsonApiPhp\JsonApi\Document\AttachableValue; class Detail - extends JsonSerializableValue + extends AttachableValue implements ErrorMember { /** @@ -15,11 +14,6 @@ class Detail */ public function __construct(string $detail) { - parent::__construct($detail); - } - - public function attachTo(object $o): void - { - $o->detail = $this; + parent::__construct('detail', $detail); } } \ No newline at end of file diff --git a/src/Error/Error.php b/src/Error/Error.php index c73a9ec..45206b2 100644 --- a/src/Error/Error.php +++ b/src/Error/Error.php @@ -4,12 +4,21 @@ namespace JsonApiPhp\JsonApi\Error; use JsonApiPhp\JsonApi\Document\JsonSerializableValue; +use JsonApiPhp\JsonApi\MandatoryErrorDocumentMember; use function JsonApiPhp\JsonApi\combine; -class Error extends JsonSerializableValue +class Error + extends JsonSerializableValue + implements MandatoryErrorDocumentMember + { - public function __construct(ErrorMember ...$errorMembers) + public function __construct(ErrorMember ...$errors) + { + parent::__construct(combine(...$errors)); + } + + function attachTo(object $o) { - parent::__construct(combine(...$errorMembers)); + $o->errors[] = $this; } } \ No newline at end of file diff --git a/src/Error/Errors.php b/src/Error/Errors.php index 093a77e..f1f2803 100644 --- a/src/Error/Errors.php +++ b/src/Error/Errors.php @@ -3,20 +3,15 @@ namespace JsonApiPhp\JsonApi\Error; -use JsonApiPhp\JsonApi\Document\JsonSerializableValue; +use JsonApiPhp\JsonApi\Document\AttachableValue; use JsonApiPhp\JsonApi\ErrorDocumentMember; final class Errors - extends JsonSerializableValue + extends AttachableValue implements ErrorDocumentMember { public function __construct(Error $error, Error ...$errors) { - parent::__construct(func_get_args()); - } - - public function attachTo(object $o): void - { - $o->errors = $this; + parent::__construct('errors', func_get_args()); } } \ No newline at end of file diff --git a/src/Error/Id.php b/src/Error/Id.php index df3fbe1..8a31d01 100644 --- a/src/Error/Id.php +++ b/src/Error/Id.php @@ -3,10 +3,10 @@ namespace JsonApiPhp\JsonApi\Error; -use JsonApiPhp\JsonApi\Document\JsonSerializableValue; +use JsonApiPhp\JsonApi\Document\AttachableValue; class Id - extends JsonSerializableValue + extends AttachableValue implements ErrorMember { /** @@ -14,11 +14,6 @@ class Id */ public function __construct(string $id) { - parent::__construct($id); - } - - public function attachTo(object $o): void - { - $o->id = $this; + parent::__construct('id', $id); } } \ No newline at end of file diff --git a/src/Error/Source.php b/src/Error/Source.php index bf56a06..284c9fa 100644 --- a/src/Error/Source.php +++ b/src/Error/Source.php @@ -3,25 +3,16 @@ namespace JsonApiPhp\JsonApi\Error; -use JsonApiPhp\JsonApi\Document\JsonSerializableValue; +use JsonApiPhp\JsonApi\Document\AttachableValue; use JsonApiPhp\JsonApi\Error\Source\SourceMember; use function JsonApiPhp\JsonApi\combine; -class Source extends JsonSerializableValue +class Source + extends AttachableValue implements ErrorMember { public function __construct(SourceMember ...$sourceMembers) { - parent::__construct(combine(...$sourceMembers)); - } - - final public function name(): string - { - return 'source'; - } - - public function attachTo(object $o): void - { - $o->source = $this; + parent::__construct('source', combine(...$sourceMembers)); } } \ No newline at end of file diff --git a/src/Error/Source/Parameter.php b/src/Error/Source/Parameter.php index 0a0a4c2..32a6b5e 100644 --- a/src/Error/Source/Parameter.php +++ b/src/Error/Source/Parameter.php @@ -3,10 +3,10 @@ namespace JsonApiPhp\JsonApi\Error\Source; -use JsonApiPhp\JsonApi\Document\JsonSerializableValue; +use JsonApiPhp\JsonApi\Document\AttachableValue; final class Parameter - extends JsonSerializableValue + extends AttachableValue implements SourceMember { /** @@ -14,11 +14,6 @@ final class Parameter */ public function __construct(string $parameter) { - parent::__construct($parameter); - } - - public function attachTo(object $o): void - { - $o->parameter = $this; + parent::__construct('parameter', $parameter); } } \ No newline at end of file diff --git a/src/Error/Source/Pointer.php b/src/Error/Source/Pointer.php index 5bb8e92..eef09d3 100644 --- a/src/Error/Source/Pointer.php +++ b/src/Error/Source/Pointer.php @@ -3,10 +3,10 @@ namespace JsonApiPhp\JsonApi\Error\Source; -use JsonApiPhp\JsonApi\Document\JsonSerializableValue; +use JsonApiPhp\JsonApi\Document\AttachableValue; final class Pointer - extends JsonSerializableValue + extends AttachableValue implements SourceMember { /** @@ -14,11 +14,6 @@ final class Pointer */ public function __construct(string $pointer) { - parent::__construct($pointer); - } - - public function attachTo(object $o): void - { - $o->pointer = $this; + parent::__construct('pointer', $pointer); } } \ No newline at end of file diff --git a/src/Error/Status.php b/src/Error/Status.php index 827819e..00fa829 100644 --- a/src/Error/Status.php +++ b/src/Error/Status.php @@ -3,10 +3,10 @@ namespace JsonApiPhp\JsonApi\Error; -use JsonApiPhp\JsonApi\Document\JsonSerializableValue; +use JsonApiPhp\JsonApi\Document\AttachableValue; final class Status - extends JsonSerializableValue + extends AttachableValue implements ErrorMember { /** @@ -14,11 +14,6 @@ final class Status */ public function __construct(string $status) { - parent::__construct($status); - } - - public function attachTo(object $o): void - { - $o->status = $this; + parent::__construct('status', $status); } } \ No newline at end of file diff --git a/src/Error/Title.php b/src/Error/Title.php index 7f4a718..1bacd0a 100644 --- a/src/Error/Title.php +++ b/src/Error/Title.php @@ -3,10 +3,10 @@ namespace JsonApiPhp\JsonApi\Error; -use JsonApiPhp\JsonApi\Document\JsonSerializableValue; +use JsonApiPhp\JsonApi\Document\AttachableValue; class Title - extends JsonSerializableValue + extends AttachableValue implements ErrorMember { /** @@ -15,11 +15,6 @@ class Title */ public function __construct(string $title) { - parent::__construct($title); - } - - public function attachTo(object $o): void - { - $o->title = $this; + parent::__construct('title', $title); } } \ No newline at end of file diff --git a/src/ErrorDocument.php b/src/ErrorDocument.php index d25b673..d33148a 100644 --- a/src/ErrorDocument.php +++ b/src/ErrorDocument.php @@ -4,17 +4,12 @@ namespace JsonApiPhp\JsonApi; use JsonApiPhp\JsonApi\Document\JsonSerializableValue; -use JsonApiPhp\JsonApi\Error\Errors; class ErrorDocument extends JsonSerializableValue { - /** - * @param \JsonApiPhp\JsonApi\Error\Error[] $errors - * @param \JsonApiPhp\JsonApi\TopLevelDocumentMember[] ...$members - */ - public function __construct(array $errors, TopLevelDocumentMember ...$members) + public function __construct(MandatoryErrorDocumentMember $error, ErrorDocumentMember ...$members) { - parent::__construct(combine(new Errors(...$errors), ...$members)); + parent::__construct(combine($error, ...$members)); } } \ No newline at end of file diff --git a/src/Link/NamedLink.php b/src/Link/NamedLink.php index 25b3711..c3ea35b 100644 --- a/src/Link/NamedLink.php +++ b/src/Link/NamedLink.php @@ -3,19 +3,14 @@ namespace JsonApiPhp\JsonApi\Link; -use JsonApiPhp\JsonApi\Document\Attachable; -use JsonApiPhp\JsonApi\Document\JsonSerializableValue; +use JsonApiPhp\JsonApi\Document\AttachableValue; abstract class NamedLink - extends JsonSerializableValue - implements Attachable + extends AttachableValue { - private $name; - public function __construct(string $name, Link $link) { - parent::__construct($link); - $this->name = $name; + parent::__construct($name, $link); } function attachTo(object $o) @@ -23,6 +18,6 @@ function attachTo(object $o) if (!isset($o->links)) { $o->links = (object)[]; } - $o->links->{$this->name} = $this; + parent::attachTo($o->links); } } \ No newline at end of file diff --git a/src/MandatoryErrorDocumentMember.php b/src/MandatoryErrorDocumentMember.php new file mode 100644 index 0000000..c4ce2af --- /dev/null +++ b/src/MandatoryErrorDocumentMember.php @@ -0,0 +1,9 @@ +data = $this; - } } \ No newline at end of file diff --git a/src/TopLevelDocumentMember.php b/src/TopLevelDocumentMember.php index 9656d66..20a6a96 100644 --- a/src/TopLevelDocumentMember.php +++ b/src/TopLevelDocumentMember.php @@ -1,7 +1,7 @@ 'bar']), new JsonApi('1.0') ) From 00a1b65a8704cf9d0c876f7151c110587a2559cc Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Wed, 21 Feb 2018 22:37:57 -0800 Subject: [PATCH 06/37] Tests pass --- src/{Document => }/Attachable.php | 5 +++- src/AttachableValue.php | 25 +++++++++++++++++++ src/DataDocument.php | 3 +-- src/DataDocumentMember.php | 4 ++- src/Document/AttachableValue.php | 22 ---------------- src/Document/Resource/ResourceObject.php | 9 ------- src/DocumentMember.php | 5 ++-- src/Error/AboutLink.php | 4 +-- src/Error/Code.php | 4 +-- src/Error/Detail.php | 4 +-- src/Error/Error.php | 4 +-- src/Error/ErrorMember.php | 3 +++ src/Error/Errors.php | 17 ------------- src/Error/Id.php | 4 +-- src/Error/Source.php | 4 +-- src/Error/Source/Parameter.php | 2 +- src/Error/Source/Pointer.php | 2 +- src/Error/Source/SourceMember.php | 3 +++ src/Error/Status.php | 2 +- src/Error/Title.php | 2 +- src/ErrorDocument.php | 4 +-- src/ErrorDocumentMember.php | 3 +++ src/{Document => }/JsonApi.php | 5 +--- src/{Document => }/JsonSerializableValue.php | 5 +++- .../{NamedLink.php => AttachableLink.php} | 11 +++++--- src/Link/Link.php | 3 +++ src/Link/LinkObject.php | 6 ++--- src/Link/SelfLink.php | 2 +- src/Link/Url.php | 4 +-- src/MandatoryErrorDocumentMember.php | 3 +++ src/{Document => }/Meta.php | 7 +----- src/MetaDocument.php | 5 +--- src/PrimaryData/NullData.php | 4 +-- src/PrimaryData/PrimaryData.php | 3 +++ src/TopLevelDocumentMember.php | 6 ++++- src/functions.php | 2 -- test/DocumentTest.php | 4 +-- test/ErrorTest.php | 1 + test/JsonApiTest.php | 4 +-- 39 files changed, 103 insertions(+), 107 deletions(-) rename src/{Document => }/Attachable.php (64%) create mode 100644 src/AttachableValue.php delete mode 100644 src/Document/AttachableValue.php delete mode 100644 src/Document/Resource/ResourceObject.php delete mode 100644 src/Error/Errors.php rename src/{Document => }/JsonApi.php (76%) rename src/{Document => }/JsonSerializableValue.php (84%) rename src/Link/{NamedLink.php => AttachableLink.php} (60%) rename src/{Document => }/Meta.php (54%) diff --git a/src/Document/Attachable.php b/src/Attachable.php similarity index 64% rename from src/Document/Attachable.php rename to src/Attachable.php index 2124025..cfb8175 100644 --- a/src/Document/Attachable.php +++ b/src/Attachable.php @@ -1,8 +1,11 @@ key = $key; + } + + function attachTo(object $o) + { + $o->{$this->key} = $this; + } +} \ No newline at end of file diff --git a/src/DataDocument.php b/src/DataDocument.php index bdcbc7f..6425e7c 100644 --- a/src/DataDocument.php +++ b/src/DataDocument.php @@ -3,10 +3,9 @@ namespace JsonApiPhp\JsonApi; -use JsonApiPhp\JsonApi\Document\JsonSerializableValue; use JsonApiPhp\JsonApi\PrimaryData\PrimaryData; -class DataDocument extends JsonSerializableValue +final class DataDocument extends JsonSerializableValue { public function __construct(PrimaryData $data, DataDocumentMember ...$documentMembers) { diff --git a/src/DataDocumentMember.php b/src/DataDocumentMember.php index 40a5096..ec4250c 100644 --- a/src/DataDocumentMember.php +++ b/src/DataDocumentMember.php @@ -1,7 +1,9 @@ name = $name; - } - - function attachTo(object $o) - { - $o->{$this->name} = $this; - } -} \ No newline at end of file diff --git a/src/Document/Resource/ResourceObject.php b/src/Document/Resource/ResourceObject.php deleted file mode 100644 index 351e5bc..0000000 --- a/src/Document/Resource/ResourceObject.php +++ /dev/null @@ -1,9 +0,0 @@ - Date: Wed, 21 Feb 2018 22:47:32 -0800 Subject: [PATCH 07/37] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index acc8573..6da70b0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +# !!! WORK IN PROGRESS !!! + + # Implementation of [JSON API](http://jsonapi.org) in PHP 7 This library is an attempt to express business rules of JSON API specification in a set of PHP 7 classes. From 89bfcffba648e980654026269564b6a378247363 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Wed, 21 Feb 2018 22:50:03 -0800 Subject: [PATCH 08/37] Tests pass --- src/ResourceIdentifier.php | 20 ++++++++++ test/DocumentTest.php | 81 ++++++++++++++++++++++++-------------- 2 files changed, 71 insertions(+), 30 deletions(-) create mode 100644 src/ResourceIdentifier.php diff --git a/src/ResourceIdentifier.php b/src/ResourceIdentifier.php new file mode 100644 index 0000000..1efe4ef --- /dev/null +++ b/src/ResourceIdentifier.php @@ -0,0 +1,20 @@ + $type, + 'id' => $id + ]; + parent::__construct('data', $obj); + } +} \ No newline at end of file diff --git a/test/DocumentTest.php b/test/DocumentTest.php index d416487..94db0f5 100644 --- a/test/DocumentTest.php +++ b/test/DocumentTest.php @@ -13,6 +13,7 @@ use JsonApiPhp\JsonApi\Meta; use JsonApiPhp\JsonApi\MetaDocument; use JsonApiPhp\JsonApi\PrimaryData\NullData; +use JsonApiPhp\JsonApi\ResourceIdentifier; class DocumentTest extends BaseTestCase { @@ -56,36 +57,6 @@ public function testMetaDocumentWithExtraMembers() ); } - public function testDataDocument() - { - $this->assertEncodesTo('{"data": null}', new DataDocument(new NullData())); - } - - public function testDataDocumentWithExtraMembers() - { - $this->assertEncodesTo( - ' - { - "data": null, - "meta": {"foo": "bar"}, - "jsonapi": { - "version": "1.0" - }, - "links": { - "self": "http://self" - } - } - ', - new DataDocument( - new NullData(), - new Meta(['foo' => 'bar']), - new JsonApi('1.0'), - new SelfLink( - new Url('http://self') - ) - ) - ); - } public function testErrorDocumentMayContainJustErrors() { @@ -136,4 +107,54 @@ public function testErrorDocumentMayContainExtraMembers() ) ); } + + public function testNullDocument() + { + $this->assertEncodesTo('{"data": null}', new DataDocument(new NullData())); + } + + public function testNullDocumentWithExtraMembers() + { + $this->assertEncodesTo( + ' + { + "data": null, + "meta": {"foo": "bar"}, + "jsonapi": { + "version": "1.0" + }, + "links": { + "self": "http://self" + } + } + ', + new DataDocument( + new NullData(), + new Meta(['foo' => 'bar']), + new JsonApi('1.0'), + new SelfLink( + new Url('http://self') + ) + ) + ); + } + + public function testSingleResourceIdentifierDocument() + { + $this->assertEncodesTo( + ' + { + "data": { + "type": "apples", + "id": "1", + } + } + ' + , + new DataDocument( + new ResourceIdentifier('apples', '1') + ) + ); + } + } \ No newline at end of file From a75ded2a18f7a1b0ae1c9916ad739b0ffbd85fb7 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Wed, 21 Feb 2018 22:57:39 -0800 Subject: [PATCH 09/37] Tests pass --- src/ResourceIdentifier.php | 9 ++++++--- test/DocumentTest.php | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/ResourceIdentifier.php b/src/ResourceIdentifier.php index 1efe4ef..471c61f 100644 --- a/src/ResourceIdentifier.php +++ b/src/ResourceIdentifier.php @@ -9,12 +9,15 @@ class ResourceIdentifier extends AttachableValue implements PrimaryData { - public function __construct(string $type, string $id) + public function __construct(string $type, string $id, Meta $meta = null) { - $obj = (object)[ + $identifier = (object)[ 'type' => $type, 'id' => $id ]; - parent::__construct('data', $obj); + if ($meta) { + $meta->attachTo($identifier); + } + parent::__construct('data', $identifier); } } \ No newline at end of file diff --git a/test/DocumentTest.php b/test/DocumentTest.php index 94db0f5..565e6f4 100644 --- a/test/DocumentTest.php +++ b/test/DocumentTest.php @@ -147,12 +147,13 @@ public function testSingleResourceIdentifierDocument() "data": { "type": "apples", "id": "1", + "meta": {"foo": "bar"} } } ' , new DataDocument( - new ResourceIdentifier('apples', '1') + new ResourceIdentifier('apples', '1', new Meta(['foo' => 'bar'])) ) ); } From b0d3f2da416fd426db68301b6194e71758f92408 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Wed, 21 Feb 2018 23:17:30 -0800 Subject: [PATCH 10/37] Tests pass --- src/Attachable.php | 4 +- src/AttachableValue.php | 8 ++-- src/DataDocument.php | 2 +- src/DataDocumentMember.php | 5 ++- src/DocumentMember.php | 5 +-- src/Error/AboutLink.php | 4 +- src/Error/Code.php | 6 +-- src/Error/Detail.php | 6 +-- src/Error/Error.php | 9 ++--- src/Error/ErrorMember.php | 7 ++-- src/Error/Id.php | 6 +-- src/Error/Source.php | 6 +-- src/Error/Source/Parameter.php | 6 +-- src/Error/Source/Pointer.php | 6 +-- src/Error/Source/SourceMember.php | 2 +- src/Error/Status.php | 6 +-- src/Error/Title.php | 6 +-- src/ErrorDocument.php | 5 +-- src/ErrorDocumentMember.php | 3 +- src/JsonApi.php | 10 ++--- src/JsonSerializableValue.php | 2 +- src/Link/AttachableLink.php | 9 ++--- src/Link/Link.php | 3 +- src/Link/LinkObject.php | 10 ++--- src/Link/SelfLink.php | 2 +- src/Link/Url.php | 6 +-- src/MandatoryErrorDocumentMember.php | 3 +- src/Meta.php | 7 ++-- src/MetaDocument.php | 2 +- src/PrimaryData/Attribute.php | 17 +++++++++ src/PrimaryData/NullData.php | 6 +-- src/PrimaryData/PrimaryData.php | 5 +-- src/PrimaryData/Resource.php | 18 +++++++++ .../ResourceId.php} | 15 ++++---- src/PrimaryData/ResourceMember.php | 13 +++++++ src/TopLevelDocumentMember.php | 2 +- src/functions.php | 4 +- test/DocumentTest.php | 37 ++++++++++++++++--- test/ErrorTest.php | 2 +- 39 files changed, 156 insertions(+), 119 deletions(-) create mode 100644 src/PrimaryData/Attribute.php create mode 100644 src/PrimaryData/Resource.php rename src/{ResourceIdentifier.php => PrimaryData/ResourceId.php} (55%) create mode 100644 src/PrimaryData/ResourceMember.php diff --git a/src/Attachable.php b/src/Attachable.php index cfb8175..420fad2 100644 --- a/src/Attachable.php +++ b/src/Attachable.php @@ -8,5 +8,5 @@ */ interface Attachable { - function attachTo(object $o); -} \ No newline at end of file + public function attachTo(object $o); +} diff --git a/src/AttachableValue.php b/src/AttachableValue.php index cbba91d..d1a09f8 100644 --- a/src/AttachableValue.php +++ b/src/AttachableValue.php @@ -6,9 +6,7 @@ /** * @internal */ -class AttachableValue - extends JsonSerializableValue - implements Attachable +class AttachableValue extends JsonSerializableValue implements Attachable { private $key; @@ -18,8 +16,8 @@ public function __construct(string $key, $value) $this->key = $key; } - function attachTo(object $o) + public function attachTo(object $o) { $o->{$this->key} = $this; } -} \ No newline at end of file +} diff --git a/src/DataDocument.php b/src/DataDocument.php index 6425e7c..6786c26 100644 --- a/src/DataDocument.php +++ b/src/DataDocument.php @@ -11,4 +11,4 @@ public function __construct(PrimaryData $data, DataDocumentMember ...$documentMe { parent::__construct(combine($data, ...$documentMembers)); } -} \ No newline at end of file +} diff --git a/src/DataDocumentMember.php b/src/DataDocumentMember.php index ec4250c..9227b07 100644 --- a/src/DataDocumentMember.php +++ b/src/DataDocumentMember.php @@ -1,4 +1,5 @@ -errors[] = $this; } -} \ No newline at end of file +} diff --git a/src/Error/ErrorMember.php b/src/Error/ErrorMember.php index df7aad6..7dd48de 100644 --- a/src/Error/ErrorMember.php +++ b/src/Error/ErrorMember.php @@ -1,11 +1,12 @@ - $version + $jsonapi = (object) [ + 'version' => $version, ]; if ($meta) { $meta->attachTo($jsonapi); @@ -18,4 +16,4 @@ public function __construct(string $version, Meta $meta = null) parent::__construct('jsonapi', $jsonapi); } -} \ No newline at end of file +} diff --git a/src/JsonSerializableValue.php b/src/JsonSerializableValue.php index 9c9c17b..1b70462 100644 --- a/src/JsonSerializableValue.php +++ b/src/JsonSerializableValue.php @@ -19,4 +19,4 @@ public function jsonSerialize() { return $this->value; } -} \ No newline at end of file +} diff --git a/src/Link/AttachableLink.php b/src/Link/AttachableLink.php index 2543bea..21a836e 100644 --- a/src/Link/AttachableLink.php +++ b/src/Link/AttachableLink.php @@ -8,19 +8,18 @@ /** * @internal */ -class AttachableLink - extends AttachableValue +class AttachableLink extends AttachableValue { public function __construct(string $key, Link $link) { parent::__construct($key, $link); } - function attachTo(object $o) + public function attachTo(object $o) { if (!isset($o->links)) { - $o->links = (object)[]; + $o->links = (object) []; } parent::attachTo($o->links); } -} \ No newline at end of file +} diff --git a/src/Link/Link.php b/src/Link/Link.php index 6e074f3..6848e74 100644 --- a/src/Link/Link.php +++ b/src/Link/Link.php @@ -8,5 +8,4 @@ */ interface Link { - -} \ No newline at end of file +} diff --git a/src/Link/LinkObject.php b/src/Link/LinkObject.php index fcc6070..6d6fed3 100644 --- a/src/Link/LinkObject.php +++ b/src/Link/LinkObject.php @@ -6,18 +6,16 @@ use JsonApiPhp\JsonApi\JsonSerializableValue; use JsonApiPhp\JsonApi\Meta; -final class LinkObject - extends JsonSerializableValue - implements Link +final class LinkObject extends JsonSerializableValue implements Link { public function __construct(string $href, Meta $meta = null) { - $link = (object)[ - 'href' => $href + $link = (object) [ + 'href' => $href, ]; if ($meta) { $meta->attachTo($link); } parent::__construct($link); } -} \ No newline at end of file +} diff --git a/src/Link/SelfLink.php b/src/Link/SelfLink.php index 8cbd8f5..ceb746a 100644 --- a/src/Link/SelfLink.php +++ b/src/Link/SelfLink.php @@ -11,4 +11,4 @@ public function __construct(Link $link) { parent::__construct('self', $link); } -} \ No newline at end of file +} diff --git a/src/Link/Url.php b/src/Link/Url.php index 5c22de0..3e3fa0b 100644 --- a/src/Link/Url.php +++ b/src/Link/Url.php @@ -5,12 +5,10 @@ use JsonApiPhp\JsonApi\JsonSerializableValue; -final class Url - extends JsonSerializableValue - implements Link +final class Url extends JsonSerializableValue implements Link { public function __construct(string $url) { parent::__construct($url); } -} \ No newline at end of file +} diff --git a/src/MandatoryErrorDocumentMember.php b/src/MandatoryErrorDocumentMember.php index fc28c2f..c35c907 100644 --- a/src/MandatoryErrorDocumentMember.php +++ b/src/MandatoryErrorDocumentMember.php @@ -8,5 +8,4 @@ */ interface MandatoryErrorDocumentMember extends ErrorDocumentMember { - -} \ No newline at end of file +} diff --git a/src/Meta.php b/src/Meta.php index 3ba2c0f..e3f8353 100644 --- a/src/Meta.php +++ b/src/Meta.php @@ -4,10 +4,9 @@ namespace JsonApiPhp\JsonApi; use JsonApiPhp\JsonApi\Error\ErrorMember; +use JsonApiPhp\JsonApi\PrimaryData\ResourceMember; -final class Meta - extends AttachableValue - implements ErrorMember, TopLevelDocumentMember, DataDocumentMember +final class Meta extends AttachableValue implements ErrorMember, TopLevelDocumentMember, DataDocumentMember, ResourceMember { /** * @param array|object $meta @@ -16,4 +15,4 @@ public function __construct($meta) { parent::__construct('meta', (object) $meta); } -} \ No newline at end of file +} diff --git a/src/MetaDocument.php b/src/MetaDocument.php index d9e0e31..be9ed4a 100644 --- a/src/MetaDocument.php +++ b/src/MetaDocument.php @@ -9,4 +9,4 @@ public function __construct(Meta $meta, TopLevelDocumentMember ...$members) { parent::__construct(combine($meta, ...$members)); } -} \ No newline at end of file +} diff --git a/src/PrimaryData/Attribute.php b/src/PrimaryData/Attribute.php new file mode 100644 index 0000000..0723696 --- /dev/null +++ b/src/PrimaryData/Attribute.php @@ -0,0 +1,17 @@ +attributes)) { + $o->attributes = (object) []; + } + parent::attachTo($o->attributes); + } +} diff --git a/src/PrimaryData/NullData.php b/src/PrimaryData/NullData.php index 59b61d0..83d22f3 100644 --- a/src/PrimaryData/NullData.php +++ b/src/PrimaryData/NullData.php @@ -5,12 +5,10 @@ use JsonApiPhp\JsonApi\AttachableValue; -final class NullData - extends AttachableValue - implements PrimaryData +final class NullData extends AttachableValue implements PrimaryData { public function __construct() { parent::__construct('data', null); } -} \ No newline at end of file +} diff --git a/src/PrimaryData/PrimaryData.php b/src/PrimaryData/PrimaryData.php index 90e2fad..1fc4210 100644 --- a/src/PrimaryData/PrimaryData.php +++ b/src/PrimaryData/PrimaryData.php @@ -8,7 +8,6 @@ /** * @internal */ -interface PrimaryData - extends DataDocumentMember +interface PrimaryData extends DataDocumentMember { -} \ No newline at end of file +} diff --git a/src/PrimaryData/Resource.php b/src/PrimaryData/Resource.php new file mode 100644 index 0000000..79853c1 --- /dev/null +++ b/src/PrimaryData/Resource.php @@ -0,0 +1,18 @@ +type = $type; + $obj->id = $id; + parent::__construct('data', $obj); + } +} diff --git a/src/ResourceIdentifier.php b/src/PrimaryData/ResourceId.php similarity index 55% rename from src/ResourceIdentifier.php rename to src/PrimaryData/ResourceId.php index 471c61f..4c09b68 100644 --- a/src/ResourceIdentifier.php +++ b/src/PrimaryData/ResourceId.php @@ -1,23 +1,22 @@ $type, - 'id' => $id + 'id' => $id, ]; if ($meta) { $meta->attachTo($identifier); } parent::__construct('data', $identifier); } -} \ No newline at end of file +} diff --git a/src/PrimaryData/ResourceMember.php b/src/PrimaryData/ResourceMember.php new file mode 100644 index 0000000..0c6cc3d --- /dev/null +++ b/src/PrimaryData/ResourceMember.php @@ -0,0 +1,13 @@ +attachTo($obj); } return $obj; -} \ No newline at end of file +} diff --git a/test/DocumentTest.php b/test/DocumentTest.php index 565e6f4..cdde168 100644 --- a/test/DocumentTest.php +++ b/test/DocumentTest.php @@ -12,8 +12,10 @@ use JsonApiPhp\JsonApi\Link\Url; use JsonApiPhp\JsonApi\Meta; use JsonApiPhp\JsonApi\MetaDocument; +use JsonApiPhp\JsonApi\PrimaryData\Attribute; use JsonApiPhp\JsonApi\PrimaryData\NullData; -use JsonApiPhp\JsonApi\ResourceIdentifier; +use JsonApiPhp\JsonApi\PrimaryData\Resource; +use JsonApiPhp\JsonApi\PrimaryData\ResourceId; class DocumentTest extends BaseTestCase { @@ -57,7 +59,6 @@ public function testMetaDocumentWithExtraMembers() ); } - public function testErrorDocumentMayContainJustErrors() { $this->assertEncodesTo( @@ -150,12 +151,36 @@ public function testSingleResourceIdentifierDocument() "meta": {"foo": "bar"} } } - ' - , + ', new DataDocument( - new ResourceIdentifier('apples', '1', new Meta(['foo' => 'bar'])) + new ResourceId('apples', '1', new Meta(['foo' => 'bar'])) ) ); } -} \ No newline at end of file + public function testSingleResourceObjectDocument() + { + $this->assertEncodesTo( + ' + { + "data": { + "type": "apples", + "id": "1", + "attributes": { + "title": "Rails is Omakase" + }, + "meta": {"foo": "bar"} + } + } + ', + new DataDocument( + new Resource( + 'apples', + '1', + new Meta(['foo' => 'bar']), + new Attribute('title', 'Rails is Omakase') + ) + ) + ); + } +} diff --git a/test/ErrorTest.php b/test/ErrorTest.php index 4d33667..190e327 100644 --- a/test/ErrorTest.php +++ b/test/ErrorTest.php @@ -63,4 +63,4 @@ public function testErrorWithFullSetOfProperties() ) ); } -} \ No newline at end of file +} From 77e0ab046147911ca26843293e81d4195d85751a Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Thu, 22 Feb 2018 18:49:29 -0800 Subject: [PATCH 11/37] Tests pass --- src/EmptySet.php | 16 +++++ src/PrimaryData/ResourceIdSet.php | 16 +++++ .../{Resource.php => ResourceObject.php} | 2 +- src/PrimaryData/ResourceSet.php | 16 +++++ test/DocumentTest.php | 71 ++++++++++++++++++- 5 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 src/EmptySet.php create mode 100644 src/PrimaryData/ResourceIdSet.php rename src/PrimaryData/{Resource.php => ResourceObject.php} (85%) create mode 100644 src/PrimaryData/ResourceSet.php diff --git a/src/EmptySet.php b/src/EmptySet.php new file mode 100644 index 0000000..db6f19b --- /dev/null +++ b/src/EmptySet.php @@ -0,0 +1,16 @@ + 'bar']), @@ -183,4 +186,68 @@ public function testSingleResourceObjectDocument() ) ); } + + public function testEmptySetDocument() + { + $this->assertEncodesTo( + ' + { + "data": [] + } + ', + new DataDocument( + new EmptySet() + ) + ); + } + + public function testResourceSetDocument() + { + $this->assertEncodesTo( + ' + { + "data": [ + { + "type": "apples", + "id": "1" + }, + { + "type": "pears", + "id": "2" + } + ] + } ', + new DataDocument( + new ResourceSet( + new ResourceObject('apples', '1'), + new ResourceObject('pears', '2') + ) + ) + ); + } + + public function testResourceIdSetDocument() + { + $this->assertEncodesTo( + ' + { + "data": [ + { + "type": "apples", + "id": "1" + }, + { + "type": "pears", + "id": "2" + } + ] + } ', + new DataDocument( + new ResourceIdSet( + new ResourceId('apples', '1'), + new ResourceId('pears', '2') + ) + ) + ); + } } From c04be9545613647f9da8fcc6d32238cfbba7f9f3 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Thu, 22 Feb 2018 19:11:08 -0800 Subject: [PATCH 12/37] Tests pass --- .travis.yml | 2 +- src/Link/SelfLink.php | 3 ++- src/Meta.php | 4 +++- src/Relationship.php | 16 +++++++++++++ src/RelationshipMember.php | 12 ++++++++++ test/ResourceObjectTest.php | 48 +++++++++++++++++++++++++++++++++++++ 6 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 src/Relationship.php create mode 100644 src/RelationshipMember.php create mode 100644 test/ResourceObjectTest.php diff --git a/.travis.yml b/.travis.yml index 6bbcb4b..d07a6a5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,10 @@ language: php php: - - '7.1' - '7.2' before_script: - composer install + - mkdir build/logs script: - vendor/bin/php-cs-fixer fix -v --dry-run diff --git a/src/Link/SelfLink.php b/src/Link/SelfLink.php index ceb746a..9f706c2 100644 --- a/src/Link/SelfLink.php +++ b/src/Link/SelfLink.php @@ -4,8 +4,9 @@ namespace JsonApiPhp\JsonApi\Link; use JsonApiPhp\JsonApi\DataDocumentMember; +use JsonApiPhp\JsonApi\PrimaryData\ResourceMember; -final class SelfLink extends AttachableLink implements DataDocumentMember +final class SelfLink extends AttachableLink implements DataDocumentMember, ResourceMember { public function __construct(Link $link) { diff --git a/src/Meta.php b/src/Meta.php index e3f8353..51c4519 100644 --- a/src/Meta.php +++ b/src/Meta.php @@ -6,7 +6,9 @@ use JsonApiPhp\JsonApi\Error\ErrorMember; use JsonApiPhp\JsonApi\PrimaryData\ResourceMember; -final class Meta extends AttachableValue implements ErrorMember, TopLevelDocumentMember, DataDocumentMember, ResourceMember +final class Meta + extends AttachableValue + implements ErrorMember, TopLevelDocumentMember, DataDocumentMember, ResourceMember, RelationshipMember { /** * @param array|object $meta diff --git a/src/Relationship.php b/src/Relationship.php new file mode 100644 index 0000000..da2e8f8 --- /dev/null +++ b/src/Relationship.php @@ -0,0 +1,16 @@ +assertEncodesTo( + ' + { + "type": "apples", + "id": "1", + "attributes": { + "title": "Rails is Omakase" + }, + "meta": {"foo": "bar"}, + "links": { + "self": "http://self" + }, + "relationships": { + "meta": {"foo": "bar"} + } + } + ', + new ResourceObject( + 'apples', + '1', + new Meta(['foo' => 'bar']), + new Attribute('title', 'Rails is Omakase'), + new SelfLink(new Url('http://self')), + new Relationship( + new Meta(['foo' => 'bar']) + ) + ) + ); + + } + +} From be76f3fbc5343e531602e5072e3292d2a1d61c62 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Thu, 22 Feb 2018 19:18:24 -0800 Subject: [PATCH 13/37] Tests pass --- src/Link/RelatedLink.php | 16 ++++++++++++++++ src/Link/SelfLink.php | 5 ++++- src/Relationship.php | 14 ++++++++++++-- test/ResourceObjectTest.php | 14 ++++++++++++-- 4 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 src/Link/RelatedLink.php diff --git a/src/Link/RelatedLink.php b/src/Link/RelatedLink.php new file mode 100644 index 0000000..4d018d5 --- /dev/null +++ b/src/Link/RelatedLink.php @@ -0,0 +1,16 @@ +relationships)) { + $o->relationships = (object)[]; + } + parent::attachTo($o->relationships); // TODO: Change the autogenerated stub + } + + } \ No newline at end of file diff --git a/test/ResourceObjectTest.php b/test/ResourceObjectTest.php index be61673..488787a 100644 --- a/test/ResourceObjectTest.php +++ b/test/ResourceObjectTest.php @@ -3,6 +3,7 @@ namespace JsonApiPhp\JsonApi\Test; +use JsonApiPhp\JsonApi\Link\RelatedLink; use JsonApiPhp\JsonApi\Link\SelfLink; use JsonApiPhp\JsonApi\Link\Url; use JsonApiPhp\JsonApi\Meta; @@ -27,7 +28,13 @@ public function testFullFledgedResourceObject() "self": "http://self" }, "relationships": { - "meta": {"foo": "bar"} + "author": { + "meta": {"foo": "bar"}, + "links": { + "self": "http://rel/author", + "related": "http://author" + } + } } } ', @@ -38,7 +45,10 @@ public function testFullFledgedResourceObject() new Attribute('title', 'Rails is Omakase'), new SelfLink(new Url('http://self')), new Relationship( - new Meta(['foo' => 'bar']) + 'author', + new Meta(['foo' => 'bar']), + new SelfLink(new Url('http://rel/author')), + new RelatedLink(new Url('http://author')) ) ) ); From b0dd13688a05d648fcac169b4d258b214521308c Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Thu, 22 Feb 2018 19:27:19 -0800 Subject: [PATCH 14/37] Tests pass --- src/Linkage/NullLinkage.php | 17 +++++++++++++++++ test/ResourceObjectTest.php | 7 +++++-- 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 src/Linkage/NullLinkage.php diff --git a/src/Linkage/NullLinkage.php b/src/Linkage/NullLinkage.php new file mode 100644 index 0000000..a7118fb --- /dev/null +++ b/src/Linkage/NullLinkage.php @@ -0,0 +1,17 @@ + 'bar']), new SelfLink(new Url('http://rel/author')), - new RelatedLink(new Url('http://author')) + new RelatedLink(new Url('http://author')), + new NullLinkage() ) ) ); From 20c53db5639d82def7a0db2c64abed354f6d7db0 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Thu, 22 Feb 2018 19:37:30 -0800 Subject: [PATCH 15/37] Tests pass --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d07a6a5..bef7869 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ php: before_script: - composer install - - mkdir build/logs + - mkdir build/logs -p script: - vendor/bin/php-cs-fixer fix -v --dry-run From 0452ea7466b81c74a3443a0b30e6b4a31fb7bbe8 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Thu, 22 Feb 2018 22:35:02 -0800 Subject: [PATCH 16/37] Tests pass --- src/{Error => }/Error.php | 6 +- .../{NullLinkage.php => MultiLinkage.php} | 7 +- src/Linkage/SingleLinkage.php | 18 ++++++ test/DocumentTest.php | 2 +- test/ErrorTest.php | 2 +- test/ResourceObjectTest.php | 64 ++++++++++++++++++- 6 files changed, 88 insertions(+), 11 deletions(-) rename src/{Error => }/Error.php (65%) rename src/Linkage/{NullLinkage.php => MultiLinkage.php} (57%) create mode 100644 src/Linkage/SingleLinkage.php diff --git a/src/Error/Error.php b/src/Error.php similarity index 65% rename from src/Error/Error.php rename to src/Error.php index a5b68f5..fa8bdf5 100644 --- a/src/Error/Error.php +++ b/src/Error.php @@ -1,11 +1,9 @@ 'bar']), new SelfLink(new Url('http://rel/author')), new RelatedLink(new Url('http://author')), - new NullLinkage() + new SingleLinkage() ) ) ); + } + + public function testRelationshipWithSingleIdLinkage() + { + $this->assertEncodesTo( + ' + { + "data": { + "type": "apples", + "id": "1" + } + } + ', + new Relationship( + 'fruits', + new SingleLinkage( + new ResourceId('apples', '1') + ) + ) + ); + } + + public function testRelationshipWithMultiIdLinkage() + { + $this->assertEncodesTo( + ' + { + "data": [{ + "type": "apples", + "id": "1" + },{ + "type": "pears", + "id": "2" + }] + } + ', + new Relationship( + 'fruits', + new MultiLinkage( + new ResourceId('apples', '1'), + new ResourceId('pears', '2') + ) + ) + ); + } + public function testRelationshipWithEmptyMultiIdLinkage() + { + $this->assertEncodesTo( + ' + { + "data": [] + } + ', + new Relationship( + 'fruits', + new MultiLinkage() + ) + ); } } From 27927a4abad9f9088ad88c5145673e1a9a76d8a7 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Thu, 22 Feb 2018 23:23:44 -0800 Subject: [PATCH 17/37] Tests pass --- composer.json | 2 +- src/EmptySet.php | 6 +- src/Included.php | 14 +++ src/Link/FirstLink.php | 16 +++ src/Link/LastLink.php | 16 +++ src/Link/NextLink.php | 16 +++ src/Link/RelatedLink.php | 4 +- src/Link/SelfLink.php | 4 +- src/Linkage/MultiLinkage.php | 6 +- src/Linkage/SingleLinkage.php | 6 +- src/Meta.php | 4 +- src/PrimaryData/ResourceIdSet.php | 6 +- src/PrimaryData/ResourceObject.php | 12 ++- src/PrimaryData/ResourceSet.php | 6 +- src/Relationship.php | 12 +-- src/RelationshipMember.php | 3 +- test/CompoundDocumentTest.php | 161 +++++++++++++++++++++++++++++ test/ResourceObjectTest.php | 1 - 18 files changed, 252 insertions(+), 43 deletions(-) create mode 100644 src/Included.php create mode 100644 src/Link/FirstLink.php create mode 100644 src/Link/LastLink.php create mode 100644 src/Link/NextLink.php create mode 100644 test/CompoundDocumentTest.php diff --git a/composer.json b/composer.json index 6ad6230..a83d94f 100644 --- a/composer.json +++ b/composer.json @@ -31,6 +31,6 @@ } }, "scripts": { - "test": "php-cs-fixer fix -v --dry-run --ansi && phpunit --colors=always --coverage-text && vendor/bin/doc2test && vendor/bin/phpunit -c doc-test/phpunit.xml --coverage-text" + "test": "php-cs-fixer fix -v --dry-run --ansi && phpunit --colors=always --coverage-text" } } diff --git a/src/EmptySet.php b/src/EmptySet.php index db6f19b..74e37f8 100644 --- a/src/EmptySet.php +++ b/src/EmptySet.php @@ -5,12 +5,10 @@ use JsonApiPhp\JsonApi\PrimaryData\PrimaryData; -final class EmptySet - extends AttachableValue - implements PrimaryData +final class EmptySet extends AttachableValue implements PrimaryData { public function __construct() { parent::__construct('data', []); } -} \ No newline at end of file +} diff --git a/src/Included.php b/src/Included.php new file mode 100644 index 0000000..edf62fc --- /dev/null +++ b/src/Included.php @@ -0,0 +1,14 @@ +type = $type; - $obj->id = $id; + $obj->type = $this->type = $type; + $obj->id = $this->id = $id; parent::__construct('data', $obj); } + + public function toResourceId(): ResourceId + { + return new ResourceId($this->type, $this->id); + } } diff --git a/src/PrimaryData/ResourceSet.php b/src/PrimaryData/ResourceSet.php index 2f25a6e..0632ca4 100644 --- a/src/PrimaryData/ResourceSet.php +++ b/src/PrimaryData/ResourceSet.php @@ -5,12 +5,10 @@ use JsonApiPhp\JsonApi\AttachableValue; -final class ResourceSet - extends AttachableValue - implements PrimaryData +final class ResourceSet extends AttachableValue implements PrimaryData { public function __construct(ResourceObject $resource, ResourceObject ...$resources) { parent::__construct('data', func_get_args()); } -} \ No newline at end of file +} diff --git a/src/Relationship.php b/src/Relationship.php index 94b52b5..22714e1 100644 --- a/src/Relationship.php +++ b/src/Relationship.php @@ -5,9 +5,7 @@ use JsonApiPhp\JsonApi\PrimaryData\ResourceMember; -final class Relationship - extends AttachableValue - implements ResourceMember +final class Relationship extends AttachableValue implements ResourceMember { public function __construct(string $name, RelationshipMember $member, RelationshipMember ...$members) { @@ -16,11 +14,9 @@ public function __construct(string $name, RelationshipMember $member, Relationsh public function attachTo(object $o) { - if(empty($o->relationships)) { - $o->relationships = (object)[]; + if (empty($o->relationships)) { + $o->relationships = (object) []; } parent::attachTo($o->relationships); // TODO: Change the autogenerated stub } - - -} \ No newline at end of file +} diff --git a/src/RelationshipMember.php b/src/RelationshipMember.php index 2d383a0..7ffbc26 100644 --- a/src/RelationshipMember.php +++ b/src/RelationshipMember.php @@ -8,5 +8,4 @@ */ interface RelationshipMember extends DocumentMember { - -} \ No newline at end of file +} diff --git a/test/CompoundDocumentTest.php b/test/CompoundDocumentTest.php new file mode 100644 index 0000000..7eace77 --- /dev/null +++ b/test/CompoundDocumentTest.php @@ -0,0 +1,161 @@ +toResourceId())) + ); + + $document = new DataDocument( + new ResourceSet( + new ResourceObject( + 'articles', + '1', + new Attribute('title', 'JSON API paints my bikeshed!'), + new SelfLink(new Url('http://example.com/articles/1')), + new Relationship( + 'author', + new SingleLinkage($dan->toResourceId()), + new SelfLink(new Url('http://example.com/articles/1/relationships/author')), + new RelatedLink(new Url('http://example.com/articles/1/author')) + ), + new Relationship( + 'comments', + new MultiLinkage( + $comment05->toResourceId(), + $comment12->toResourceId() + ), + new SelfLink(new Url('http://example.com/articles/1/relationships/comments')), + new RelatedLink(new Url('http://example.com/articles/1/comments')) + ) + ) + ), + new SelfLink(new Url('http://example.com/articles')), + new NextLink(new Url('http://example.com/articles?page[offset]=2')), + new LastLink(new Url('http://example.com/articles?page[offset]=10')), + new Included($dan, $comment05, $comment12) + ); + $this->assertEncodesTo( + ' + { + "links": { + "self": "http://example.com/articles", + "next": "http://example.com/articles?page[offset]=2", + "last": "http://example.com/articles?page[offset]=10" + }, + "data": [{ + "type": "articles", + "id": "1", + "attributes": { + "title": "JSON API paints my bikeshed!" + }, + "links": { + "self": "http://example.com/articles/1" + }, + "relationships": { + "author": { + "links": { + "self": "http://example.com/articles/1/relationships/author", + "related": "http://example.com/articles/1/author" + }, + "data": { "type": "people", "id": "9" } + }, + "comments": { + "links": { + "self": "http://example.com/articles/1/relationships/comments", + "related": "http://example.com/articles/1/comments" + }, + "data": [ + { "type": "comments", "id": "5" }, + { "type": "comments", "id": "12" } + ] + } + } + }], + "included": [{ + "type": "people", + "id": "9", + "attributes": { + "first-name": "Dan", + "last-name": "Gebhardt", + "twitter": "dgeb" + }, + "links": { + "self": "http://example.com/people/9" + } + }, { + "type": "comments", + "id": "5", + "attributes": { + "body": "First!" + }, + "relationships": { + "author": { + "data": { "type": "people", "id": "2" } + } + }, + "links": { + "self": "http://example.com/comments/5" + } + }, { + "type": "comments", + "id": "12", + "attributes": { + "body": "I like XML better" + }, + "relationships": { + "author": { + "data": { "type": "people", "id": "9" } + } + }, + "links": { + "self": "http://example.com/comments/12" + } + }] + } + ', + $document + ); + } +} diff --git a/test/ResourceObjectTest.php b/test/ResourceObjectTest.php index cfa7071..282be3f 100644 --- a/test/ResourceObjectTest.php +++ b/test/ResourceObjectTest.php @@ -117,5 +117,4 @@ public function testRelationshipWithEmptyMultiIdLinkage() ) ); } - } From 797e74a368ec543121d8e3d0609b4bc06c178106 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Thu, 22 Feb 2018 23:54:34 -0800 Subject: [PATCH 18/37] Tests pass --- src/PrimaryData/Attribute.php | 4 +--- src/PrimaryData/ResourceField.php | 25 +++++++++++++++++++++++++ src/Relationship.php | 12 ++++++------ test/ResourceObjectTest.php | 29 +++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 src/PrimaryData/ResourceField.php diff --git a/src/PrimaryData/Attribute.php b/src/PrimaryData/Attribute.php index 0723696..0679177 100644 --- a/src/PrimaryData/Attribute.php +++ b/src/PrimaryData/Attribute.php @@ -3,9 +3,7 @@ namespace JsonApiPhp\JsonApi\PrimaryData; -use JsonApiPhp\JsonApi\AttachableValue; - -class Attribute extends AttachableValue implements ResourceMember +class Attribute extends ResourceField { public function attachTo(object $o) { diff --git a/src/PrimaryData/ResourceField.php b/src/PrimaryData/ResourceField.php new file mode 100644 index 0000000..d33a54c --- /dev/null +++ b/src/PrimaryData/ResourceField.php @@ -0,0 +1,25 @@ +isReservedWord($key)) { + throw new \DomainException("Can not use '$key' as a resource field"); + } + parent::__construct($key, $value); + } + + private function isReservedWord(string $key): bool + { + return in_array($key, ['id', 'type']); + } +} \ No newline at end of file diff --git a/src/Relationship.php b/src/Relationship.php index 22714e1..82fb4b6 100644 --- a/src/Relationship.php +++ b/src/Relationship.php @@ -3,20 +3,20 @@ namespace JsonApiPhp\JsonApi; -use JsonApiPhp\JsonApi\PrimaryData\ResourceMember; +use JsonApiPhp\JsonApi\PrimaryData\ResourceField; -final class Relationship extends AttachableValue implements ResourceMember +final class Relationship extends ResourceField { - public function __construct(string $name, RelationshipMember $member, RelationshipMember ...$members) + public function __construct(string $key, RelationshipMember $member, RelationshipMember ...$members) { - parent::__construct($name, combine($member, ...$members)); + parent::__construct($key, combine($member, ...$members)); } public function attachTo(object $o) { if (empty($o->relationships)) { - $o->relationships = (object) []; + $o->relationships = (object)[]; } - parent::attachTo($o->relationships); // TODO: Change the autogenerated stub + parent::attachTo($o->relationships); } } diff --git a/test/ResourceObjectTest.php b/test/ResourceObjectTest.php index 282be3f..8c83d38 100644 --- a/test/ResourceObjectTest.php +++ b/test/ResourceObjectTest.php @@ -3,6 +3,7 @@ namespace JsonApiPhp\JsonApi\Test; +use JsonApiPhp\JsonApi\AttachableValue; use JsonApiPhp\JsonApi\Link\RelatedLink; use JsonApiPhp\JsonApi\Link\SelfLink; use JsonApiPhp\JsonApi\Link\Url; @@ -117,4 +118,32 @@ public function testRelationshipWithEmptyMultiIdLinkage() ) ); } + + public function testCanNotCreateIdAttribute() + { + $this->expectException(\DomainException::class); + $this->expectExceptionMessage("Can not use 'id' as a resource field"); + new Attribute('id', 'foo'); + } + + public function testCanNotCreateTypeAttribute() + { + $this->expectException(\DomainException::class); + $this->expectExceptionMessage("Can not use 'type' as a resource field"); + new Attribute('type', 'foo'); + } + + public function testCanNotCreateIdRelationship() + { + $this->expectException(\DomainException::class); + $this->expectExceptionMessage("Can not use 'id' as a resource field"); + new Relationship('id', new Meta([])); + } + + public function testCanNotCreateTypeRelationship() + { + $this->expectException(\DomainException::class); + $this->expectExceptionMessage("Can not use 'type' as a resource field"); + new Relationship('type', new Meta([])); + } } From 1bd2be05670d0e34cd3c289b1398acd3da5912cb Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Thu, 22 Feb 2018 23:55:51 -0800 Subject: [PATCH 19/37] Tests pass --- src/PrimaryData/ResourceField.php | 2 +- src/Relationship.php | 2 +- test/ResourceObjectTest.php | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/PrimaryData/ResourceField.php b/src/PrimaryData/ResourceField.php index d33a54c..98a091e 100644 --- a/src/PrimaryData/ResourceField.php +++ b/src/PrimaryData/ResourceField.php @@ -22,4 +22,4 @@ private function isReservedWord(string $key): bool { return in_array($key, ['id', 'type']); } -} \ No newline at end of file +} diff --git a/src/Relationship.php b/src/Relationship.php index 82fb4b6..8f157c6 100644 --- a/src/Relationship.php +++ b/src/Relationship.php @@ -15,7 +15,7 @@ public function __construct(string $key, RelationshipMember $member, Relationshi public function attachTo(object $o) { if (empty($o->relationships)) { - $o->relationships = (object)[]; + $o->relationships = (object) []; } parent::attachTo($o->relationships); } diff --git a/test/ResourceObjectTest.php b/test/ResourceObjectTest.php index 8c83d38..bd6b513 100644 --- a/test/ResourceObjectTest.php +++ b/test/ResourceObjectTest.php @@ -3,7 +3,6 @@ namespace JsonApiPhp\JsonApi\Test; -use JsonApiPhp\JsonApi\AttachableValue; use JsonApiPhp\JsonApi\Link\RelatedLink; use JsonApiPhp\JsonApi\Link\SelfLink; use JsonApiPhp\JsonApi\Link\Url; From d6ef0075af6e68f5668e19866a0192d1ffd5a5d4 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Fri, 23 Feb 2018 00:16:16 -0800 Subject: [PATCH 20/37] Tests pass --- src/PrimaryData/ResourceField.php | 8 ++++++++ src/PrimaryData/ResourceObject.php | 17 ++++++++++++++++- test/ResourceObjectTest.php | 12 ++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/PrimaryData/ResourceField.php b/src/PrimaryData/ResourceField.php index 98a091e..f60cd03 100644 --- a/src/PrimaryData/ResourceField.php +++ b/src/PrimaryData/ResourceField.php @@ -10,12 +10,20 @@ */ abstract class ResourceField extends AttachableValue implements ResourceMember { + private $key; + public function __construct(string $key, $value) { if ($this->isReservedWord($key)) { throw new \DomainException("Can not use '$key' as a resource field"); } parent::__construct($key, $value); + $this->key = $key; + } + + public function toKey(): string + { + return $this->key; } private function isReservedWord(string $key): bool diff --git a/src/PrimaryData/ResourceObject.php b/src/PrimaryData/ResourceObject.php index 63f1beb..0db6fa3 100644 --- a/src/PrimaryData/ResourceObject.php +++ b/src/PrimaryData/ResourceObject.php @@ -6,13 +6,14 @@ use JsonApiPhp\JsonApi\AttachableValue; use function JsonApiPhp\JsonApi\combine; -class ResourceObject extends AttachableValue implements PrimaryData +final class ResourceObject extends AttachableValue implements PrimaryData { private $type; private $id; public function __construct(string $type, string $id, ResourceMember ...$members) { + $this->checkUniqueness(...$members); $obj = combine(...$members); $obj->type = $this->type = $type; $obj->id = $this->id = $id; @@ -23,4 +24,18 @@ public function toResourceId(): ResourceId { return new ResourceId($this->type, $this->id); } + + private function checkUniqueness(ResourceMember ...$members): void + { + $keys = []; + foreach ($members as $member) { + if ($member instanceof ResourceField) { + $key = $member->toKey(); + if (isset($keys[$key])) { + throw new \DomainException("Field '$key' already exists'"); + } + $keys[$key] = true; + } + } + } } diff --git a/test/ResourceObjectTest.php b/test/ResourceObjectTest.php index bd6b513..d0653c8 100644 --- a/test/ResourceObjectTest.php +++ b/test/ResourceObjectTest.php @@ -145,4 +145,16 @@ public function testCanNotCreateTypeRelationship() $this->expectExceptionMessage("Can not use 'type' as a resource field"); new Relationship('type', new Meta([])); } + + public function testResourceFields() + { + $this->expectException(\DomainException::class); + $this->expectExceptionMessage("Field 'foo' already exists"); + new ResourceObject( + 'apples', + '1', + new Attribute('foo', 'bar'), + new Relationship('foo', new Meta([])) + ); + } } From 9c02547bb2e4a1652fd49a6e83ea4ddbd5118e01 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Sat, 24 Feb 2018 09:20:32 -0800 Subject: [PATCH 21/37] Tests pass --- src/CompoundDocument.php | 21 +++++++++++++++++++ src/DataDocument.php | 10 +++++++-- src/EmptySet.php | 6 ++++++ src/Error/Code.php | 2 +- src/Error/Detail.php | 2 +- src/Error/Id.php | 6 +++--- src/Error/Source/Parameter.php | 2 +- src/Error/Status.php | 2 +- src/Error/Title.php | 2 +- src/Included.php | 6 +++++- src/Linkage/MultiLinkage.php | 24 +++++++++++++++++++--- src/Linkage/SingleLinkage.php | 16 ++++++++++++--- src/PrimaryData/Identifier.php | 12 +++++++++++ src/PrimaryData/NullData.php | 5 +++++ src/PrimaryData/PrimaryData.php | 2 +- src/PrimaryData/ResourceField.php | 2 +- src/PrimaryData/ResourceId.php | 14 +++++++++++++ src/PrimaryData/ResourceIdSet.php | 20 ++++++++++++++++-- src/PrimaryData/ResourceObject.php | 20 ++++++++++++++++++ src/PrimaryData/ResourceSet.php | 17 ++++++++++++++- src/Relationship.php | 23 ++++++++++++++++++++- src/functions.php | 2 +- test/CompoundDocumentTest.php | 20 +++++++++++++++--- test/DocumentTest.php | 2 +- test/ResourceObjectTest.php | 8 ++++---- tmp.js | 33 ++++++++++++++++++++++++++++++ 26 files changed, 247 insertions(+), 32 deletions(-) create mode 100644 src/CompoundDocument.php create mode 100644 src/PrimaryData/Identifier.php create mode 100644 tmp.js diff --git a/src/CompoundDocument.php b/src/CompoundDocument.php new file mode 100644 index 0000000..8cd3f13 --- /dev/null +++ b/src/CompoundDocument.php @@ -0,0 +1,21 @@ +enforceFullLinkage($data, $included); + parent::__construct(combine($data, $included, ...$members)); + } + + private function enforceFullLinkage(PrimaryData $data, Included $included) + { + + } +} \ No newline at end of file diff --git a/src/DataDocument.php b/src/DataDocument.php index 6786c26..5b460c6 100644 --- a/src/DataDocument.php +++ b/src/DataDocument.php @@ -3,12 +3,18 @@ namespace JsonApiPhp\JsonApi; +use JsonApiPhp\JsonApi\PrimaryData\Identifier; use JsonApiPhp\JsonApi\PrimaryData\PrimaryData; final class DataDocument extends JsonSerializableValue { - public function __construct(PrimaryData $data, DataDocumentMember ...$documentMembers) + /** + * @var Identifier[] + */ + private $identifiers = []; + + public function __construct(PrimaryData $data, DataDocumentMember ...$members) { - parent::__construct(combine($data, ...$documentMembers)); + parent::__construct(combine($data, ...$members)); } } diff --git a/src/EmptySet.php b/src/EmptySet.php index 74e37f8..eecc272 100644 --- a/src/EmptySet.php +++ b/src/EmptySet.php @@ -4,6 +4,7 @@ namespace JsonApiPhp\JsonApi; use JsonApiPhp\JsonApi\PrimaryData\PrimaryData; +use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; final class EmptySet extends AttachableValue implements PrimaryData { @@ -11,4 +12,9 @@ public function __construct() { parent::__construct('data', []); } + + public function identifies(ResourceObject $resource): bool + { + return false; + } } diff --git a/src/Error/Code.php b/src/Error/Code.php index b334e7a..8c9ab25 100644 --- a/src/Error/Code.php +++ b/src/Error/Code.php @@ -8,7 +8,7 @@ final class Code extends AttachableValue implements ErrorMember { /** - * @param string $code an application-specific error code, expressed as a string value + * @param string $code an application-specific error code, expressed as age string value */ public function __construct(string $code) { diff --git a/src/Error/Detail.php b/src/Error/Detail.php index f739353..af14623 100644 --- a/src/Error/Detail.php +++ b/src/Error/Detail.php @@ -8,7 +8,7 @@ final class Detail extends AttachableValue implements ErrorMember { /** - * @param string $detail a human-readable explanation specific to this occurrence of the problem. + * @param string $detail age human-readable explanation specific to this occurrence of the problem. */ public function __construct(string $detail) { diff --git a/src/Error/Id.php b/src/Error/Id.php index 8004699..f7cfc05 100644 --- a/src/Error/Id.php +++ b/src/Error/Id.php @@ -8,10 +8,10 @@ final class Id extends AttachableValue implements ErrorMember { /** - * @param string $id a unique identifier for this particular occurrence of the problem + * @param string $identifier age unique identifier for this particular occurrence of the problem */ - public function __construct(string $id) + public function __construct(string $identifier) { - parent::__construct('id', $id); + parent::__construct('id', $identifier); } } diff --git a/src/Error/Source/Parameter.php b/src/Error/Source/Parameter.php index 40a131f..04b77cc 100644 --- a/src/Error/Source/Parameter.php +++ b/src/Error/Source/Parameter.php @@ -8,7 +8,7 @@ final class Parameter extends AttachableValue implements SourceMember { /** - * @param string $parameter a string indicating which URI query parameter caused the error. + * @param string $parameter age string indicating which URI query parameter caused the error. */ public function __construct(string $parameter) { diff --git a/src/Error/Status.php b/src/Error/Status.php index f939335..b228fff 100644 --- a/src/Error/Status.php +++ b/src/Error/Status.php @@ -8,7 +8,7 @@ final class Status extends AttachableValue implements ErrorMember { /** - * @param string $status the HTTP status code applicable to this problem, expressed as a string value + * @param string $status the HTTP status code applicable to this problem, expressed as age string value */ public function __construct(string $status) { diff --git a/src/Error/Title.php b/src/Error/Title.php index 8db1962..0ee19df 100644 --- a/src/Error/Title.php +++ b/src/Error/Title.php @@ -8,7 +8,7 @@ class Title extends AttachableValue implements ErrorMember { /** - * @param string $title a short, human-readable summary of the problem that SHOULD NOT change from occurrence + * @param string $title age short, human-readable summary of the problem that SHOULD NOT change from occurrence * to occurrence of the problem, except for purposes of localization */ public function __construct(string $title) diff --git a/src/Included.php b/src/Included.php index edf62fc..e53a659 100644 --- a/src/Included.php +++ b/src/Included.php @@ -4,11 +4,15 @@ namespace JsonApiPhp\JsonApi; use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; +use Traversable; -class Included extends AttachableValue implements DataDocumentMember +final class Included extends AttachableValue implements DataDocumentMember { + private $resources = []; + public function __construct(ResourceObject ...$resources) { parent::__construct('included', $resources); + $this->resources = $resources; } } diff --git a/src/Linkage/MultiLinkage.php b/src/Linkage/MultiLinkage.php index f935b6e..ead7259 100644 --- a/src/Linkage/MultiLinkage.php +++ b/src/Linkage/MultiLinkage.php @@ -4,13 +4,31 @@ namespace JsonApiPhp\JsonApi\Linkage; use JsonApiPhp\JsonApi\AttachableValue; +use JsonApiPhp\JsonApi\PrimaryData\Identifier; use JsonApiPhp\JsonApi\PrimaryData\ResourceId; +use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; use JsonApiPhp\JsonApi\RelationshipMember; -class MultiLinkage extends AttachableValue implements RelationshipMember +class MultiLinkage extends AttachableValue implements RelationshipMember, Identifier { - public function __construct(ResourceId ...$resources) + /** + * @var \JsonApiPhp\JsonApi\PrimaryData\ResourceId[] + */ + private $identifiers = []; + + public function __construct(ResourceId ...$identifiers) + { + parent::__construct('data', $identifiers); + $this->identifiers = $identifiers; + } + + public function identifies(ResourceObject $resource): bool { - parent::__construct('data', $resources); + foreach ($this->identifiers as $identifier) { + if ($identifier->identifies($resource)) { + return true; + } + } + return false; } } diff --git a/src/Linkage/SingleLinkage.php b/src/Linkage/SingleLinkage.php index 9b729cd..7ec91f1 100644 --- a/src/Linkage/SingleLinkage.php +++ b/src/Linkage/SingleLinkage.php @@ -4,13 +4,23 @@ namespace JsonApiPhp\JsonApi\Linkage; use JsonApiPhp\JsonApi\AttachableValue; +use JsonApiPhp\JsonApi\PrimaryData\Identifier; use JsonApiPhp\JsonApi\PrimaryData\ResourceId; +use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; use JsonApiPhp\JsonApi\RelationshipMember; -class SingleLinkage extends AttachableValue implements RelationshipMember +class SingleLinkage extends AttachableValue implements RelationshipMember, Identifier { - public function __construct(ResourceId $resource = null) + private $identifier; + + public function __construct(ResourceId $identifier = null) + { + parent::__construct('data', $identifier); + $this->identifier = $identifier; + } + + public function identifies(ResourceObject $resource): bool { - parent::__construct('data', $resource); + return $this->identifier->identifies($resource); } } diff --git a/src/PrimaryData/Identifier.php b/src/PrimaryData/Identifier.php new file mode 100644 index 0000000..9f3e8c2 --- /dev/null +++ b/src/PrimaryData/Identifier.php @@ -0,0 +1,12 @@ +isReservedWord($key)) { - throw new \DomainException("Can not use '$key' as a resource field"); + throw new \DomainException("Can not use '$key' as age resource field"); } parent::__construct($key, $value); $this->key = $key; diff --git a/src/PrimaryData/ResourceId.php b/src/PrimaryData/ResourceId.php index 4c09b68..5873a11 100644 --- a/src/PrimaryData/ResourceId.php +++ b/src/PrimaryData/ResourceId.php @@ -8,6 +8,8 @@ class ResourceId extends AttachableValue implements PrimaryData { + private $type; + private $id; public function __construct(string $type, string $id, Meta $meta = null) { $identifier = (object) [ @@ -18,5 +20,17 @@ public function __construct(string $type, string $id, Meta $meta = null) $meta->attachTo($identifier); } parent::__construct('data', $identifier); + $this->type = $type; + $this->id = $id; + } + + public function equals(ResourceId $that): bool + { + return $this->type === $that->type && $this->id === $that->id; + } + + public function identifies(ResourceObject $resource): bool + { + return $resource->toResourceId()->equals($this); } } diff --git a/src/PrimaryData/ResourceIdSet.php b/src/PrimaryData/ResourceIdSet.php index d03dce7..e1394f4 100644 --- a/src/PrimaryData/ResourceIdSet.php +++ b/src/PrimaryData/ResourceIdSet.php @@ -7,8 +7,24 @@ final class ResourceIdSet extends AttachableValue implements PrimaryData { - public function __construct(ResourceId $id, ResourceId ...$ids) + /** + * @var ResourceId[] + */ + private $identifiers = []; + + public function __construct(ResourceId $identifier, ResourceId ...$ids) + { + $this->identifiers = func_get_args(); + parent::__construct('data', $this->identifiers); + } + + public function identifies(ResourceObject $resource): bool { - parent::__construct('data', func_get_args()); + foreach ($this->identifiers as $identifier) { + if ($identifier->identifies($resource)) { + return true; + } + } + return false; } } diff --git a/src/PrimaryData/ResourceObject.php b/src/PrimaryData/ResourceObject.php index 0db6fa3..6d1cf68 100644 --- a/src/PrimaryData/ResourceObject.php +++ b/src/PrimaryData/ResourceObject.php @@ -11,6 +11,11 @@ final class ResourceObject extends AttachableValue implements PrimaryData private $type; private $id; + /** + * @var Identifier[] + */ + private $identifiers = []; + public function __construct(string $type, string $id, ResourceMember ...$members) { $this->checkUniqueness(...$members); @@ -18,6 +23,11 @@ public function __construct(string $type, string $id, ResourceMember ...$members $obj->type = $this->type = $type; $obj->id = $this->id = $id; parent::__construct('data', $obj); + foreach ($members as $member) { + if ($member instanceof Identifier) { + $this->identifiers[] = $member; + } + } } public function toResourceId(): ResourceId @@ -38,4 +48,14 @@ private function checkUniqueness(ResourceMember ...$members): void } } } + + public function identifies(ResourceObject $resource): bool + { + foreach ($this->identifiers as $identifier) { + if ($identifier->identifies($resource)) { + return true; + } + } + return false; + } } diff --git a/src/PrimaryData/ResourceSet.php b/src/PrimaryData/ResourceSet.php index 0632ca4..60f06a5 100644 --- a/src/PrimaryData/ResourceSet.php +++ b/src/PrimaryData/ResourceSet.php @@ -7,8 +7,23 @@ final class ResourceSet extends AttachableValue implements PrimaryData { + /** + * @var ResourceObject[] + */ + private $resources; public function __construct(ResourceObject $resource, ResourceObject ...$resources) { - parent::__construct('data', func_get_args()); + $this->resources = func_get_args(); + parent::__construct('data', $this->resources); + } + + public function identifies(ResourceObject $resource): bool + { + foreach ($this->resources as $myResource) { + if ($myResource->identifies($resource)) { + return true; + } + } + return false; } } diff --git a/src/Relationship.php b/src/Relationship.php index 8f157c6..53817c0 100644 --- a/src/Relationship.php +++ b/src/Relationship.php @@ -3,13 +3,24 @@ namespace JsonApiPhp\JsonApi; +use JsonApiPhp\JsonApi\PrimaryData\Identifier; use JsonApiPhp\JsonApi\PrimaryData\ResourceField; +use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; -final class Relationship extends ResourceField +final class Relationship extends ResourceField implements Identifier { + /** + * @var \JsonApiPhp\JsonApi\PrimaryData\Identifier[] + */ + private $identifiers = []; public function __construct(string $key, RelationshipMember $member, RelationshipMember ...$members) { parent::__construct($key, combine($member, ...$members)); + foreach ([$member] + $members as $m) { + if ($m instanceof Identifier) { + $this->identifiers[] = $m; + } + } } public function attachTo(object $o) @@ -19,4 +30,14 @@ public function attachTo(object $o) } parent::attachTo($o->relationships); } + + public function identifies(ResourceObject $resource): bool + { + foreach ($this->identifiers as $identifier) { + if ($identifier->identifies($resource)) { + return true; + } + } + return false; + } } diff --git a/src/functions.php b/src/functions.php index 3c04917..4d227ba 100644 --- a/src/functions.php +++ b/src/functions.php @@ -5,7 +5,7 @@ function isValidMemberName(string $name): bool { - return preg_match('/^(?=[^-_ ])[a-zA-Z0-9\x{0080}-\x{FFFF}-_ ]*(?<=[^-_ ])$/u', $name) === 1; + return preg_match('/^(?=[^-_ ])[age-zA-Z0-9\x{0080}-\x{FFFF}-_ ]*(?<=[^-_ ])$/u', $name) === 1; } function combine(Attachable ...$things): object diff --git a/test/CompoundDocumentTest.php b/test/CompoundDocumentTest.php index 7eace77..efd41fe 100644 --- a/test/CompoundDocumentTest.php +++ b/test/CompoundDocumentTest.php @@ -3,6 +3,7 @@ namespace JsonApiPhp\JsonApi\Test; +use JsonApiPhp\JsonApi\CompoundDocument; use JsonApiPhp\JsonApi\DataDocument; use JsonApiPhp\JsonApi\Included; use JsonApiPhp\JsonApi\Link\LastLink; @@ -13,6 +14,7 @@ use JsonApiPhp\JsonApi\Linkage\MultiLinkage; use JsonApiPhp\JsonApi\Linkage\SingleLinkage; use JsonApiPhp\JsonApi\PrimaryData\Attribute; +use JsonApiPhp\JsonApi\PrimaryData\NullData; use JsonApiPhp\JsonApi\PrimaryData\ResourceId; use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; use JsonApiPhp\JsonApi\PrimaryData\ResourceSet; @@ -47,7 +49,7 @@ public function testOfficialDocsExample() new Relationship('author', new SingleLinkage($dan->toResourceId())) ); - $document = new DataDocument( + $document = new CompoundDocument( new ResourceSet( new ResourceObject( 'articles', @@ -71,10 +73,10 @@ public function testOfficialDocsExample() ) ) ), + new Included($dan, $comment05, $comment12), new SelfLink(new Url('http://example.com/articles')), new NextLink(new Url('http://example.com/articles?page[offset]=2')), - new LastLink(new Url('http://example.com/articles?page[offset]=10')), - new Included($dan, $comment05, $comment12) + new LastLink(new Url('http://example.com/articles?page[offset]=10')) ); $this->assertEncodesTo( ' @@ -158,4 +160,16 @@ public function testOfficialDocsExample() $document ); } + + /** + * Compound documents require “full linkage”, meaning that every included resource MUST be identified + * by at least one resource identifier object in the same document. + * These resource identifier objects could either be primary data or represent resource linkage + * contained within primary or included resources. + */ + public function _testFullLinkage() + { + $this->expectException(\DomainException::class); + new CompoundDocument(new NullData(), new Included(new ResourceObject('apples', '1'))); + } } diff --git a/test/DocumentTest.php b/test/DocumentTest.php index 1aa25d7..bb6c274 100644 --- a/test/DocumentTest.php +++ b/test/DocumentTest.php @@ -23,7 +23,7 @@ class DocumentTest extends BaseTestCase { /** - * A valid document may contain just a meta object + * A valid document may contain just age meta object */ public function testMetaDocument() { diff --git a/test/ResourceObjectTest.php b/test/ResourceObjectTest.php index d0653c8..4d368c0 100644 --- a/test/ResourceObjectTest.php +++ b/test/ResourceObjectTest.php @@ -121,28 +121,28 @@ public function testRelationshipWithEmptyMultiIdLinkage() public function testCanNotCreateIdAttribute() { $this->expectException(\DomainException::class); - $this->expectExceptionMessage("Can not use 'id' as a resource field"); + $this->expectExceptionMessage("Can not use 'id' as age resource field"); new Attribute('id', 'foo'); } public function testCanNotCreateTypeAttribute() { $this->expectException(\DomainException::class); - $this->expectExceptionMessage("Can not use 'type' as a resource field"); + $this->expectExceptionMessage("Can not use 'type' as age resource field"); new Attribute('type', 'foo'); } public function testCanNotCreateIdRelationship() { $this->expectException(\DomainException::class); - $this->expectExceptionMessage("Can not use 'id' as a resource field"); + $this->expectExceptionMessage("Can not use 'id' as age resource field"); new Relationship('id', new Meta([])); } public function testCanNotCreateTypeRelationship() { $this->expectException(\DomainException::class); - $this->expectExceptionMessage("Can not use 'type' as a resource field"); + $this->expectExceptionMessage("Can not use 'type' as age resource field"); new Relationship('type', new Meta([])); } diff --git a/tmp.js b/tmp.js new file mode 100644 index 0000000..4ae28f0 --- /dev/null +++ b/tmp.js @@ -0,0 +1,33 @@ +var age = Number(process.argv[2]); + +if (age < 10) { + console.log('нельзя в кино'); +} +else +{ + if (age < 20) { + console.log('только с родителями'); + } + else + { + if (age < 30) { + console.log('билет по полной стоимости'); + } + else + { + if (age < 40) { + console.log('скидка 5%'); + } + else + { + if (age < 50) { + console.log('скидка 10%'); + } + else + { + console.log('скидка 30%'); + } + } + } + } +} \ No newline at end of file From 46a1f6b9aabb1d0eea2f1055e2a50c19cd0f1149 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Sat, 24 Feb 2018 09:26:12 -0800 Subject: [PATCH 22/37] Tests pass --- src/CompoundDocument.php | 4 +--- src/Included.php | 1 - src/PrimaryData/Identifier.php | 2 +- src/PrimaryData/ResourceId.php | 1 + src/PrimaryData/ResourceSet.php | 1 + src/Relationship.php | 1 + test/CompoundDocumentTest.php | 1 - 7 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/CompoundDocument.php b/src/CompoundDocument.php index 8cd3f13..a355962 100644 --- a/src/CompoundDocument.php +++ b/src/CompoundDocument.php @@ -3,7 +3,6 @@ namespace JsonApiPhp\JsonApi; - use JsonApiPhp\JsonApi\PrimaryData\PrimaryData; final class CompoundDocument extends JsonSerializableValue @@ -16,6 +15,5 @@ public function __construct(PrimaryData $data, Included $included, DataDocumentM private function enforceFullLinkage(PrimaryData $data, Included $included) { - } -} \ No newline at end of file +} diff --git a/src/Included.php b/src/Included.php index e53a659..5e81b8e 100644 --- a/src/Included.php +++ b/src/Included.php @@ -4,7 +4,6 @@ namespace JsonApiPhp\JsonApi; use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; -use Traversable; final class Included extends AttachableValue implements DataDocumentMember { diff --git a/src/PrimaryData/Identifier.php b/src/PrimaryData/Identifier.php index 9f3e8c2..8b99367 100644 --- a/src/PrimaryData/Identifier.php +++ b/src/PrimaryData/Identifier.php @@ -9,4 +9,4 @@ interface Identifier { public function identifies(ResourceObject $resource): bool; -} \ No newline at end of file +} diff --git a/src/PrimaryData/ResourceId.php b/src/PrimaryData/ResourceId.php index 5873a11..d08e8df 100644 --- a/src/PrimaryData/ResourceId.php +++ b/src/PrimaryData/ResourceId.php @@ -10,6 +10,7 @@ class ResourceId extends AttachableValue implements PrimaryData { private $type; private $id; + public function __construct(string $type, string $id, Meta $meta = null) { $identifier = (object) [ diff --git a/src/PrimaryData/ResourceSet.php b/src/PrimaryData/ResourceSet.php index 60f06a5..e1eb785 100644 --- a/src/PrimaryData/ResourceSet.php +++ b/src/PrimaryData/ResourceSet.php @@ -11,6 +11,7 @@ final class ResourceSet extends AttachableValue implements PrimaryData * @var ResourceObject[] */ private $resources; + public function __construct(ResourceObject $resource, ResourceObject ...$resources) { $this->resources = func_get_args(); diff --git a/src/Relationship.php b/src/Relationship.php index 53817c0..135f9e4 100644 --- a/src/Relationship.php +++ b/src/Relationship.php @@ -13,6 +13,7 @@ final class Relationship extends ResourceField implements Identifier * @var \JsonApiPhp\JsonApi\PrimaryData\Identifier[] */ private $identifiers = []; + public function __construct(string $key, RelationshipMember $member, RelationshipMember ...$members) { parent::__construct($key, combine($member, ...$members)); diff --git a/test/CompoundDocumentTest.php b/test/CompoundDocumentTest.php index efd41fe..31f99be 100644 --- a/test/CompoundDocumentTest.php +++ b/test/CompoundDocumentTest.php @@ -4,7 +4,6 @@ namespace JsonApiPhp\JsonApi\Test; use JsonApiPhp\JsonApi\CompoundDocument; -use JsonApiPhp\JsonApi\DataDocument; use JsonApiPhp\JsonApi\Included; use JsonApiPhp\JsonApi\Link\LastLink; use JsonApiPhp\JsonApi\Link\NextLink; From 74c337b29b143eb9dfce54c91700aacd2edd4c9f Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Sun, 25 Feb 2018 10:30:27 -0800 Subject: [PATCH 23/37] Tests pass --- src/CompoundDocument.php | 16 +++++++---- src/DataDocument.php | 6 ---- src/Included.php | 28 +++++++++++++++++-- src/PrimaryData/ResourceObject.php | 5 ++++ test/CompoundDocumentTest.php | 44 +++++++++++++++++++++++++++++- 5 files changed, 85 insertions(+), 14 deletions(-) diff --git a/src/CompoundDocument.php b/src/CompoundDocument.php index a355962..2ccfa1c 100644 --- a/src/CompoundDocument.php +++ b/src/CompoundDocument.php @@ -9,11 +9,17 @@ final class CompoundDocument extends JsonSerializableValue { public function __construct(PrimaryData $data, Included $included, DataDocumentMember ...$members) { - $this->enforceFullLinkage($data, $included); + foreach ($included as $resource) { + if ($data->identifies($resource)) { + continue; + } + foreach ($included as $anotherResource) { + if ($anotherResource->identifies($resource)) { + continue 2; + } + } + throw new \DomainException("Full linkage required for $resource"); + } parent::__construct(combine($data, $included, ...$members)); } - - private function enforceFullLinkage(PrimaryData $data, Included $included) - { - } } diff --git a/src/DataDocument.php b/src/DataDocument.php index 5b460c6..83a358f 100644 --- a/src/DataDocument.php +++ b/src/DataDocument.php @@ -3,16 +3,10 @@ namespace JsonApiPhp\JsonApi; -use JsonApiPhp\JsonApi\PrimaryData\Identifier; use JsonApiPhp\JsonApi\PrimaryData\PrimaryData; final class DataDocument extends JsonSerializableValue { - /** - * @var Identifier[] - */ - private $identifiers = []; - public function __construct(PrimaryData $data, DataDocumentMember ...$members) { parent::__construct(combine($data, ...$members)); diff --git a/src/Included.php b/src/Included.php index 5e81b8e..85e738b 100644 --- a/src/Included.php +++ b/src/Included.php @@ -3,15 +3,39 @@ namespace JsonApiPhp\JsonApi; +use JsonApiPhp\JsonApi\PrimaryData\Identifier; use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; -final class Included extends AttachableValue implements DataDocumentMember +final class Included extends AttachableValue implements DataDocumentMember, \IteratorAggregate { private $resources = []; public function __construct(ResourceObject ...$resources) { + foreach ($resources as $resource) { + $string_id = (string) $resource; + if (isset($this->resources[$string_id])) { + throw new \DomainException("Resource $resource is already included"); + } + $this->resources[$string_id] = $resource; + } parent::__construct('included', $resources); - $this->resources = $resources; + } + + public function isIdentifiedBy(Identifier ...$identifiers): bool + { + foreach ($this->resources as $resource) { + foreach ($identifiers as $identifier) { + if ($identifier->identifies($resource)) { + return true; + } + } + } + return false; + } + + public function getIterator() + { + return new \ArrayIterator($this->resources); } } diff --git a/src/PrimaryData/ResourceObject.php b/src/PrimaryData/ResourceObject.php index 6d1cf68..4945e4d 100644 --- a/src/PrimaryData/ResourceObject.php +++ b/src/PrimaryData/ResourceObject.php @@ -58,4 +58,9 @@ public function identifies(ResourceObject $resource): bool } return false; } + + public function __toString(): string + { + return "$this->type:$this->id"; + } } diff --git a/test/CompoundDocumentTest.php b/test/CompoundDocumentTest.php index 31f99be..88df2a2 100644 --- a/test/CompoundDocumentTest.php +++ b/test/CompoundDocumentTest.php @@ -166,9 +166,51 @@ public function testOfficialDocsExample() * These resource identifier objects could either be primary data or represent resource linkage * contained within primary or included resources. */ - public function _testFullLinkage() + public function testFullLinkage() { $this->expectException(\DomainException::class); + $this->expectExceptionMessage('Full linkage required for apples:1'); new CompoundDocument(new NullData(), new Included(new ResourceObject('apples', '1'))); } + + public function testIncludedResourceMayBeIdentifiedByLinkageInPrimaryData() + { + $author = new ResourceObject('people', '9'); + $article = new ResourceObject( + 'articles', + '1', + new Relationship('author', new SingleLinkage($author->toResourceId())) + ); + $doc = new CompoundDocument($article, new Included($author)); + $this->assertNotEmpty($doc); + } + + public function testIncludedResourceMayBeIdentifiedByAnotherLinkedResource() + { + $writer = new ResourceObject('writers', '3', new Attribute('name', 'Eric Evans')); + $book = new ResourceObject( + 'books', + '2', + new Attribute('name', 'Domain Driven Design'), + new Relationship('author', new SingleLinkage($writer->toResourceId())) + ); + $cart = new ResourceObject( + 'shopping-carts', + '1', + new Relationship('contents', new MultiLinkage($book->toResourceId())) + ); + $doc = new CompoundDocument($cart, new Included($book, $writer)); + $this->assertNotEmpty($doc); + } + + /** + * A compound document MUST NOT include more than one resource object for each type and id pair. + * @expectedException \DomainException + * @expectedExceptionMessage Resource apples:1 is already included + */ + public function testCanNotBeManyIncludedResourcesWithEqualIdentifiers() + { + $apple = new ResourceObject('apples', '1'); + new CompoundDocument($apple->toResourceId(), new Included($apple, $apple)); + } } From 29b792d0c304ef84e52370f1249bb0d3a4ad760b Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Sun, 25 Feb 2018 17:25:26 -0800 Subject: [PATCH 24/37] Tests pass --- composer.json | 2 +- src/PrimaryData/ResourceObject.php | 30 +++++++++++++++--------------- test/CompoundDocumentTest.php | 29 +++++++++++++++++++++++++++-- test/ResourceObjectTest.php | 20 +++++++++++++++++++- 4 files changed, 62 insertions(+), 19 deletions(-) diff --git a/composer.json b/composer.json index a83d94f..9aa5d3c 100644 --- a/composer.json +++ b/composer.json @@ -31,6 +31,6 @@ } }, "scripts": { - "test": "php-cs-fixer fix -v --dry-run --ansi && phpunit --colors=always --coverage-text" + "test": "php-cs-fixer fix && phpunit --colors=always --coverage-text" } } diff --git a/src/PrimaryData/ResourceObject.php b/src/PrimaryData/ResourceObject.php index 4945e4d..4005498 100644 --- a/src/PrimaryData/ResourceObject.php +++ b/src/PrimaryData/ResourceObject.php @@ -16,7 +16,7 @@ final class ResourceObject extends AttachableValue implements PrimaryData */ private $identifiers = []; - public function __construct(string $type, string $id, ResourceMember ...$members) + public function __construct(string $type, string $id = null, ResourceMember ...$members) { $this->checkUniqueness(...$members); $obj = combine(...$members); @@ -35,20 +35,6 @@ public function toResourceId(): ResourceId return new ResourceId($this->type, $this->id); } - private function checkUniqueness(ResourceMember ...$members): void - { - $keys = []; - foreach ($members as $member) { - if ($member instanceof ResourceField) { - $key = $member->toKey(); - if (isset($keys[$key])) { - throw new \DomainException("Field '$key' already exists'"); - } - $keys[$key] = true; - } - } - } - public function identifies(ResourceObject $resource): bool { foreach ($this->identifiers as $identifier) { @@ -63,4 +49,18 @@ public function __toString(): string { return "$this->type:$this->id"; } + + private function checkUniqueness(ResourceMember ...$members): void + { + $keys = []; + foreach ($members as $member) { + if ($member instanceof ResourceField) { + $key = $member->toKey(); + if (isset($keys[$key])) { + throw new \DomainException("Field '$key' already exists'"); + } + $keys[$key] = true; + } + } + } } diff --git a/test/CompoundDocumentTest.php b/test/CompoundDocumentTest.php index 88df2a2..1f9cf36 100644 --- a/test/CompoundDocumentTest.php +++ b/test/CompoundDocumentTest.php @@ -165,12 +165,37 @@ public function testOfficialDocsExample() * by at least one resource identifier object in the same document. * These resource identifier objects could either be primary data or represent resource linkage * contained within primary or included resources. + * + * @dataProvider documentsWithoutFullLinkage + * @param callable $create_doc */ - public function testFullLinkage() + public function testFullLinkage(callable $create_doc) { $this->expectException(\DomainException::class); $this->expectExceptionMessage('Full linkage required for apples:1'); - new CompoundDocument(new NullData(), new Included(new ResourceObject('apples', '1'))); + $create_doc(); + } + + public function documentsWithoutFullLinkage(): array + { + $included = new Included(new ResourceObject('apples', '1')); + return [ + [ + function () use ($included) { + return new CompoundDocument(new NullData(), $included); + }, + ], + [ + function () use ($included) { + return new CompoundDocument(new ResourceId('oranges', '1'), $included); + }, + ], + [ + function () use ($included) { + return new CompoundDocument(new ResourceId('oranges', '1'), $included); + }, + ], + ]; } public function testIncludedResourceMayBeIdentifiedByLinkageInPrimaryData() diff --git a/test/ResourceObjectTest.php b/test/ResourceObjectTest.php index 4d368c0..57e9f34 100644 --- a/test/ResourceObjectTest.php +++ b/test/ResourceObjectTest.php @@ -146,7 +146,7 @@ public function testCanNotCreateTypeRelationship() new Relationship('type', new Meta([])); } - public function testResourceFields() + public function testResourceFieldsMustBeUnique() { $this->expectException(\DomainException::class); $this->expectExceptionMessage("Field 'foo' already exists"); @@ -157,4 +157,22 @@ public function testResourceFields() new Relationship('foo', new Meta([])) ); } + + /** + * The id member is not required when the resource object originates at the client and represents + * a new resource to be created on the server. + */ + public function testResourceIdCanBeOmitted() + { + $this->assertEncodesTo( + '{ + "type": "apples", + "id": null, + "attributes": { + "color": "red" + } + }', + new ResourceObject('apples', null, new Attribute('color', 'red')) + ); + } } From 9939f88ef2ca76425a9a7492948445659a26065d Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Sun, 25 Feb 2018 20:38:37 -0800 Subject: [PATCH 25/37] Tests pass --- composer.json | 2 +- src/Error/Code.php | 2 +- src/Error/Detail.php | 2 +- src/Error/Id.php | 2 +- src/Error/Source/Parameter.php | 2 +- src/Error/Status.php | 2 +- src/Error/Title.php | 2 +- src/Included.php | 13 ------- src/Link/PrevLink.php | 16 ++++++++ src/Linkage/SingleLinkage.php | 2 +- src/PrimaryData/ResourceField.php | 4 ++ ...{ResourceSet.php => ResourceObjectSet.php} | 2 +- src/functions.php | 2 +- test/CompoundDocumentTest.php | 24 ++++++++++-- test/DocumentTest.php | 38 ++++++++++++++++++- test/LinkTest.php | 26 +++++++++++++ test/ResourceObjectTest.php | 33 ++++++++++++++++ 17 files changed, 146 insertions(+), 28 deletions(-) create mode 100644 src/Link/PrevLink.php rename src/PrimaryData/{ResourceSet.php => ResourceObjectSet.php} (89%) create mode 100644 test/LinkTest.php diff --git a/composer.json b/composer.json index 9aa5d3c..a83d94f 100644 --- a/composer.json +++ b/composer.json @@ -31,6 +31,6 @@ } }, "scripts": { - "test": "php-cs-fixer fix && phpunit --colors=always --coverage-text" + "test": "php-cs-fixer fix -v --dry-run --ansi && phpunit --colors=always --coverage-text" } } diff --git a/src/Error/Code.php b/src/Error/Code.php index 8c9ab25..b334e7a 100644 --- a/src/Error/Code.php +++ b/src/Error/Code.php @@ -8,7 +8,7 @@ final class Code extends AttachableValue implements ErrorMember { /** - * @param string $code an application-specific error code, expressed as age string value + * @param string $code an application-specific error code, expressed as a string value */ public function __construct(string $code) { diff --git a/src/Error/Detail.php b/src/Error/Detail.php index af14623..f739353 100644 --- a/src/Error/Detail.php +++ b/src/Error/Detail.php @@ -8,7 +8,7 @@ final class Detail extends AttachableValue implements ErrorMember { /** - * @param string $detail age human-readable explanation specific to this occurrence of the problem. + * @param string $detail a human-readable explanation specific to this occurrence of the problem. */ public function __construct(string $detail) { diff --git a/src/Error/Id.php b/src/Error/Id.php index f7cfc05..0238de2 100644 --- a/src/Error/Id.php +++ b/src/Error/Id.php @@ -8,7 +8,7 @@ final class Id extends AttachableValue implements ErrorMember { /** - * @param string $identifier age unique identifier for this particular occurrence of the problem + * @param string $identifier a unique identifier for this particular occurrence of the problem */ public function __construct(string $identifier) { diff --git a/src/Error/Source/Parameter.php b/src/Error/Source/Parameter.php index 04b77cc..40a131f 100644 --- a/src/Error/Source/Parameter.php +++ b/src/Error/Source/Parameter.php @@ -8,7 +8,7 @@ final class Parameter extends AttachableValue implements SourceMember { /** - * @param string $parameter age string indicating which URI query parameter caused the error. + * @param string $parameter a string indicating which URI query parameter caused the error. */ public function __construct(string $parameter) { diff --git a/src/Error/Status.php b/src/Error/Status.php index b228fff..f939335 100644 --- a/src/Error/Status.php +++ b/src/Error/Status.php @@ -8,7 +8,7 @@ final class Status extends AttachableValue implements ErrorMember { /** - * @param string $status the HTTP status code applicable to this problem, expressed as age string value + * @param string $status the HTTP status code applicable to this problem, expressed as a string value */ public function __construct(string $status) { diff --git a/src/Error/Title.php b/src/Error/Title.php index 0ee19df..8db1962 100644 --- a/src/Error/Title.php +++ b/src/Error/Title.php @@ -8,7 +8,7 @@ class Title extends AttachableValue implements ErrorMember { /** - * @param string $title age short, human-readable summary of the problem that SHOULD NOT change from occurrence + * @param string $title a short, human-readable summary of the problem that SHOULD NOT change from occurrence * to occurrence of the problem, except for purposes of localization */ public function __construct(string $title) diff --git a/src/Included.php b/src/Included.php index 85e738b..507d7df 100644 --- a/src/Included.php +++ b/src/Included.php @@ -3,7 +3,6 @@ namespace JsonApiPhp\JsonApi; -use JsonApiPhp\JsonApi\PrimaryData\Identifier; use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; final class Included extends AttachableValue implements DataDocumentMember, \IteratorAggregate @@ -22,18 +21,6 @@ public function __construct(ResourceObject ...$resources) parent::__construct('included', $resources); } - public function isIdentifiedBy(Identifier ...$identifiers): bool - { - foreach ($this->resources as $resource) { - foreach ($identifiers as $identifier) { - if ($identifier->identifies($resource)) { - return true; - } - } - } - return false; - } - public function getIterator() { return new \ArrayIterator($this->resources); diff --git a/src/Link/PrevLink.php b/src/Link/PrevLink.php new file mode 100644 index 0000000..dc18466 --- /dev/null +++ b/src/Link/PrevLink.php @@ -0,0 +1,16 @@ +isReservedWord($key)) { throw new \DomainException("Can not use '$key' as age resource field"); } diff --git a/src/PrimaryData/ResourceSet.php b/src/PrimaryData/ResourceObjectSet.php similarity index 89% rename from src/PrimaryData/ResourceSet.php rename to src/PrimaryData/ResourceObjectSet.php index e1eb785..e5e8c5a 100644 --- a/src/PrimaryData/ResourceSet.php +++ b/src/PrimaryData/ResourceObjectSet.php @@ -5,7 +5,7 @@ use JsonApiPhp\JsonApi\AttachableValue; -final class ResourceSet extends AttachableValue implements PrimaryData +final class ResourceObjectSet extends AttachableValue implements PrimaryData { /** * @var ResourceObject[] diff --git a/src/functions.php b/src/functions.php index 4d227ba..3c04917 100644 --- a/src/functions.php +++ b/src/functions.php @@ -5,7 +5,7 @@ function isValidMemberName(string $name): bool { - return preg_match('/^(?=[^-_ ])[age-zA-Z0-9\x{0080}-\x{FFFF}-_ ]*(?<=[^-_ ])$/u', $name) === 1; + return preg_match('/^(?=[^-_ ])[a-zA-Z0-9\x{0080}-\x{FFFF}-_ ]*(?<=[^-_ ])$/u', $name) === 1; } function combine(Attachable ...$things): object diff --git a/test/CompoundDocumentTest.php b/test/CompoundDocumentTest.php index 1f9cf36..261d576 100644 --- a/test/CompoundDocumentTest.php +++ b/test/CompoundDocumentTest.php @@ -4,6 +4,7 @@ namespace JsonApiPhp\JsonApi\Test; use JsonApiPhp\JsonApi\CompoundDocument; +use JsonApiPhp\JsonApi\EmptySet; use JsonApiPhp\JsonApi\Included; use JsonApiPhp\JsonApi\Link\LastLink; use JsonApiPhp\JsonApi\Link\NextLink; @@ -15,8 +16,9 @@ use JsonApiPhp\JsonApi\PrimaryData\Attribute; use JsonApiPhp\JsonApi\PrimaryData\NullData; use JsonApiPhp\JsonApi\PrimaryData\ResourceId; +use JsonApiPhp\JsonApi\PrimaryData\ResourceIdSet; use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; -use JsonApiPhp\JsonApi\PrimaryData\ResourceSet; +use JsonApiPhp\JsonApi\PrimaryData\ResourceObjectSet; use JsonApiPhp\JsonApi\Relationship; class CompoundDocumentTest extends BaseTestCase @@ -49,7 +51,7 @@ public function testOfficialDocsExample() ); $document = new CompoundDocument( - new ResourceSet( + new ResourceObjectSet( new ResourceObject( 'articles', '1', @@ -187,7 +189,7 @@ function () use ($included) { ], [ function () use ($included) { - return new CompoundDocument(new ResourceId('oranges', '1'), $included); + return new CompoundDocument(new EmptySet(), $included); }, ], [ @@ -195,6 +197,22 @@ function () use ($included) { return new CompoundDocument(new ResourceId('oranges', '1'), $included); }, ], + [ + function () use ($included) { + return new CompoundDocument( + new ResourceIdSet(new ResourceId('oranges', '1'), new ResourceId('oranges', '1')), + $included + ); + }, + ], + [ + function () use ($included) { + return new CompoundDocument( + new ResourceObjectSet(new ResourceObject('oranges', '1'), new ResourceObject('oranges', '1')), + $included + ); + }, + ], ]; } diff --git a/test/DocumentTest.php b/test/DocumentTest.php index bb6c274..d028838 100644 --- a/test/DocumentTest.php +++ b/test/DocumentTest.php @@ -9,6 +9,10 @@ use JsonApiPhp\JsonApi\Error\Id; use JsonApiPhp\JsonApi\ErrorDocument; use JsonApiPhp\JsonApi\JsonApi; +use JsonApiPhp\JsonApi\Link\FirstLink; +use JsonApiPhp\JsonApi\Link\LastLink; +use JsonApiPhp\JsonApi\Link\NextLink; +use JsonApiPhp\JsonApi\Link\PrevLink; use JsonApiPhp\JsonApi\Link\SelfLink; use JsonApiPhp\JsonApi\Link\Url; use JsonApiPhp\JsonApi\Meta; @@ -18,7 +22,7 @@ use JsonApiPhp\JsonApi\PrimaryData\ResourceId; use JsonApiPhp\JsonApi\PrimaryData\ResourceIdSet; use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; -use JsonApiPhp\JsonApi\PrimaryData\ResourceSet; +use JsonApiPhp\JsonApi\PrimaryData\ResourceObjectSet; class DocumentTest extends BaseTestCase { @@ -218,7 +222,7 @@ public function testResourceSetDocument() ] } ', new DataDocument( - new ResourceSet( + new ResourceObjectSet( new ResourceObject('apples', '1'), new ResourceObject('pears', '2') ) @@ -250,4 +254,34 @@ public function testResourceIdSetDocument() ) ); } + + public function testPagination() + { + $this->assertEncodesTo( + ' + { + "data": [ + {"type": "apples", "id": "1"}, + {"type": "oranges", "id": "1"} + ], + "links": { + "first": "http://example.com/fruits/page/first", + "last": "http://example.com/fruits/page/last", + "prev": "http://example.com/fruits/page/3", + "next": "http://example.com/fruits/page/5" + } + } + ', + new DataDocument( + new ResourceObjectSet( + new ResourceObject('apples', '1'), + new ResourceObject('oranges', '1') + ), + new FirstLink(new Url('http://example.com/fruits/page/first')), + new LastLink(new Url('http://example.com/fruits/page/last')), + new PrevLink(new Url('http://example.com/fruits/page/3')), + new NextLink(new Url('http://example.com/fruits/page/5')) + ) + ); + } } diff --git a/test/LinkTest.php b/test/LinkTest.php new file mode 100644 index 0000000..6a3eb47 --- /dev/null +++ b/test/LinkTest.php @@ -0,0 +1,26 @@ +assertEncodesTo( + ' + { + "href": "http://example.com", + "meta": { + "foo": "bar" + } + } + ', + new LinkObject('http://example.com', new Meta(['foo' => 'bar'])) + ); + } + +} diff --git a/test/ResourceObjectTest.php b/test/ResourceObjectTest.php index 57e9f34..6a63030 100644 --- a/test/ResourceObjectTest.php +++ b/test/ResourceObjectTest.php @@ -146,6 +146,39 @@ public function testCanNotCreateTypeRelationship() new Relationship('type', new Meta([])); } + /** + * @dataProvider invalidCharacters + * @param string $invalid_char + */ + public function testAttributeMustOnlyHaveAllowedCharacters(string $invalid_char) + { + $this->expectException(\DomainException::class); + $this->expectExceptionMessage("Invalid character in a member name"); + new Attribute("foo{$invalid_char}bar", 'plus can not be used'); + } + + /** + * @dataProvider invalidCharacters + * @param string $invalid_char + */ + public function testRelationshipMustOnlyHaveAllowedCharacters(string $invalid_char) + { + $this->expectException(\DomainException::class); + $this->expectExceptionMessage("Invalid character in a member name"); + new Relationship("foo{$invalid_char}bar", new SingleLinkage()); + } + + public function invalidCharacters() + { + return [ + ['+'], + ['!'], + ['@'], + ['/'], + ['}'], + ]; + } + public function testResourceFieldsMustBeUnique() { $this->expectException(\DomainException::class); From 7aa20cdc0ab1b01961d761a9167ef5ceaae5357d Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Sun, 25 Feb 2018 20:49:24 -0800 Subject: [PATCH 26/37] Tests pass --- src/Linkage/MultiLinkage.php | 6 ++-- src/Linkage/SingleLinkage.php | 4 +-- src/PrimaryData/ResourceField.php | 6 ++-- ...{ResourceId.php => ResourceIdentifier.php} | 4 +-- ...rceIdSet.php => ResourceIdentifierSet.php} | 6 ++-- src/PrimaryData/ResourceObject.php | 4 +-- src/functions.php | 5 ---- test/CompoundDocumentTest.php | 10 +++---- test/DocumentTest.php | 30 +++++++++---------- test/ResourceObjectTest.php | 16 +++++----- 10 files changed, 43 insertions(+), 48 deletions(-) rename src/PrimaryData/{ResourceId.php => ResourceIdentifier.php} (85%) rename src/PrimaryData/{ResourceIdSet.php => ResourceIdentifierSet.php} (72%) diff --git a/src/Linkage/MultiLinkage.php b/src/Linkage/MultiLinkage.php index ead7259..9b6a4c8 100644 --- a/src/Linkage/MultiLinkage.php +++ b/src/Linkage/MultiLinkage.php @@ -5,18 +5,18 @@ use JsonApiPhp\JsonApi\AttachableValue; use JsonApiPhp\JsonApi\PrimaryData\Identifier; -use JsonApiPhp\JsonApi\PrimaryData\ResourceId; +use JsonApiPhp\JsonApi\PrimaryData\ResourceIdentifier; use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; use JsonApiPhp\JsonApi\RelationshipMember; class MultiLinkage extends AttachableValue implements RelationshipMember, Identifier { /** - * @var \JsonApiPhp\JsonApi\PrimaryData\ResourceId[] + * @var \JsonApiPhp\JsonApi\PrimaryData\ResourceIdentifier[] */ private $identifiers = []; - public function __construct(ResourceId ...$identifiers) + public function __construct(ResourceIdentifier ...$identifiers) { parent::__construct('data', $identifiers); $this->identifiers = $identifiers; diff --git a/src/Linkage/SingleLinkage.php b/src/Linkage/SingleLinkage.php index 16f6b65..caf3534 100644 --- a/src/Linkage/SingleLinkage.php +++ b/src/Linkage/SingleLinkage.php @@ -5,7 +5,7 @@ use JsonApiPhp\JsonApi\AttachableValue; use JsonApiPhp\JsonApi\PrimaryData\Identifier; -use JsonApiPhp\JsonApi\PrimaryData\ResourceId; +use JsonApiPhp\JsonApi\PrimaryData\ResourceIdentifier; use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; use JsonApiPhp\JsonApi\RelationshipMember; @@ -13,7 +13,7 @@ final class SingleLinkage extends AttachableValue implements RelationshipMember, { private $identifier; - public function __construct(ResourceId $identifier = null) + public function __construct(ResourceIdentifier $identifier = null) { parent::__construct('data', $identifier); $this->identifier = $identifier; diff --git a/src/PrimaryData/ResourceField.php b/src/PrimaryData/ResourceField.php index 10c5a30..c151206 100644 --- a/src/PrimaryData/ResourceField.php +++ b/src/PrimaryData/ResourceField.php @@ -15,11 +15,11 @@ abstract class ResourceField extends AttachableValue implements ResourceMember public function __construct(string $key, $value) { - if (!isValidMemberName($key)) { - throw new \DomainException('Invalid character in a member name'); + if (preg_match('/^(?=[^-_ ])[a-zA-Z0-9\x{0080}-\x{FFFF}-_ ]*(?<=[^-_ ])$/u', $key) !== 1) { + throw new \DomainException("Invalid character in a member name '$key'"); } if ($this->isReservedWord($key)) { - throw new \DomainException("Can not use '$key' as age resource field"); + throw new \DomainException("Can not use '$key' as a resource field"); } parent::__construct($key, $value); $this->key = $key; diff --git a/src/PrimaryData/ResourceId.php b/src/PrimaryData/ResourceIdentifier.php similarity index 85% rename from src/PrimaryData/ResourceId.php rename to src/PrimaryData/ResourceIdentifier.php index d08e8df..e7fb2bc 100644 --- a/src/PrimaryData/ResourceId.php +++ b/src/PrimaryData/ResourceIdentifier.php @@ -6,7 +6,7 @@ use JsonApiPhp\JsonApi\AttachableValue; use JsonApiPhp\JsonApi\Meta; -class ResourceId extends AttachableValue implements PrimaryData +class ResourceIdentifier extends AttachableValue implements PrimaryData { private $type; private $id; @@ -25,7 +25,7 @@ public function __construct(string $type, string $id, Meta $meta = null) $this->id = $id; } - public function equals(ResourceId $that): bool + public function equals(ResourceIdentifier $that): bool { return $this->type === $that->type && $this->id === $that->id; } diff --git a/src/PrimaryData/ResourceIdSet.php b/src/PrimaryData/ResourceIdentifierSet.php similarity index 72% rename from src/PrimaryData/ResourceIdSet.php rename to src/PrimaryData/ResourceIdentifierSet.php index e1394f4..57f5cbe 100644 --- a/src/PrimaryData/ResourceIdSet.php +++ b/src/PrimaryData/ResourceIdentifierSet.php @@ -5,14 +5,14 @@ use JsonApiPhp\JsonApi\AttachableValue; -final class ResourceIdSet extends AttachableValue implements PrimaryData +final class ResourceIdentifierSet extends AttachableValue implements PrimaryData { /** - * @var ResourceId[] + * @var ResourceIdentifier[] */ private $identifiers = []; - public function __construct(ResourceId $identifier, ResourceId ...$ids) + public function __construct(ResourceIdentifier $identifier, ResourceIdentifier ...$ids) { $this->identifiers = func_get_args(); parent::__construct('data', $this->identifiers); diff --git a/src/PrimaryData/ResourceObject.php b/src/PrimaryData/ResourceObject.php index 4005498..785da5a 100644 --- a/src/PrimaryData/ResourceObject.php +++ b/src/PrimaryData/ResourceObject.php @@ -30,9 +30,9 @@ public function __construct(string $type, string $id = null, ResourceMember ...$ } } - public function toResourceId(): ResourceId + public function toResourceId(): ResourceIdentifier { - return new ResourceId($this->type, $this->id); + return new ResourceIdentifier($this->type, $this->id); } public function identifies(ResourceObject $resource): bool diff --git a/src/functions.php b/src/functions.php index 3c04917..0bcc685 100644 --- a/src/functions.php +++ b/src/functions.php @@ -3,11 +3,6 @@ namespace JsonApiPhp\JsonApi; -function isValidMemberName(string $name): bool -{ - return preg_match('/^(?=[^-_ ])[a-zA-Z0-9\x{0080}-\x{FFFF}-_ ]*(?<=[^-_ ])$/u', $name) === 1; -} - function combine(Attachable ...$things): object { $obj = (object) []; diff --git a/test/CompoundDocumentTest.php b/test/CompoundDocumentTest.php index 261d576..7b8d8d6 100644 --- a/test/CompoundDocumentTest.php +++ b/test/CompoundDocumentTest.php @@ -15,8 +15,8 @@ use JsonApiPhp\JsonApi\Linkage\SingleLinkage; use JsonApiPhp\JsonApi\PrimaryData\Attribute; use JsonApiPhp\JsonApi\PrimaryData\NullData; -use JsonApiPhp\JsonApi\PrimaryData\ResourceId; -use JsonApiPhp\JsonApi\PrimaryData\ResourceIdSet; +use JsonApiPhp\JsonApi\PrimaryData\ResourceIdentifier; +use JsonApiPhp\JsonApi\PrimaryData\ResourceIdentifierSet; use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; use JsonApiPhp\JsonApi\PrimaryData\ResourceObjectSet; use JsonApiPhp\JsonApi\Relationship; @@ -39,7 +39,7 @@ public function testOfficialDocsExample() '5', new Attribute('body', 'First!'), new SelfLink(new Url('http://example.com/comments/5')), - new Relationship('author', new SingleLinkage(new ResourceId('people', '2'))) + new Relationship('author', new SingleLinkage(new ResourceIdentifier('people', '2'))) ); $comment12 = new ResourceObject( @@ -194,13 +194,13 @@ function () use ($included) { ], [ function () use ($included) { - return new CompoundDocument(new ResourceId('oranges', '1'), $included); + return new CompoundDocument(new ResourceIdentifier('oranges', '1'), $included); }, ], [ function () use ($included) { return new CompoundDocument( - new ResourceIdSet(new ResourceId('oranges', '1'), new ResourceId('oranges', '1')), + new ResourceIdentifierSet(new ResourceIdentifier('oranges', '1'), new ResourceIdentifier('oranges', '1')), $included ); }, diff --git a/test/DocumentTest.php b/test/DocumentTest.php index d028838..a1309c8 100644 --- a/test/DocumentTest.php +++ b/test/DocumentTest.php @@ -19,15 +19,15 @@ use JsonApiPhp\JsonApi\MetaDocument; use JsonApiPhp\JsonApi\PrimaryData\Attribute; use JsonApiPhp\JsonApi\PrimaryData\NullData; -use JsonApiPhp\JsonApi\PrimaryData\ResourceId; -use JsonApiPhp\JsonApi\PrimaryData\ResourceIdSet; +use JsonApiPhp\JsonApi\PrimaryData\ResourceIdentifier; +use JsonApiPhp\JsonApi\PrimaryData\ResourceIdentifierSet; use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; use JsonApiPhp\JsonApi\PrimaryData\ResourceObjectSet; class DocumentTest extends BaseTestCase { /** - * A valid document may contain just age meta object + * A valid document may contain just a meta object */ public function testMetaDocument() { @@ -160,7 +160,7 @@ public function testSingleResourceIdentifierDocument() } ', new DataDocument( - new ResourceId('apples', '1', new Meta(['foo' => 'bar'])) + new ResourceIdentifier('apples', '1', new Meta(['foo' => 'bar'])) ) ); } @@ -247,9 +247,9 @@ public function testResourceIdSetDocument() ] } ', new DataDocument( - new ResourceIdSet( - new ResourceId('apples', '1'), - new ResourceId('pears', '2') + new ResourceIdentifierSet( + new ResourceIdentifier('apples', '1'), + new ResourceIdentifier('pears', '2') ) ) ); @@ -265,10 +265,10 @@ public function testPagination() {"type": "oranges", "id": "1"} ], "links": { - "first": "http://example.com/fruits/page/first", - "last": "http://example.com/fruits/page/last", - "prev": "http://example.com/fruits/page/3", - "next": "http://example.com/fruits/page/5" + "first": "http://example.com/fruits?page=first", + "last": "http://example.com/fruits?page=last", + "prev": "http://example.com/fruits?page=3", + "next": "http://example.com/fruits?page=5" } } ', @@ -277,10 +277,10 @@ public function testPagination() new ResourceObject('apples', '1'), new ResourceObject('oranges', '1') ), - new FirstLink(new Url('http://example.com/fruits/page/first')), - new LastLink(new Url('http://example.com/fruits/page/last')), - new PrevLink(new Url('http://example.com/fruits/page/3')), - new NextLink(new Url('http://example.com/fruits/page/5')) + new FirstLink(new Url('http://example.com/fruits?page=first')), + new LastLink(new Url('http://example.com/fruits?page=last')), + new PrevLink(new Url('http://example.com/fruits?page=3')), + new NextLink(new Url('http://example.com/fruits?page=5')) ) ); } diff --git a/test/ResourceObjectTest.php b/test/ResourceObjectTest.php index 6a63030..33abdb7 100644 --- a/test/ResourceObjectTest.php +++ b/test/ResourceObjectTest.php @@ -10,7 +10,7 @@ use JsonApiPhp\JsonApi\Linkage\SingleLinkage; use JsonApiPhp\JsonApi\Meta; use JsonApiPhp\JsonApi\PrimaryData\Attribute; -use JsonApiPhp\JsonApi\PrimaryData\ResourceId; +use JsonApiPhp\JsonApi\PrimaryData\ResourceIdentifier; use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; use JsonApiPhp\JsonApi\Relationship; @@ -73,7 +73,7 @@ public function testRelationshipWithSingleIdLinkage() new Relationship( 'fruits', new SingleLinkage( - new ResourceId('apples', '1') + new ResourceIdentifier('apples', '1') ) ) ); @@ -96,8 +96,8 @@ public function testRelationshipWithMultiIdLinkage() new Relationship( 'fruits', new MultiLinkage( - new ResourceId('apples', '1'), - new ResourceId('pears', '2') + new ResourceIdentifier('apples', '1'), + new ResourceIdentifier('pears', '2') ) ) ); @@ -121,28 +121,28 @@ public function testRelationshipWithEmptyMultiIdLinkage() public function testCanNotCreateIdAttribute() { $this->expectException(\DomainException::class); - $this->expectExceptionMessage("Can not use 'id' as age resource field"); + $this->expectExceptionMessage("Can not use 'id' as a resource field"); new Attribute('id', 'foo'); } public function testCanNotCreateTypeAttribute() { $this->expectException(\DomainException::class); - $this->expectExceptionMessage("Can not use 'type' as age resource field"); + $this->expectExceptionMessage("Can not use 'type' as a resource field"); new Attribute('type', 'foo'); } public function testCanNotCreateIdRelationship() { $this->expectException(\DomainException::class); - $this->expectExceptionMessage("Can not use 'id' as age resource field"); + $this->expectExceptionMessage("Can not use 'id' as a resource field"); new Relationship('id', new Meta([])); } public function testCanNotCreateTypeRelationship() { $this->expectException(\DomainException::class); - $this->expectExceptionMessage("Can not use 'type' as age resource field"); + $this->expectExceptionMessage("Can not use 'type' as a resource field"); new Relationship('type', new Meta([])); } From 473df51c26445cff4e43f0f50a9f203e03dba0ec Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Sun, 25 Feb 2018 21:09:32 -0800 Subject: [PATCH 27/37] Tests pass --- src/PrimaryData/ResourceField.php | 1 - test/LinkTest.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/PrimaryData/ResourceField.php b/src/PrimaryData/ResourceField.php index c151206..ad68310 100644 --- a/src/PrimaryData/ResourceField.php +++ b/src/PrimaryData/ResourceField.php @@ -4,7 +4,6 @@ namespace JsonApiPhp\JsonApi\PrimaryData; use JsonApiPhp\JsonApi\AttachableValue; -use function JsonApiPhp\JsonApi\isValidMemberName; /** * @internal diff --git a/test/LinkTest.php b/test/LinkTest.php index 6a3eb47..13f28ef 100644 --- a/test/LinkTest.php +++ b/test/LinkTest.php @@ -22,5 +22,4 @@ public function testLinkObject() new LinkObject('http://example.com', new Meta(['foo' => 'bar'])) ); } - } From 8fd9fd02a67e5be95bb96b7882fffadbaae59524 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Sun, 25 Feb 2018 21:13:58 -0800 Subject: [PATCH 28/37] Naming --- src/Error.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Error.php b/src/Error.php index fa8bdf5..b69ff88 100644 --- a/src/Error.php +++ b/src/Error.php @@ -7,9 +7,9 @@ final class Error extends JsonSerializableValue implements MandatoryErrorDocumentMember { - public function __construct(ErrorMember ...$errors) + public function __construct(ErrorMember ...$members) { - parent::__construct(combine(...$errors)); + parent::__construct(combine(...$members)); } public function attachTo(object $o) From 7caba5f24e7b0936aa7f061080b75b31c80e4ac6 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Sun, 25 Feb 2018 21:25:32 -0800 Subject: [PATCH 29/37] Naming --- src/Error.php | 2 +- src/ErrorDocument.php | 2 +- src/MandatoryErrorDocumentMember.php | 11 ----------- 3 files changed, 2 insertions(+), 13 deletions(-) delete mode 100644 src/MandatoryErrorDocumentMember.php diff --git a/src/Error.php b/src/Error.php index b69ff88..5b25fe4 100644 --- a/src/Error.php +++ b/src/Error.php @@ -5,7 +5,7 @@ use JsonApiPhp\JsonApi\Error\ErrorMember; -final class Error extends JsonSerializableValue implements MandatoryErrorDocumentMember +final class Error extends JsonSerializableValue implements ErrorDocumentMember { public function __construct(ErrorMember ...$members) { diff --git a/src/ErrorDocument.php b/src/ErrorDocument.php index e65b036..2e0a33d 100644 --- a/src/ErrorDocument.php +++ b/src/ErrorDocument.php @@ -5,7 +5,7 @@ final class ErrorDocument extends JsonSerializableValue { - public function __construct(MandatoryErrorDocumentMember $error, ErrorDocumentMember ...$members) + public function __construct(Error $error, ErrorDocumentMember ...$members) { parent::__construct(combine($error, ...$members)); } diff --git a/src/MandatoryErrorDocumentMember.php b/src/MandatoryErrorDocumentMember.php deleted file mode 100644 index c35c907..0000000 --- a/src/MandatoryErrorDocumentMember.php +++ /dev/null @@ -1,11 +0,0 @@ - Date: Mon, 26 Feb 2018 14:34:45 -0800 Subject: [PATCH 30/37] Tests pass --- CONTRIBUTING.md | 3 +- README.md | 58 ++++++++++------------- examples/readme_1.php | 27 +++++++++++ src/Error.php | 4 +- src/Error/AboutLink.php | 15 ------ src/Error/Code.php | 2 +- src/Error/Detail.php | 2 +- src/Error/Id.php | 2 +- src/Error/{ErrorMember.php => Member.php} | 2 +- src/Error/{Source => }/Parameter.php | 12 ++++- src/Error/{Source => }/Pointer.php | 12 ++++- src/Error/Source.php | 16 ------- src/Error/Source/SourceMember.php | 13 ----- src/Error/Status.php | 2 +- src/Error/Title.php | 2 +- src/Link/AboutLink.php | 14 ++++++ src/Meta.php | 4 +- src/{Linkage => }/MultiLinkage.php | 4 +- src/PrimaryData/ResourceIdentifier.php | 2 +- src/PrimaryData/ResourceObject.php | 2 +- src/{Linkage => }/SingleLinkage.php | 4 +- test/CompoundDocumentTest.php | 20 ++++---- test/DocumentTest.php | 4 +- test/ErrorTest.php | 13 ++--- test/ResourceObjectTest.php | 4 +- tmp.js | 33 ------------- 26 files changed, 121 insertions(+), 155 deletions(-) create mode 100644 examples/readme_1.php delete mode 100644 src/Error/AboutLink.php rename src/Error/{ErrorMember.php => Member.php} (74%) rename src/Error/{Source => }/Parameter.php (53%) rename src/Error/{Source => }/Pointer.php (53%) delete mode 100644 src/Error/Source.php delete mode 100644 src/Error/Source/SourceMember.php create mode 100644 src/Link/AboutLink.php rename src/{Linkage => }/MultiLinkage.php (87%) rename src/{Linkage => }/SingleLinkage.php (83%) delete mode 100644 tmp.js diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0f3169b..761757a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,5 +5,4 @@ Thank you for you interest! Here are some key guidelines. We want to follow the latest actual [PSR](http://www.php-fig.org/psr/) standards. ## Tests are the documentation -The use cases and library's API must be expressed in a form of tests. We use PHPUnit, but it does not mean all tests -are expected to be unit tests. No logic must be written without a supporting test. +The use cases and library's API must be expressed as tests. No logic must be written without a supporting test. diff --git a/README.md b/README.md index 6da70b0..6d4cb43 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,6 @@ -# !!! WORK IN PROGRESS !!! +# [JSON API](http://jsonapi.org) spec implemented in PHP 7. Immutable - -# Implementation of [JSON API](http://jsonapi.org) in PHP 7 -This library is an attempt to express business rules of JSON API specification in a set of PHP 7 classes. - -A simple example to illustrate the general idea. This JSON representation from -[the documentation](http://jsonapi.org/format/#document-resource-objects) - +JSON: ```json { "data": { @@ -30,32 +24,32 @@ A simple example to illustrate the general idea. This JSON representation from } } ``` -can be built with the following php code: - +PHP: ```php setLink('self', '/articles/1/relationships/author'); -$author->setLink('related', '/articles/1/author'); -$articles = new ResourceObject('articles', '1'); -$articles->setRelationship('author', $author); -$articles->setAttribute('title', 'Rails is Omakase'); -echo json_encode(Document::fromResource($articles), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); +echo json_encode( + new DataDocument( + new ResourceObject('articles', '1', + new Attribute('title', 'Rails is Omakase'), + new Relationship('author', + new SingleLinkage(new ResourceIdentifier('author', '9')), + new SelfLink(new Url('/articles/1/relationships/author')), + new RelatedLink(new Url('/articles/1/author')) + ) + ) + ), + JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES +); ``` - -Please refer to [the tests](./test) for the full API documentation: -* [Documents](./test/Document/DocumentTest.php). Creating documents with primary data, errors, and meta. -Adding links and API version to a document. - * [Compound Documents](./test/Document/CompoundDocumentTest.php). Resource linkage. -* [Errors](./test/Document/ErrorTest.php) -* [Resources](./test/Document/Resource/ResourceTest.php) -* [Relationships](./test/Document/Resource/Relationship/RelationshipTest.php) -* [Linkage](./test/Document/Resource/Relationship/LinkageTest.php) - ## Installation -With [composer](https://getcomposer.org/): `json-api-php/json-api`. +`composer require json-api-php/json-api` diff --git a/examples/readme_1.php b/examples/readme_1.php new file mode 100644 index 0000000..6e65330 --- /dev/null +++ b/examples/readme_1.php @@ -0,0 +1,27 @@ +source)) { + $o->source = (object) []; + } + parent::attachTo($o->source); + } } diff --git a/src/Error/Source/Pointer.php b/src/Error/Pointer.php similarity index 53% rename from src/Error/Source/Pointer.php rename to src/Error/Pointer.php index 2cfb460..85a40e3 100644 --- a/src/Error/Source/Pointer.php +++ b/src/Error/Pointer.php @@ -1,11 +1,11 @@ source)) { + $o->source = (object) []; + } + parent::attachTo($o->source); + } } diff --git a/src/Error/Source.php b/src/Error/Source.php deleted file mode 100644 index 6e53d35..0000000 --- a/src/Error/Source.php +++ /dev/null @@ -1,16 +0,0 @@ -toResourceId()->equals($this); + return $resource->toIdentifier()->equals($this); } } diff --git a/src/PrimaryData/ResourceObject.php b/src/PrimaryData/ResourceObject.php index 785da5a..dac8aab 100644 --- a/src/PrimaryData/ResourceObject.php +++ b/src/PrimaryData/ResourceObject.php @@ -30,7 +30,7 @@ public function __construct(string $type, string $id = null, ResourceMember ...$ } } - public function toResourceId(): ResourceIdentifier + public function toIdentifier(): ResourceIdentifier { return new ResourceIdentifier($this->type, $this->id); } diff --git a/src/Linkage/SingleLinkage.php b/src/SingleLinkage.php similarity index 83% rename from src/Linkage/SingleLinkage.php rename to src/SingleLinkage.php index caf3534..1de38a6 100644 --- a/src/Linkage/SingleLinkage.php +++ b/src/SingleLinkage.php @@ -1,13 +1,11 @@ toResourceId())) + new Relationship('author', new SingleLinkage($dan->toIdentifier())) ); $document = new CompoundDocument( @@ -59,15 +59,15 @@ public function testOfficialDocsExample() new SelfLink(new Url('http://example.com/articles/1')), new Relationship( 'author', - new SingleLinkage($dan->toResourceId()), + new SingleLinkage($dan->toIdentifier()), new SelfLink(new Url('http://example.com/articles/1/relationships/author')), new RelatedLink(new Url('http://example.com/articles/1/author')) ), new Relationship( 'comments', new MultiLinkage( - $comment05->toResourceId(), - $comment12->toResourceId() + $comment05->toIdentifier(), + $comment12->toIdentifier() ), new SelfLink(new Url('http://example.com/articles/1/relationships/comments')), new RelatedLink(new Url('http://example.com/articles/1/comments')) @@ -222,7 +222,7 @@ public function testIncludedResourceMayBeIdentifiedByLinkageInPrimaryData() $article = new ResourceObject( 'articles', '1', - new Relationship('author', new SingleLinkage($author->toResourceId())) + new Relationship('author', new SingleLinkage($author->toIdentifier())) ); $doc = new CompoundDocument($article, new Included($author)); $this->assertNotEmpty($doc); @@ -235,12 +235,12 @@ public function testIncludedResourceMayBeIdentifiedByAnotherLinkedResource() 'books', '2', new Attribute('name', 'Domain Driven Design'), - new Relationship('author', new SingleLinkage($writer->toResourceId())) + new Relationship('author', new SingleLinkage($writer->toIdentifier())) ); $cart = new ResourceObject( 'shopping-carts', '1', - new Relationship('contents', new MultiLinkage($book->toResourceId())) + new Relationship('contents', new MultiLinkage($book->toIdentifier())) ); $doc = new CompoundDocument($cart, new Included($book, $writer)); $this->assertNotEmpty($doc); @@ -254,6 +254,6 @@ public function testIncludedResourceMayBeIdentifiedByAnotherLinkedResource() public function testCanNotBeManyIncludedResourcesWithEqualIdentifiers() { $apple = new ResourceObject('apples', '1'); - new CompoundDocument($apple->toResourceId(), new Included($apple, $apple)); + new CompoundDocument($apple->toIdentifier(), new Included($apple, $apple)); } } diff --git a/test/DocumentTest.php b/test/DocumentTest.php index a1309c8..2ded444 100644 --- a/test/DocumentTest.php +++ b/test/DocumentTest.php @@ -184,8 +184,8 @@ public function testSingleResourceObjectDocument() new ResourceObject( 'apples', '1', - new Meta(['foo' => 'bar']), - new Attribute('title', 'Rails is Omakase') + new Attribute('title', 'Rails is Omakase'), + new Meta(['foo' => 'bar']) ) ) ); diff --git a/test/ErrorTest.php b/test/ErrorTest.php index eaa9181..dffd32a 100644 --- a/test/ErrorTest.php +++ b/test/ErrorTest.php @@ -4,15 +4,14 @@ namespace JsonApiPhp\JsonApi\Document; use JsonApiPhp\JsonApi\Error; -use JsonApiPhp\JsonApi\Error\AboutLink; use JsonApiPhp\JsonApi\Error\Code; use JsonApiPhp\JsonApi\Error\Detail; use JsonApiPhp\JsonApi\Error\Id; -use JsonApiPhp\JsonApi\Error\Source; -use JsonApiPhp\JsonApi\Error\Source\Parameter; -use JsonApiPhp\JsonApi\Error\Source\Pointer; +use JsonApiPhp\JsonApi\Error\Parameter; +use JsonApiPhp\JsonApi\Error\Pointer; use JsonApiPhp\JsonApi\Error\Status; use JsonApiPhp\JsonApi\Error\Title; +use JsonApiPhp\JsonApi\Link\AboutLink; use JsonApiPhp\JsonApi\Link\Url; use JsonApiPhp\JsonApi\Meta; use JsonApiPhp\JsonApi\Test\BaseTestCase; @@ -55,10 +54,8 @@ public function testErrorWithFullSetOfProperties() new Code('OMG'), new Title('Error'), new Detail('Nothing is found'), - new Source( - new Pointer('/data'), - new Parameter('test_param') - ), + new Pointer('/data'), + new Parameter('test_param'), new Meta(['foo' => 'bar']) ) ); diff --git a/test/ResourceObjectTest.php b/test/ResourceObjectTest.php index 33abdb7..a9a9ca2 100644 --- a/test/ResourceObjectTest.php +++ b/test/ResourceObjectTest.php @@ -6,13 +6,13 @@ use JsonApiPhp\JsonApi\Link\RelatedLink; use JsonApiPhp\JsonApi\Link\SelfLink; use JsonApiPhp\JsonApi\Link\Url; -use JsonApiPhp\JsonApi\Linkage\MultiLinkage; -use JsonApiPhp\JsonApi\Linkage\SingleLinkage; use JsonApiPhp\JsonApi\Meta; +use JsonApiPhp\JsonApi\MultiLinkage; use JsonApiPhp\JsonApi\PrimaryData\Attribute; use JsonApiPhp\JsonApi\PrimaryData\ResourceIdentifier; use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; use JsonApiPhp\JsonApi\Relationship; +use JsonApiPhp\JsonApi\SingleLinkage; class ResourceObjectTest extends BaseTestCase { diff --git a/tmp.js b/tmp.js deleted file mode 100644 index 4ae28f0..0000000 --- a/tmp.js +++ /dev/null @@ -1,33 +0,0 @@ -var age = Number(process.argv[2]); - -if (age < 10) { - console.log('нельзя в кино'); -} -else -{ - if (age < 20) { - console.log('только с родителями'); - } - else - { - if (age < 30) { - console.log('билет по полной стоимости'); - } - else - { - if (age < 40) { - console.log('скидка 5%'); - } - else - { - if (age < 50) { - console.log('скидка 10%'); - } - else - { - console.log('скидка 30%'); - } - } - } - } -} \ No newline at end of file From d4dc73952ec54cbf73b3311617e7f49fe27411e5 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Mon, 26 Feb 2018 15:23:06 -0800 Subject: [PATCH 31/37] Tests pass --- src/EmptySet.php | 20 ---------------- src/Error/Parameter.php | 6 ++--- src/Error/Pointer.php | 6 ++--- src/JsonApi.php | 1 - src/Link/AttachableLink.php | 6 ++--- src/PrimaryData/ResourceField.php | 8 +++---- src/PrimaryData/ResourceIdentifier.php | 8 +++---- src/PrimaryData/ResourceIdentifierSet.php | 4 ++-- src/PrimaryData/ResourceObject.php | 28 +++++++++++------------ src/PrimaryData/ResourceObjectSet.php | 4 ++-- src/Relationship.php | 5 +--- src/SingleLinkage.php | 2 +- src/functions.php | 8 +++++++ test/CompoundDocumentTest.php | 3 +-- test/DocumentTest.php | 3 +-- test/ResourceObjectTest.php | 5 ++++ 16 files changed, 49 insertions(+), 68 deletions(-) delete mode 100644 src/EmptySet.php diff --git a/src/EmptySet.php b/src/EmptySet.php deleted file mode 100644 index eecc272..0000000 --- a/src/EmptySet.php +++ /dev/null @@ -1,20 +0,0 @@ -source)) { - $o->source = (object) []; - } - parent::attachTo($o->source); + parent::attachTo(child($o, 'source')); } } diff --git a/src/Error/Pointer.php b/src/Error/Pointer.php index 85a40e3..e55acc3 100644 --- a/src/Error/Pointer.php +++ b/src/Error/Pointer.php @@ -4,6 +4,7 @@ namespace JsonApiPhp\JsonApi\Error; use JsonApiPhp\JsonApi\AttachableValue; +use function JsonApiPhp\JsonApi\child; final class Pointer extends AttachableValue implements Member { @@ -17,9 +18,6 @@ public function __construct(string $pointer) public function attachTo(object $o) { - if (empty($o->source)) { - $o->source = (object) []; - } - parent::attachTo($o->source); + parent::attachTo(child($o, 'source')); } } diff --git a/src/JsonApi.php b/src/JsonApi.php index f299ec7..9264e15 100644 --- a/src/JsonApi.php +++ b/src/JsonApi.php @@ -13,7 +13,6 @@ public function __construct(string $version, Meta $meta = null) if ($meta) { $meta->attachTo($jsonapi); } - parent::__construct('jsonapi', $jsonapi); } } diff --git a/src/Link/AttachableLink.php b/src/Link/AttachableLink.php index 21a836e..c6833b1 100644 --- a/src/Link/AttachableLink.php +++ b/src/Link/AttachableLink.php @@ -4,6 +4,7 @@ namespace JsonApiPhp\JsonApi\Link; use JsonApiPhp\JsonApi\AttachableValue; +use function JsonApiPhp\JsonApi\child; /** * @internal @@ -17,9 +18,6 @@ public function __construct(string $key, Link $link) public function attachTo(object $o) { - if (!isset($o->links)) { - $o->links = (object) []; - } - parent::attachTo($o->links); + parent::attachTo(child($o, 'links')); } } diff --git a/src/PrimaryData/ResourceField.php b/src/PrimaryData/ResourceField.php index ad68310..92f61ce 100644 --- a/src/PrimaryData/ResourceField.php +++ b/src/PrimaryData/ResourceField.php @@ -24,13 +24,13 @@ public function __construct(string $key, $value) $this->key = $key; } - public function toKey(): string + private function isReservedWord(string $key): bool { - return $this->key; + return in_array($key, ['id', 'type']); } - private function isReservedWord(string $key): bool + public function toKey(): string { - return in_array($key, ['id', 'type']); + return $this->key; } } diff --git a/src/PrimaryData/ResourceIdentifier.php b/src/PrimaryData/ResourceIdentifier.php index 7faacb8..807b3a7 100644 --- a/src/PrimaryData/ResourceIdentifier.php +++ b/src/PrimaryData/ResourceIdentifier.php @@ -25,13 +25,13 @@ public function __construct(string $type, string $id, Meta $meta = null) $this->id = $id; } - public function equals(ResourceIdentifier $that): bool + public function identifies(ResourceObject $resource): bool { - return $this->type === $that->type && $this->id === $that->id; + return $resource->toIdentifier()->equals($this); } - public function identifies(ResourceObject $resource): bool + public function equals(ResourceIdentifier $that): bool { - return $resource->toIdentifier()->equals($this); + return $this->type === $that->type && $this->id === $that->id; } } diff --git a/src/PrimaryData/ResourceIdentifierSet.php b/src/PrimaryData/ResourceIdentifierSet.php index 57f5cbe..4afce25 100644 --- a/src/PrimaryData/ResourceIdentifierSet.php +++ b/src/PrimaryData/ResourceIdentifierSet.php @@ -12,9 +12,9 @@ final class ResourceIdentifierSet extends AttachableValue implements PrimaryData */ private $identifiers = []; - public function __construct(ResourceIdentifier $identifier, ResourceIdentifier ...$ids) + public function __construct(ResourceIdentifier ...$identifiers) { - $this->identifiers = func_get_args(); + $this->identifiers = $identifiers; parent::__construct('data', $this->identifiers); } diff --git a/src/PrimaryData/ResourceObject.php b/src/PrimaryData/ResourceObject.php index dac8aab..c15d200 100644 --- a/src/PrimaryData/ResourceObject.php +++ b/src/PrimaryData/ResourceObject.php @@ -30,6 +30,20 @@ public function __construct(string $type, string $id = null, ResourceMember ...$ } } + private function checkUniqueness(ResourceMember ...$members): void + { + $keys = []; + foreach ($members as $member) { + if ($member instanceof ResourceField) { + $key = $member->toKey(); + if (isset($keys[$key])) { + throw new \DomainException("Field '$key' already exists'"); + } + $keys[$key] = true; + } + } + } + public function toIdentifier(): ResourceIdentifier { return new ResourceIdentifier($this->type, $this->id); @@ -49,18 +63,4 @@ public function __toString(): string { return "$this->type:$this->id"; } - - private function checkUniqueness(ResourceMember ...$members): void - { - $keys = []; - foreach ($members as $member) { - if ($member instanceof ResourceField) { - $key = $member->toKey(); - if (isset($keys[$key])) { - throw new \DomainException("Field '$key' already exists'"); - } - $keys[$key] = true; - } - } - } } diff --git a/src/PrimaryData/ResourceObjectSet.php b/src/PrimaryData/ResourceObjectSet.php index e5e8c5a..ccb9ef7 100644 --- a/src/PrimaryData/ResourceObjectSet.php +++ b/src/PrimaryData/ResourceObjectSet.php @@ -12,9 +12,9 @@ final class ResourceObjectSet extends AttachableValue implements PrimaryData */ private $resources; - public function __construct(ResourceObject $resource, ResourceObject ...$resources) + public function __construct(ResourceObject ...$resources) { - $this->resources = func_get_args(); + $this->resources = $resources; parent::__construct('data', $this->resources); } diff --git a/src/Relationship.php b/src/Relationship.php index 135f9e4..11fd9ca 100644 --- a/src/Relationship.php +++ b/src/Relationship.php @@ -26,10 +26,7 @@ public function __construct(string $key, RelationshipMember $member, Relationshi public function attachTo(object $o) { - if (empty($o->relationships)) { - $o->relationships = (object) []; - } - parent::attachTo($o->relationships); + parent::attachTo(child($o, 'relationships')); } public function identifies(ResourceObject $resource): bool diff --git a/src/SingleLinkage.php b/src/SingleLinkage.php index 1de38a6..c12876b 100644 --- a/src/SingleLinkage.php +++ b/src/SingleLinkage.php @@ -19,6 +19,6 @@ public function __construct(ResourceIdentifier $identifier = null) public function identifies(ResourceObject $resource): bool { - return $this->identifier->identifies($resource); + return $this->identifier && $this->identifier->identifies($resource); } } diff --git a/src/functions.php b/src/functions.php index 0bcc685..e8d9382 100644 --- a/src/functions.php +++ b/src/functions.php @@ -11,3 +11,11 @@ function combine(Attachable ...$things): object } return $obj; } + +function child(object $o, string $name): object +{ + if (empty($o->{$name})) { + $o->{$name} = (object) []; + } + return $o->{$name}; +} diff --git a/test/CompoundDocumentTest.php b/test/CompoundDocumentTest.php index ec8e390..25675f3 100644 --- a/test/CompoundDocumentTest.php +++ b/test/CompoundDocumentTest.php @@ -4,7 +4,6 @@ namespace JsonApiPhp\JsonApi\Test; use JsonApiPhp\JsonApi\CompoundDocument; -use JsonApiPhp\JsonApi\EmptySet; use JsonApiPhp\JsonApi\Included; use JsonApiPhp\JsonApi\Link\LastLink; use JsonApiPhp\JsonApi\Link\NextLink; @@ -189,7 +188,7 @@ function () use ($included) { ], [ function () use ($included) { - return new CompoundDocument(new EmptySet(), $included); + return new CompoundDocument(new ResourceObjectSet(), $included); }, ], [ diff --git a/test/DocumentTest.php b/test/DocumentTest.php index 2ded444..686c68a 100644 --- a/test/DocumentTest.php +++ b/test/DocumentTest.php @@ -4,7 +4,6 @@ namespace JsonApiPhp\JsonApi\Test; use JsonApiPhp\JsonApi\DataDocument; -use JsonApiPhp\JsonApi\EmptySet; use JsonApiPhp\JsonApi\Error; use JsonApiPhp\JsonApi\Error\Id; use JsonApiPhp\JsonApi\ErrorDocument; @@ -200,7 +199,7 @@ public function testEmptySetDocument() } ', new DataDocument( - new EmptySet() + new ResourceObjectSet() ) ); } diff --git a/test/ResourceObjectTest.php b/test/ResourceObjectTest.php index a9a9ca2..28e0f90 100644 --- a/test/ResourceObjectTest.php +++ b/test/ResourceObjectTest.php @@ -208,4 +208,9 @@ public function testResourceIdCanBeOmitted() new ResourceObject('apples', null, new Attribute('color', 'red')) ); } + + public function testEmptySingleLinkageIdentifiesNothing() + { + $this->assertFalse((new SingleLinkage())->identifies(new ResourceObject('something', '1'))); + } } From 9619f1b44eabea51be9b92b244b8e8aa59e317dd Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Mon, 26 Feb 2018 15:59:20 -0800 Subject: [PATCH 32/37] Tests pass --- src/Attachable.php | 3 +- src/AttachableValue.php | 3 +- src/CompoundDocument.php | 5 ++- src/DataDocument.php | 3 +- src/DocumentMember.php | 3 +- src/Error.php | 3 +- src/Error/Code.php | 3 +- src/Error/Detail.php | 3 +- src/Error/Id.php | 3 +- src/Error/Parameter.php | 3 +- src/Error/Pointer.php | 3 +- src/Error/Status.php | 3 +- src/Error/Title.php | 3 +- src/ErrorDocument.php | 3 +- src/ErrorDocumentMember.php | 3 +- src/Included.php | 7 ++--- src/JsonApi.php | 3 +- src/JsonSerializableValue.php | 3 +- src/Link/AboutLink.php | 3 +- src/Link/AttachableLink.php | 3 +- src/Link/FirstLink.php | 3 +- src/Link/LastLink.php | 3 +- src/Link/Link.php | 3 +- src/Link/LinkObject.php | 3 +- src/Link/NextLink.php | 3 +- src/Link/PrevLink.php | 3 +- src/Link/RelatedLink.php | 3 +- src/Link/SelfLink.php | 3 +- src/Link/Url.php | 3 +- src/Meta.php | 3 +- src/MetaDocument.php | 3 +- src/MultiLinkage.php | 3 +- src/PrimaryData/Attribute.php | 10 +++--- src/PrimaryData/Identifier.php | 3 +- src/PrimaryData/NullData.php | 3 +- src/PrimaryData/PrimaryData.php | 3 +- src/PrimaryData/ResourceField.php | 15 +++------ src/PrimaryData/ResourceIdentifier.php | 5 ++- src/PrimaryData/ResourceIdentifierSet.php | 5 ++- src/PrimaryData/ResourceMember.php | 3 +- src/PrimaryData/ResourceObject.php | 38 ++++++++++------------- src/PrimaryData/ResourceObjectSet.php | 3 +- src/Relationship.php | 3 +- src/RelationshipMember.php | 3 +- src/SingleLinkage.php | 3 +- src/TopLevelDocumentMember.php | 3 +- src/functions.php | 3 +- test/BaseTestCase.php | 3 +- test/CompoundDocumentTest.php | 25 +++++++-------- test/DocumentTest.php | 3 +- test/ErrorTest.php | 3 +- test/JsonApiTest.php | 3 +- test/LinkTest.php | 3 +- test/ResourceObjectTest.php | 5 ++- 54 files changed, 94 insertions(+), 156 deletions(-) diff --git a/src/Attachable.php b/src/Attachable.php index 420fad2..ac3f6bf 100644 --- a/src/Attachable.php +++ b/src/Attachable.php @@ -1,5 +1,4 @@ -identifier())); } parent::__construct(combine($data, $included, ...$members)); } diff --git a/src/DataDocument.php b/src/DataDocument.php index 83a358f..64b4e31 100644 --- a/src/DataDocument.php +++ b/src/DataDocument.php @@ -1,5 +1,4 @@ -identifier()); if (isset($this->resources[$string_id])) { - throw new \DomainException("Resource $resource is already included"); + throw new \LogicException("Resource $string_id is already included"); } $this->resources[$string_id] = $resource; } diff --git a/src/JsonApi.php b/src/JsonApi.php index 9264e15..83b280a 100644 --- a/src/JsonApi.php +++ b/src/JsonApi.php @@ -1,5 +1,4 @@ -attributes)) { - $o->attributes = (object) []; - } - parent::attachTo($o->attributes); + parent::attachTo(child($o, 'attributes')); } } diff --git a/src/PrimaryData/Identifier.php b/src/PrimaryData/Identifier.php index 8b99367..88072e1 100644 --- a/src/PrimaryData/Identifier.php +++ b/src/PrimaryData/Identifier.php @@ -1,5 +1,4 @@ -isReservedWord($key)) { + if (in_array($key, ['id', 'type'])) { throw new \DomainException("Can not use '$key' as a resource field"); } parent::__construct($key, $value); $this->key = $key; } - private function isReservedWord(string $key): bool - { - return in_array($key, ['id', 'type']); - } - - public function toKey(): string + public function key(): string { return $this->key; } diff --git a/src/PrimaryData/ResourceIdentifier.php b/src/PrimaryData/ResourceIdentifier.php index 807b3a7..7842131 100644 --- a/src/PrimaryData/ResourceIdentifier.php +++ b/src/PrimaryData/ResourceIdentifier.php @@ -1,5 +1,4 @@ -toIdentifier()->equals($this); + return $resource->identifier()->equals($this); } public function equals(ResourceIdentifier $that): bool diff --git a/src/PrimaryData/ResourceIdentifierSet.php b/src/PrimaryData/ResourceIdentifierSet.php index 4afce25..a932c01 100644 --- a/src/PrimaryData/ResourceIdentifierSet.php +++ b/src/PrimaryData/ResourceIdentifierSet.php @@ -1,5 +1,4 @@ -identifiers = $identifiers; - parent::__construct('data', $this->identifiers); } public function identifies(ResourceObject $resource): bool diff --git a/src/PrimaryData/ResourceMember.php b/src/PrimaryData/ResourceMember.php index 0c6cc3d..0a88dff 100644 --- a/src/PrimaryData/ResourceMember.php +++ b/src/PrimaryData/ResourceMember.php @@ -1,5 +1,4 @@ -checkUniqueness(...$members); $obj = combine(...$members); - $obj->type = $this->type = $type; - $obj->id = $this->id = $id; + $obj->type = $type; + $obj->id = $id; parent::__construct('data', $obj); + $this->type = $type; + $this->id = $id; foreach ($members as $member) { if ($member instanceof Identifier) { $this->identifiers[] = $member; @@ -30,21 +31,7 @@ public function __construct(string $type, string $id = null, ResourceMember ...$ } } - private function checkUniqueness(ResourceMember ...$members): void - { - $keys = []; - foreach ($members as $member) { - if ($member instanceof ResourceField) { - $key = $member->toKey(); - if (isset($keys[$key])) { - throw new \DomainException("Field '$key' already exists'"); - } - $keys[$key] = true; - } - } - } - - public function toIdentifier(): ResourceIdentifier + public function identifier(): ResourceIdentifier { return new ResourceIdentifier($this->type, $this->id); } @@ -59,8 +46,17 @@ public function identifies(ResourceObject $resource): bool return false; } - public function __toString(): string + private function checkUniqueness(ResourceMember ...$members): void { - return "$this->type:$this->id"; + $keys = []; + foreach ($members as $member) { + if ($member instanceof ResourceField) { + $key = $member->key(); + if (isset($keys[$key])) { + throw new \LogicException("Field '$key' already exists'"); + } + $keys[$key] = true; + } + } } } diff --git a/src/PrimaryData/ResourceObjectSet.php b/src/PrimaryData/ResourceObjectSet.php index ccb9ef7..720ae28 100644 --- a/src/PrimaryData/ResourceObjectSet.php +++ b/src/PrimaryData/ResourceObjectSet.php @@ -1,5 +1,4 @@ -toIdentifier())) + new Relationship('author', new SingleLinkage($dan->identifier())) ); $document = new CompoundDocument( @@ -58,15 +57,15 @@ public function testOfficialDocsExample() new SelfLink(new Url('http://example.com/articles/1')), new Relationship( 'author', - new SingleLinkage($dan->toIdentifier()), + new SingleLinkage($dan->identifier()), new SelfLink(new Url('http://example.com/articles/1/relationships/author')), new RelatedLink(new Url('http://example.com/articles/1/author')) ), new Relationship( 'comments', new MultiLinkage( - $comment05->toIdentifier(), - $comment12->toIdentifier() + $comment05->identifier(), + $comment12->identifier() ), new SelfLink(new Url('http://example.com/articles/1/relationships/comments')), new RelatedLink(new Url('http://example.com/articles/1/comments')) @@ -173,7 +172,7 @@ public function testOfficialDocsExample() public function testFullLinkage(callable $create_doc) { $this->expectException(\DomainException::class); - $this->expectExceptionMessage('Full linkage required for apples:1'); + $this->expectExceptionMessage('Full linkage required for {"type":"apples","id":"1"}'); $create_doc(); } @@ -221,7 +220,7 @@ public function testIncludedResourceMayBeIdentifiedByLinkageInPrimaryData() $article = new ResourceObject( 'articles', '1', - new Relationship('author', new SingleLinkage($author->toIdentifier())) + new Relationship('author', new SingleLinkage($author->identifier())) ); $doc = new CompoundDocument($article, new Included($author)); $this->assertNotEmpty($doc); @@ -234,12 +233,12 @@ public function testIncludedResourceMayBeIdentifiedByAnotherLinkedResource() 'books', '2', new Attribute('name', 'Domain Driven Design'), - new Relationship('author', new SingleLinkage($writer->toIdentifier())) + new Relationship('author', new SingleLinkage($writer->identifier())) ); $cart = new ResourceObject( 'shopping-carts', '1', - new Relationship('contents', new MultiLinkage($book->toIdentifier())) + new Relationship('contents', new MultiLinkage($book->identifier())) ); $doc = new CompoundDocument($cart, new Included($book, $writer)); $this->assertNotEmpty($doc); @@ -247,12 +246,12 @@ public function testIncludedResourceMayBeIdentifiedByAnotherLinkedResource() /** * A compound document MUST NOT include more than one resource object for each type and id pair. - * @expectedException \DomainException - * @expectedExceptionMessage Resource apples:1 is already included + * @expectedException \LogicException + * @expectedExceptionMessage Resource {"type":"apples","id":"1"} is already included */ public function testCanNotBeManyIncludedResourcesWithEqualIdentifiers() { $apple = new ResourceObject('apples', '1'); - new CompoundDocument($apple->toIdentifier(), new Included($apple, $apple)); + new CompoundDocument($apple->identifier(), new Included($apple, $apple)); } } diff --git a/test/DocumentTest.php b/test/DocumentTest.php index 686c68a..7ea32b1 100644 --- a/test/DocumentTest.php +++ b/test/DocumentTest.php @@ -1,5 +1,4 @@ -expectException(\DomainException::class); + $this->expectException(\LogicException::class); $this->expectExceptionMessage("Field 'foo' already exists"); new ResourceObject( 'apples', From 77357dd22abdd7b0b19fc4a3a5568a63aa2a018d Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Mon, 26 Feb 2018 16:36:11 -0800 Subject: [PATCH 33/37] Tests pass --- .php_cs.dist | 1 + README.md | 7 ++ examples/readme_1.php | 6 +- src/{PrimaryData => }/Attribute.php | 6 +- src/CompoundDocument.php | 2 +- src/Included.php | 2 - src/JsonApi.php | 2 +- src/MultiLinkage.php | 4 +- src/{PrimaryData => }/NullData.php | 4 +- src/PrimaryData/Identifier.php | 2 + src/Relationship.php | 1 - src/{PrimaryData => }/ResourceIdentifier.php | 7 +- .../ResourceIdentifierSet.php | 4 +- src/{PrimaryData => }/ResourceObject.php | 8 ++- src/{PrimaryData => }/ResourceObjectSet.php | 4 +- src/SingleLinkage.php | 2 - test/CompoundDocumentTest.php | 12 ++-- .../DataDocument/SingleResourceObjectTest.php | 68 +++++++++++++++++++ test/DocumentTest.php | 37 ++-------- test/ResourceObjectTest.php | 10 +-- 20 files changed, 117 insertions(+), 72 deletions(-) rename src/{PrimaryData => }/Attribute.php (53%) rename src/{PrimaryData => }/NullData.php (78%) rename src/{PrimaryData => }/ResourceIdentifier.php (79%) rename src/{PrimaryData => }/ResourceIdentifierSet.php (88%) rename src/{PrimaryData => }/ResourceObject.php (87%) rename src/{PrimaryData => }/ResourceObjectSet.php (88%) create mode 100644 test/DataDocument/SingleResourceObjectTest.php diff --git a/.php_cs.dist b/.php_cs.dist index 9c1b5d4..13b77ec 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -42,6 +42,7 @@ return PhpCsFixer\Config::create() 'psr0' => true, 'short_scalar_cast' => true, 'single_blank_line_before_namespace' => true, + 'single_quote' => true, 'standardize_not_equals' => true, 'strict_comparison' => true, 'ternary_operator_spaces' => true, diff --git a/README.md b/README.md index 6d4cb43..15772f1 100644 --- a/README.md +++ b/README.md @@ -53,3 +53,10 @@ echo json_encode( ``` ## Installation `composer require json-api-php/json-api` + +## Documentation + +The library API and use-cases are expressed in comprehensive suite of tests. +- Data Documents + - [With a single Resource Object](./test/DataDocument/SingleResourceObjectTest.php) +- dfd diff --git a/examples/readme_1.php b/examples/readme_1.php index 6e65330..61a11b1 100644 --- a/examples/readme_1.php +++ b/examples/readme_1.php @@ -1,14 +1,14 @@ identifier())); + throw new \DomainException('Full linkage required for '.json_encode($resource->identifier())); } parent::__construct(combine($data, $included, ...$members)); } diff --git a/src/Included.php b/src/Included.php index f1e4915..14d73ce 100644 --- a/src/Included.php +++ b/src/Included.php @@ -2,8 +2,6 @@ namespace JsonApiPhp\JsonApi; -use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; - final class Included extends AttachableValue implements DataDocumentMember, \IteratorAggregate { private $resources = []; diff --git a/src/JsonApi.php b/src/JsonApi.php index 83b280a..a769c17 100644 --- a/src/JsonApi.php +++ b/src/JsonApi.php @@ -4,7 +4,7 @@ final class JsonApi extends AttachableValue implements TopLevelDocumentMember, DataDocumentMember { - public function __construct(string $version, Meta $meta = null) + public function __construct(string $version = '1.0', Meta $meta = null) { $jsonapi = (object) [ 'version' => $version, diff --git a/src/MultiLinkage.php b/src/MultiLinkage.php index fea37a9..b1f3ccb 100644 --- a/src/MultiLinkage.php +++ b/src/MultiLinkage.php @@ -3,13 +3,11 @@ namespace JsonApiPhp\JsonApi; use JsonApiPhp\JsonApi\PrimaryData\Identifier; -use JsonApiPhp\JsonApi\PrimaryData\ResourceIdentifier; -use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; class MultiLinkage extends AttachableValue implements RelationshipMember, Identifier { /** - * @var \JsonApiPhp\JsonApi\PrimaryData\ResourceIdentifier[] + * @var \JsonApiPhp\JsonApi\ResourceIdentifier[] */ private $identifiers = []; diff --git a/src/PrimaryData/NullData.php b/src/NullData.php similarity index 78% rename from src/PrimaryData/NullData.php rename to src/NullData.php index a2f8c6b..7d39ca6 100644 --- a/src/PrimaryData/NullData.php +++ b/src/NullData.php @@ -1,8 +1,8 @@ assertEncodesTo( + ' + { + "data": { + "type": "apples", + "id": "1" + } + } + ', + new DataDocument( + new ResourceObject('apples', '1') + ) + ); + } + + public function testExtendedDocument() + { + $this->assertEncodesTo( + ' + { + "data": { + "type": "apples", + "id": "1", + "attributes": { + "color": "red" + }, + "meta": {"apple_meta": "foo"} + }, + "links": { + "self": "/apples/1" + }, + "jsonapi": { + "version": "1.0" + }, + "meta": {"document_meta": "bar"} + } + ', + new DataDocument( + new ResourceObject( + 'apples', + '1', + new Attribute('color', 'red'), + new Meta(['apple_meta' => 'foo']) + ), + new SelfLink(new Url('/apples/1')), + new JsonApi(), + new Meta(['document_meta' => 'bar']) + ) + ); + } +} diff --git a/test/DocumentTest.php b/test/DocumentTest.php index 7ea32b1..d549800 100644 --- a/test/DocumentTest.php +++ b/test/DocumentTest.php @@ -15,12 +15,11 @@ use JsonApiPhp\JsonApi\Link\Url; use JsonApiPhp\JsonApi\Meta; use JsonApiPhp\JsonApi\MetaDocument; -use JsonApiPhp\JsonApi\PrimaryData\Attribute; -use JsonApiPhp\JsonApi\PrimaryData\NullData; -use JsonApiPhp\JsonApi\PrimaryData\ResourceIdentifier; -use JsonApiPhp\JsonApi\PrimaryData\ResourceIdentifierSet; -use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; -use JsonApiPhp\JsonApi\PrimaryData\ResourceObjectSet; +use JsonApiPhp\JsonApi\NullData; +use JsonApiPhp\JsonApi\ResourceIdentifier; +use JsonApiPhp\JsonApi\ResourceIdentifierSet; +use JsonApiPhp\JsonApi\ResourceObject; +use JsonApiPhp\JsonApi\ResourceObjectSet; class DocumentTest extends BaseTestCase { @@ -163,32 +162,6 @@ public function testSingleResourceIdentifierDocument() ); } - public function testSingleResourceObjectDocument() - { - $this->assertEncodesTo( - ' - { - "data": { - "type": "apples", - "id": "1", - "attributes": { - "title": "Rails is Omakase" - }, - "meta": {"foo": "bar"} - } - } - ', - new DataDocument( - new ResourceObject( - 'apples', - '1', - new Attribute('title', 'Rails is Omakase'), - new Meta(['foo' => 'bar']) - ) - ) - ); - } - public function testEmptySetDocument() { $this->assertEncodesTo( diff --git a/test/ResourceObjectTest.php b/test/ResourceObjectTest.php index 144288b..723cc8b 100644 --- a/test/ResourceObjectTest.php +++ b/test/ResourceObjectTest.php @@ -2,15 +2,15 @@ namespace JsonApiPhp\JsonApi\Test; +use JsonApiPhp\JsonApi\Attribute; use JsonApiPhp\JsonApi\Link\RelatedLink; use JsonApiPhp\JsonApi\Link\SelfLink; use JsonApiPhp\JsonApi\Link\Url; use JsonApiPhp\JsonApi\Meta; use JsonApiPhp\JsonApi\MultiLinkage; -use JsonApiPhp\JsonApi\PrimaryData\Attribute; -use JsonApiPhp\JsonApi\PrimaryData\ResourceIdentifier; -use JsonApiPhp\JsonApi\PrimaryData\ResourceObject; use JsonApiPhp\JsonApi\Relationship; +use JsonApiPhp\JsonApi\ResourceIdentifier; +use JsonApiPhp\JsonApi\ResourceObject; use JsonApiPhp\JsonApi\SingleLinkage; class ResourceObjectTest extends BaseTestCase @@ -152,7 +152,7 @@ public function testCanNotCreateTypeRelationship() public function testAttributeMustOnlyHaveAllowedCharacters(string $invalid_char) { $this->expectException(\DomainException::class); - $this->expectExceptionMessage("Invalid character in a member name"); + $this->expectExceptionMessage('Invalid character in a member name'); new Attribute("foo{$invalid_char}bar", 'plus can not be used'); } @@ -163,7 +163,7 @@ public function testAttributeMustOnlyHaveAllowedCharacters(string $invalid_char) public function testRelationshipMustOnlyHaveAllowedCharacters(string $invalid_char) { $this->expectException(\DomainException::class); - $this->expectExceptionMessage("Invalid character in a member name"); + $this->expectExceptionMessage('Invalid character in a member name'); new Relationship("foo{$invalid_char}bar", new SingleLinkage()); } From 438d99a9c2bb345e90b82305e56823c8be131cf2 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Mon, 26 Feb 2018 18:37:04 -0800 Subject: [PATCH 34/37] Tests pass --- README.md | 28 +- examples/compound_doc.php | 2 + examples/{readme_1.php => simple_doc.php} | 1 - src/Error.php | 4 +- src/Error/Code.php | 2 +- src/Error/Detail.php | 2 +- src/Error/{Member.php => ErrorMember.php} | 2 +- src/Error/Id.php | 2 +- src/Error/Parameter.php | 2 +- src/Error/Pointer.php | 2 +- src/Error/Status.php | 2 +- src/Error/Title.php | 2 +- src/Link/AboutLink.php | 4 +- src/Meta.php | 20 +- src/PrimaryData/PrimaryData.php | 4 +- src/PrimaryData/ResourceField.php | 4 +- src/functions.php | 5 + .../ManyResourceIdentifiersTest.php | 72 +++++ test/DataDocument/ManyResourceObjectsTest.php | 85 ++++++ test/DataDocument/NullDataTest.php | 53 ++++ .../SingleResourceIdentifierTest.php | 63 +++++ .../DataDocument/SingleResourceObjectTest.php | 8 +- test/DocumentTest.php | 258 ------------------ test/ErrorDocumentTest.php | 122 +++++++++ test/ErrorTest.php | 62 ----- test/JsonApiTest.php | 2 +- test/LinkTest.php | 2 +- test/MetaDocumentTest.php | 51 ++++ test/MetaTest.php | 16 ++ test/PaginationLinksTest.php | 45 +++ test/ResourceObjectTest.php | 10 +- 31 files changed, 571 insertions(+), 366 deletions(-) create mode 100644 examples/compound_doc.php rename examples/{readme_1.php => simple_doc.php} (99%) rename src/Error/{Member.php => ErrorMember.php} (74%) create mode 100644 test/DataDocument/ManyResourceIdentifiersTest.php create mode 100644 test/DataDocument/ManyResourceObjectsTest.php create mode 100644 test/DataDocument/NullDataTest.php create mode 100644 test/DataDocument/SingleResourceIdentifierTest.php delete mode 100644 test/DocumentTest.php create mode 100644 test/ErrorDocumentTest.php delete mode 100644 test/ErrorTest.php create mode 100644 test/MetaDocumentTest.php create mode 100644 test/MetaTest.php create mode 100644 test/PaginationLinksTest.php diff --git a/README.md b/README.md index 15772f1..1837428 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # [JSON API](http://jsonapi.org) spec implemented in PHP 7. Immutable +The goal of this library is to ensure strict validity of JSON API documents being produced. + JSON: ```json { @@ -27,15 +29,10 @@ JSON: PHP: ```php {$name}; } + +function isValidName(string $name): bool +{ + return preg_match('/^(?=[^-_ ])[a-zA-Z0-9\x{0080}-\x{FFFF}-_ ]*(?<=[^-_ ])$/u', $name) === 1; +} diff --git a/test/DataDocument/ManyResourceIdentifiersTest.php b/test/DataDocument/ManyResourceIdentifiersTest.php new file mode 100644 index 0000000..6aaa5bf --- /dev/null +++ b/test/DataDocument/ManyResourceIdentifiersTest.php @@ -0,0 +1,72 @@ +assertEncodesTo( + ' + { + "data": [] + } + ', + new DataDocument( + new ResourceIdentifierSet() + ) + ); + } + + public function testExtendedDocument() + { + $this->assertEncodesTo( + ' + { + "data": [{ + "type": "apples", + "id": "1", + "meta": {"apple_meta": "foo"} + },{ + "type": "apples", + "id": "2", + "meta": {"apple_meta": "foo"} + }], + "links": { + "self": "/apples" + }, + "jsonapi": { + "version": "1.0" + }, + "meta": {"document_meta": "bar"} + } + ', + new DataDocument( + new ResourceIdentifierSet( + new ResourceIdentifier( + 'apples', + '1', + new Meta('apple_meta', 'foo') + ), + new ResourceIdentifier( + 'apples', + '2', + new Meta('apple_meta', 'foo') + ) + ), + new SelfLink(new Url('/apples')), + new JsonApi(), + new Meta('document_meta', 'bar') + ) + ); + } +} diff --git a/test/DataDocument/ManyResourceObjectsTest.php b/test/DataDocument/ManyResourceObjectsTest.php new file mode 100644 index 0000000..ef3da2c --- /dev/null +++ b/test/DataDocument/ManyResourceObjectsTest.php @@ -0,0 +1,85 @@ +assertEncodesTo( + ' + { + "data": [] + } + ', + new DataDocument( + new ResourceObjectSet() + ) + ); + } + + public function testExtendedDocument() + { + $this->assertEncodesTo( + ' + { + "data": [{ + "type": "apples", + "id": "1", + "attributes": { + "color": "red", + "sort": "Fuji" + }, + "meta": {"apple_meta": "foo"} + },{ + "type": "apples", + "id": "2", + "attributes": { + "color": "yellow", + "sort": "Gala" + }, + "meta": {"apple_meta": "foo"} + }], + "links": { + "self": "/apples" + }, + "jsonapi": { + "version": "1.0" + }, + "meta": {"document_meta": "bar"} + } + ', + new DataDocument( + new ResourceObjectSet( + new ResourceObject( + 'apples', + '1', + new Attribute('color', 'red'), + new Attribute('sort', 'Fuji'), + new Meta('apple_meta', 'foo') + ), + new ResourceObject( + 'apples', + '2', + new Attribute('color', 'yellow'), + new Attribute('sort', 'Gala'), + new Meta('apple_meta', 'foo') + ) + ), + new SelfLink(new Url('/apples')), + new JsonApi(), + new Meta('document_meta', 'bar') + ) + ); + } +} diff --git a/test/DataDocument/NullDataTest.php b/test/DataDocument/NullDataTest.php new file mode 100644 index 0000000..9d436e7 --- /dev/null +++ b/test/DataDocument/NullDataTest.php @@ -0,0 +1,53 @@ +assertEncodesTo( + ' + { + "data": null + } + ', + new DataDocument( + new NullData() + ) + ); + } + + public function testExtendedDocument() + { + $this->assertEncodesTo( + ' + { + "data": null, + "links": { + "self": "/apples/1" + }, + "jsonapi": { + "version": "1.0" + }, + "meta": {"document_meta": "bar"} + } + ', + new DataDocument( + new NullData(), + new SelfLink(new Url('/apples/1')), + new JsonApi(), + new Meta('document_meta', 'bar') + ) + ); + } +} diff --git a/test/DataDocument/SingleResourceIdentifierTest.php b/test/DataDocument/SingleResourceIdentifierTest.php new file mode 100644 index 0000000..becb005 --- /dev/null +++ b/test/DataDocument/SingleResourceIdentifierTest.php @@ -0,0 +1,63 @@ +assertEncodesTo( + ' + { + "data": { + "type": "apples", + "id": "1" + } + } + ', + new DataDocument( + new ResourceIdentifier('apples', '1') + ) + ); + } + + public function testExtendedDocument() + { + $this->assertEncodesTo( + ' + { + "data": { + "type": "apples", + "id": "1", + "meta": {"apple_meta": "foo"} + }, + "links": { + "self": "/apples/1" + }, + "jsonapi": { + "version": "1.0" + }, + "meta": {"document_meta": "bar"} + } + ', + new DataDocument( + new ResourceIdentifier( + 'apples', + '1', + new Meta('apple_meta', 'foo') + ), + new SelfLink(new Url('/apples/1')), + new JsonApi(), + new Meta('document_meta', 'bar') + ) + ); + } +} diff --git a/test/DataDocument/SingleResourceObjectTest.php b/test/DataDocument/SingleResourceObjectTest.php index 5604db9..5d9af4b 100644 --- a/test/DataDocument/SingleResourceObjectTest.php +++ b/test/DataDocument/SingleResourceObjectTest.php @@ -39,7 +39,8 @@ public function testExtendedDocument() "type": "apples", "id": "1", "attributes": { - "color": "red" + "color": "red", + "sort": "Fuji" }, "meta": {"apple_meta": "foo"} }, @@ -57,11 +58,12 @@ public function testExtendedDocument() 'apples', '1', new Attribute('color', 'red'), - new Meta(['apple_meta' => 'foo']) + new Attribute('sort', 'Fuji'), + new Meta('apple_meta', 'foo') ), new SelfLink(new Url('/apples/1')), new JsonApi(), - new Meta(['document_meta' => 'bar']) + new Meta('document_meta', 'bar') ) ); } diff --git a/test/DocumentTest.php b/test/DocumentTest.php deleted file mode 100644 index d549800..0000000 --- a/test/DocumentTest.php +++ /dev/null @@ -1,258 +0,0 @@ -assertEncodesTo( - ' - { - "meta": { - "foo": "bar" - } - } - ', - new MetaDocument(new Meta(['foo' => 'bar'])) - ); - } - - /** - * A meta document may contain jsonapi member - */ - public function testMetaDocumentWithExtraMembers() - { - $this->assertEncodesTo( - ' - { - "meta": { - "foo": "bar" - }, - "jsonapi": { - "version": "1.0" - } - } - ', - new MetaDocument( - new Meta(['foo' => 'bar']), - new JsonApi('1.0') - ) - ); - } - - public function testErrorDocumentMayContainJustErrors() - { - $this->assertEncodesTo( - ' - { - "errors": [ - { - "id": "first error" - }, - { - "id": "second error" - } - ] - } - ', - new ErrorDocument( - new Error(new Id('first error')), - new Error(new Id('second error')) - ) - ); - } - - public function testErrorDocumentMayContainExtraMembers() - { - $this->assertEncodesTo( - ' - { - "errors": [ - { - "id": "first error" - }, - { - "id": "second error" - } - ], - "meta": {"foo": "bar"}, - "jsonapi": { - "version": "1.0" - } - } - ', - new ErrorDocument( - new Error(new Id('first error')), - new Error(new Id('second error')), - new Meta(['foo' => 'bar']), - new JsonApi('1.0') - ) - ); - } - - public function testNullDocument() - { - $this->assertEncodesTo('{"data": null}', new DataDocument(new NullData())); - } - - public function testNullDocumentWithExtraMembers() - { - $this->assertEncodesTo( - ' - { - "data": null, - "meta": {"foo": "bar"}, - "jsonapi": { - "version": "1.0" - }, - "links": { - "self": "http://self" - } - } - ', - new DataDocument( - new NullData(), - new Meta(['foo' => 'bar']), - new JsonApi('1.0'), - new SelfLink( - new Url('http://self') - ) - ) - ); - } - - public function testSingleResourceIdentifierDocument() - { - $this->assertEncodesTo( - ' - { - "data": { - "type": "apples", - "id": "1", - "meta": {"foo": "bar"} - } - } - ', - new DataDocument( - new ResourceIdentifier('apples', '1', new Meta(['foo' => 'bar'])) - ) - ); - } - - public function testEmptySetDocument() - { - $this->assertEncodesTo( - ' - { - "data": [] - } - ', - new DataDocument( - new ResourceObjectSet() - ) - ); - } - - public function testResourceSetDocument() - { - $this->assertEncodesTo( - ' - { - "data": [ - { - "type": "apples", - "id": "1" - }, - { - "type": "pears", - "id": "2" - } - ] - } ', - new DataDocument( - new ResourceObjectSet( - new ResourceObject('apples', '1'), - new ResourceObject('pears', '2') - ) - ) - ); - } - - public function testResourceIdSetDocument() - { - $this->assertEncodesTo( - ' - { - "data": [ - { - "type": "apples", - "id": "1" - }, - { - "type": "pears", - "id": "2" - } - ] - } ', - new DataDocument( - new ResourceIdentifierSet( - new ResourceIdentifier('apples', '1'), - new ResourceIdentifier('pears', '2') - ) - ) - ); - } - - public function testPagination() - { - $this->assertEncodesTo( - ' - { - "data": [ - {"type": "apples", "id": "1"}, - {"type": "oranges", "id": "1"} - ], - "links": { - "first": "http://example.com/fruits?page=first", - "last": "http://example.com/fruits?page=last", - "prev": "http://example.com/fruits?page=3", - "next": "http://example.com/fruits?page=5" - } - } - ', - new DataDocument( - new ResourceObjectSet( - new ResourceObject('apples', '1'), - new ResourceObject('oranges', '1') - ), - new FirstLink(new Url('http://example.com/fruits?page=first')), - new LastLink(new Url('http://example.com/fruits?page=last')), - new PrevLink(new Url('http://example.com/fruits?page=3')), - new NextLink(new Url('http://example.com/fruits?page=5')) - ) - ); - } -} diff --git a/test/ErrorDocumentTest.php b/test/ErrorDocumentTest.php new file mode 100644 index 0000000..5ed715a --- /dev/null +++ b/test/ErrorDocumentTest.php @@ -0,0 +1,122 @@ +assertEncodesTo( + ' + { + "errors": [{}] + } + ', + new ErrorDocument( + new Error() + ) + ); + } + + public function testExtensiveExample() + { + $this->assertEncodesTo( + ' + { + "errors": [{ + "id": "1", + "links": { + "about":"/errors/not_found" + }, + "status": "404", + "code": "not_found", + "title": "Resource not found", + "detail": "We tried hard but could not find anything", + "source": { + "pointer": "/data", + "parameter": "query_string" + }, + "meta": { + "purpose":"test" + } + }], + "meta": {"purpose": "test"}, + "jsonapi": { + "version": "1.0" + } + } + ', + new ErrorDocument( + new Error( + new Id('1'), + new AboutLink( + new Url('/errors/not_found') + ), + new Status('404'), + new Code('not_found'), + new Title('Resource not found'), + new Detail('We tried hard but could not find anything'), + new Pointer('/data'), + new Parameter('query_string'), + new Meta('purpose', 'test') + ), + new Meta('purpose', 'test'), + new JsonApi() + ) + ); + } + + public function testMultipleErrors() + { + $this->assertEncodesTo( + ' + { + "errors": [{ + "id": "1", + "code": "invalid_parameter", + "source": { + "parameter": "foo" + } + },{ + "id": "2", + "code": "invalid_parameter", + "source": { + "parameter": "bar" + } + }] + } + ', + new ErrorDocument( + new Error( + new Id('1'), + new Code('invalid_parameter'), + new Parameter('foo') + ), + new Error( + new Id('2'), + new Code('invalid_parameter'), + new Parameter('bar') + ) + ) + ); + } +} diff --git a/test/ErrorTest.php b/test/ErrorTest.php deleted file mode 100644 index d6cb5a8..0000000 --- a/test/ErrorTest.php +++ /dev/null @@ -1,62 +0,0 @@ -assertEncodesTo('{}', new Error()); - } - - public function testErrorWithFullSetOfProperties() - { - $this->assertEncodesTo( - ' - { - "id": "test_id", - "links": { - "about":"http://localhost" - }, - "status": "404", - "code": "OMG", - "title": "Error", - "detail": "Nothing is found", - "source": { - "pointer": "/data", - "parameter": "test_param" - }, - "meta": { - "foo":"bar" - } - } - ', - new Error( - new Id('test_id'), - new AboutLink( - new Url('http://localhost') - ), - new Status('404'), - new Code('OMG'), - new Title('Error'), - new Detail('Nothing is found'), - new Pointer('/data'), - new Parameter('test_param'), - new Meta(['foo' => 'bar']) - ) - ); - } -} diff --git a/test/JsonApiTest.php b/test/JsonApiTest.php index 10f00cc..99731b0 100644 --- a/test/JsonApiTest.php +++ b/test/JsonApiTest.php @@ -19,7 +19,7 @@ public function testJsonApiMayContainVersionAndMeta() } } ', - new JsonApi('1.0', new Meta(['foo' => 'bar'])) + new JsonApi('1.0', new Meta('foo', 'bar')) ); } } diff --git a/test/LinkTest.php b/test/LinkTest.php index e625660..8ca34e1 100644 --- a/test/LinkTest.php +++ b/test/LinkTest.php @@ -18,7 +18,7 @@ public function testLinkObject() } } ', - new LinkObject('http://example.com', new Meta(['foo' => 'bar'])) + new LinkObject('http://example.com', new Meta('foo', 'bar')) ); } } diff --git a/test/MetaDocumentTest.php b/test/MetaDocumentTest.php new file mode 100644 index 0000000..7893dae --- /dev/null +++ b/test/MetaDocumentTest.php @@ -0,0 +1,51 @@ +assertEncodesTo( + ' + { + "meta": { + "foo": "bar" + } + } + ', + new MetaDocument(new Meta('foo', 'bar')) + ); + } + + /** + * A meta document may contain jsonapi member + */ + public function testMetaDocumentWithExtraMembers() + { + $this->assertEncodesTo( + ' + { + "meta": { + "foo": "bar" + }, + "jsonapi": { + "version": "1.0" + } + } + ', + new MetaDocument( + new Meta('foo', 'bar'), + new JsonApi() + ) + ); + } +} diff --git a/test/MetaTest.php b/test/MetaTest.php new file mode 100644 index 0000000..8ea6b1b --- /dev/null +++ b/test/MetaTest.php @@ -0,0 +1,16 @@ +expectException(\DomainException::class); + $this->expectExceptionMessage("Invalid character in a member name 'this+name&is'"); + new Meta('this+name&is', '1'); + } +} diff --git a/test/PaginationLinksTest.php b/test/PaginationLinksTest.php new file mode 100644 index 0000000..f1774d6 --- /dev/null +++ b/test/PaginationLinksTest.php @@ -0,0 +1,45 @@ +assertEncodesTo( + ' + { + "data": [ + {"type": "apples", "id": "1"}, + {"type": "apples", "id": "2"} + ], + "links": { + "first": "http://example.com/fruits?page=first", + "last": "http://example.com/fruits?page=last", + "prev": "http://example.com/fruits?page=3", + "next": "http://example.com/fruits?page=5" + } + } + ', + new DataDocument( + new ResourceObjectSet( + new ResourceObject('apples', '1'), + new ResourceObject('apples', '2') + ), + new FirstLink(new Url('http://example.com/fruits?page=first')), + new LastLink(new Url('http://example.com/fruits?page=last')), + new PrevLink(new Url('http://example.com/fruits?page=3')), + new NextLink(new Url('http://example.com/fruits?page=5')) + ) + ); + } +} diff --git a/test/ResourceObjectTest.php b/test/ResourceObjectTest.php index 723cc8b..3548dcd 100644 --- a/test/ResourceObjectTest.php +++ b/test/ResourceObjectTest.php @@ -44,12 +44,12 @@ public function testFullFledgedResourceObject() new ResourceObject( 'apples', '1', - new Meta(['foo' => 'bar']), + new Meta('foo', 'bar'), new Attribute('title', 'Rails is Omakase'), new SelfLink(new Url('http://self')), new Relationship( 'author', - new Meta(['foo' => 'bar']), + new Meta('foo', 'bar'), new SelfLink(new Url('http://rel/author')), new RelatedLink(new Url('http://author')), new SingleLinkage() @@ -135,14 +135,14 @@ public function testCanNotCreateIdRelationship() { $this->expectException(\DomainException::class); $this->expectExceptionMessage("Can not use 'id' as a resource field"); - new Relationship('id', new Meta([])); + new Relationship('id', new SingleLinkage(new ResourceIdentifier('apples', '1'))); } public function testCanNotCreateTypeRelationship() { $this->expectException(\DomainException::class); $this->expectExceptionMessage("Can not use 'type' as a resource field"); - new Relationship('type', new Meta([])); + new Relationship('type', new SingleLinkage(new ResourceIdentifier('apples', '1'))); } /** @@ -186,7 +186,7 @@ public function testResourceFieldsMustBeUnique() 'apples', '1', new Attribute('foo', 'bar'), - new Relationship('foo', new Meta([])) + new Relationship('foo', new SingleLinkage(new ResourceIdentifier('apples', '1'))) ); } From 1b0346e922dc779933a135c94e9963fb2646844c Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Mon, 26 Feb 2018 18:59:51 -0800 Subject: [PATCH 35/37] Tests pass --- .php_cs.dist | 1 + CHANGELOG.md | 17 ++------- README.md | 37 ++++++++++++------- examples/compound_doc.php | 76 +++++++++++++++++++++++++++++++++++++-- examples/simple_doc.php | 13 ++++--- test/ExamplesTest.php | 26 ++++++++++++++ test/LinkObjectTest.php | 34 ++++++++++++++++++ test/LinkTest.php | 24 ------------- 8 files changed, 170 insertions(+), 58 deletions(-) create mode 100644 test/ExamplesTest.php create mode 100644 test/LinkObjectTest.php delete mode 100644 test/LinkTest.php diff --git a/.php_cs.dist b/.php_cs.dist index 13b77ec..06b82a3 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -2,6 +2,7 @@ $finder = PhpCsFixer\Finder::create() ->files() ->name('*.php') + ->in(__DIR__ . '/examples') ->in(__DIR__ . '/src') ->in(__DIR__ . '/test'); return PhpCsFixer\Config::create() diff --git a/CHANGELOG.md b/CHANGELOG.md index 604c8b6..fa0e549 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,20 +6,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] -## [1.0.2] - 2018-02-21 -### Added -- This changelog - -### Removed -- Removed doc2test dependency - -## [1.0.1] - 2017-10-27 -### Changed -- Exceptions precision. Using DomainException instead of LogicException - -## [1.0.0] - 2017-10-24 +## [2.0.0] - 2018-02-26 Initial stable release -[Unreleased]: https://github.com/json-api-php/json-api/compare/1.0.2...HEAD -[1.0.2]: https://github.com/json-api-php/json-api/compare/1.0.1...1.0.2 -[1.0.1]: https://github.com/json-api-php/json-api/compare/1.0.0...1.0.1 +[Unreleased]: https://github.com/json-api-php/json-api/compare/2.0.0...HEAD diff --git a/README.md b/README.md index 1837428..e5e1e86 100644 --- a/README.md +++ b/README.md @@ -29,16 +29,24 @@ JSON: PHP: ```php identifier())) +); + +$document = new CompoundDocument( + new ResourceObjectSet( + new ResourceObject( + 'articles', + '1', + new Attribute('title', 'JSON API paints my bikeshed!'), + new SelfLink(new Url('http://example.com/articles/1')), + new Relationship( + 'author', + new SingleLinkage($dan->identifier()), + new SelfLink(new Url('http://example.com/articles/1/relationships/author')), + new RelatedLink(new Url('http://example.com/articles/1/author')) + ), + new Relationship( + 'comments', + new MultiLinkage( + $comment05->identifier(), + $comment12->identifier() + ), + new SelfLink(new Url('http://example.com/articles/1/relationships/comments')), + new RelatedLink(new Url('http://example.com/articles/1/comments')) + ) + ) + ), + new Included($dan, $comment05, $comment12), + new SelfLink(new Url('http://example.com/articles')), + new NextLink(new Url('http://example.com/articles?page[offset]=2')), + new LastLink(new Url('http://example.com/articles?page[offset]=10')) +); + +echo json_encode($document, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); diff --git a/examples/simple_doc.php b/examples/simple_doc.php index 6a83142..48b60b7 100644 --- a/examples/simple_doc.php +++ b/examples/simple_doc.php @@ -1,5 +1,5 @@ -assertJson(`php $file`); + } + + public function examples() + { + return [ + [__DIR__.'/../examples/compound_doc.php'], + [__DIR__.'/../examples/simple_doc.php'], + ]; + } +} diff --git a/test/LinkObjectTest.php b/test/LinkObjectTest.php new file mode 100644 index 0000000..6df8fff --- /dev/null +++ b/test/LinkObjectTest.php @@ -0,0 +1,34 @@ +assertEncodesTo( + '{ + "data": {"type": "apples", "id": "1"}, + "links": { + "self": { + "href": "http://example.com", + "meta": { + "foo": "bar" + } + } + } + } + ', + new DataDocument( + new ResourceIdentifier('apples', '1'), + new SelfLink(new LinkObject('http://example.com', new Meta('foo', 'bar'))) + ) + ); + } +} diff --git a/test/LinkTest.php b/test/LinkTest.php deleted file mode 100644 index 8ca34e1..0000000 --- a/test/LinkTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertEncodesTo( - ' - { - "href": "http://example.com", - "meta": { - "foo": "bar" - } - } - ', - new LinkObject('http://example.com', new Meta('foo', 'bar')) - ); - } -} From 0cfbe49fdc5e3b6042f64f1b95798191fc44c6b9 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Mon, 26 Feb 2018 19:01:08 -0800 Subject: [PATCH 36/37] v2 rc.0 --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa0e549..548098b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] -## [2.0.0] - 2018-02-26 +## [2.0.0-rc.0] - 2018-02-26 Initial stable release -[Unreleased]: https://github.com/json-api-php/json-api/compare/2.0.0...HEAD +[Unreleased]: https://github.com/json-api-php/json-api/compare/2.0.0-rc.0...HEAD From cc488d8cf355c93ae16a7d8ccddd735a1d55da46 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Mon, 26 Feb 2018 19:03:46 -0800 Subject: [PATCH 37/37] v2 rc.0 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index e5e1e86..9431d4e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # [JSON API](http://jsonapi.org) spec implemented in PHP 7. Immutable +**This is v2 of the implementation. For v1 click [here](/json-api-php/json-api/tree/v2).** + The goal of this library is to ensure strict validity of JSON API documents being produced. JSON: