From 79d9c0f33175fe3be035a9b44c10a02a0430d505 Mon Sep 17 00:00:00 2001 From: Jasper Zonneveld Date: Fri, 11 Jan 2019 11:46:02 +0100 Subject: [PATCH 1/2] Do not set $relation.'_id' on the parent --- src/Item.php | 4 +-- src/Relations/HasOneRelation.php | 44 ++----------------------------- src/Relations/MorphToRelation.php | 14 ---------- 3 files changed, 4 insertions(+), 58 deletions(-) diff --git a/src/Item.php b/src/Item.php index f10a6af..8848fd2 100644 --- a/src/Item.php +++ b/src/Item.php @@ -316,7 +316,7 @@ public function hasOne(string $class, string $relationName = null) $itemType = (new $class())->getType(); if (!array_key_exists($relationName, $this->relationships)) { - $this->relationships[$relationName] = new HasOneRelation($itemType, $this); + $this->relationships[$relationName] = new HasOneRelation($itemType); } return $this->relationships[$relationName]; @@ -354,7 +354,7 @@ public function morphTo(string $relationName = null) $relationName = $relationName ?: snake_case(debug_backtrace()[1]['function']); if (!array_key_exists($relationName, $this->relationships)) { - $this->relationships[$relationName] = new MorphToRelation($this); + $this->relationships[$relationName] = new MorphToRelation(); } return $this->relationships[$relationName]; diff --git a/src/Relations/HasOneRelation.php b/src/Relations/HasOneRelation.php index 6e9168e..13df3ff 100644 --- a/src/Relations/HasOneRelation.php +++ b/src/Relations/HasOneRelation.php @@ -2,53 +2,13 @@ namespace Swis\JsonApi\Client\Relations; -use Swis\JsonApi\Client\Interfaces\DataInterface; -use Swis\JsonApi\Client\Interfaces\ItemInterface; - class HasOneRelation extends AbstractOneRelation { /** - * @var \Swis\JsonApi\Client\Interfaces\ItemInterface + * @param string $type */ - protected $parentItem; - - /** - * @param string $type - * @param \Swis\JsonApi\Client\Interfaces\ItemInterface $item - */ - public function __construct(string $type, ItemInterface $item) + public function __construct(string $type) { $this->type = $type; - $this->parentItem = $item; - } - - /** - * @param \Swis\JsonApi\Client\Interfaces\DataInterface $included - * - * @throws \InvalidArgumentException - * - * @return $this - */ - public function associate(DataInterface $included) - { - $result = parent::associate($included); - - // Set the $relation.'_id' on the parent - $this->parentItem->setAttribute($this->type.'_id', $this->getId()); - - return $result; - } - - /** - * @return $this - */ - public function dissociate() - { - $result = parent::dissociate(); - - // Remove the $relation.'_id' on the parent - $this->parentItem->setAttribute($this->type.'_id', null); - - return $result; } } diff --git a/src/Relations/MorphToRelation.php b/src/Relations/MorphToRelation.php index 3d0be41..a077cbc 100644 --- a/src/Relations/MorphToRelation.php +++ b/src/Relations/MorphToRelation.php @@ -2,20 +2,6 @@ namespace Swis\JsonApi\Client\Relations; -use Swis\JsonApi\Client\Interfaces\ItemInterface; - class MorphToRelation extends AbstractOneRelation { - /** - * @var \Swis\JsonApi\Client\Interfaces\ItemInterface - */ - protected $parentItem; - - /** - * @param \Swis\JsonApi\Client\Interfaces\ItemInterface $item - */ - public function __construct(ItemInterface $item) - { - $this->parentItem = $item; - } } From 57fb5bc749dbacd41bc78fcd4b8ad1873fdb2745 Mon Sep 17 00:00:00 2001 From: Jasper Zonneveld Date: Fri, 11 Jan 2019 12:17:40 +0100 Subject: [PATCH 2/2] Make sure dissociating a related item results in valid JSON --- CHANGELOG.md | 2 + src/Item.php | 44 ++++-- src/Relations/AbstractOneRelation.php | 5 +- src/Relations/AbstractRelation.php | 2 +- src/Relations/MorphToRelation.php | 27 ++++ tests/ItemTest.php | 203 ++++++++++++++++++++++++++ 6 files changed, 268 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6b226a..8434762 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. N.B. This is a breaking change if you implement the interface yourself or extend the `DocumentClient`. [#34](https://github.com/swisnl/json-api-client/pull/34) * `Repository` doesn't throw exceptions anymore. [#41](https://github.com/swisnl/json-api-client/pull/41) N.B. This is a breaking change if you catch `DocumentNotFoundException` or `DocumentTypeException`. If you would like the old behaviour, you can simply extend the `Repository` and implement it yourself. +* A HasOne or MorphTo relation do not set a `[relationship]_id` field on the parent when associating a related item. ### Removed @@ -27,6 +28,7 @@ N.B. This is a breaking change if you catch these exceptions. ### Fixed * Do not fail on, but skip relationships without data [#38](https://github.com/swisnl/json-api-client/pull/38) +* Dissociating a related item now produces valid JSON ## [0.11.0] - 2018-12-21 diff --git a/src/Item.php b/src/Item.php index 8848fd2..892c654 100644 --- a/src/Item.php +++ b/src/Item.php @@ -138,12 +138,16 @@ public function getRelationships(): array /** @var \Swis\JsonApi\Client\Interfaces\RelationInterface $relationship */ foreach ($this->relationships as $name => $relationship) { if ($relationship instanceof HasOneRelation) { - $relationships[$name] = [ - 'data' => [ - 'type' => $relationship->getType(), - 'id' => $relationship->getId(), - ], - ]; + $relationships[$name] = ['data' => null]; + + if ($relationship->getIncluded() !== null) { + $relationships[$name] = [ + 'data' => [ + 'type' => $relationship->getType(), + 'id' => $relationship->getId(), + ], + ]; + } } elseif ($relationship instanceof HasManyRelation) { $relationships[$name]['data'] = []; @@ -155,12 +159,16 @@ public function getRelationships(): array ]; } } elseif ($relationship instanceof MorphToRelation) { - $relationships[$name] = [ - 'data' => [ - 'type' => $relationship->getIncluded()->getType(), - 'id' => $relationship->getIncluded()->getId(), - ], - ]; + $relationships[$name] = ['data' => null]; + + if ($relationship->getIncluded() !== null) { + $relationships[$name] = [ + 'data' => [ + 'type' => $relationship->getIncluded()->getType(), + 'id' => $relationship->getIncluded()->getId(), + ], + ]; + } } elseif ($relationship instanceof MorphToManyRelation) { $relationships[$name]['data'] = []; @@ -302,6 +310,18 @@ public function hasRelationship(string $name): bool return array_key_exists($name, $this->relationships); } + /** + * @param $name + * + * @return static + */ + public function removeRelationship(string $name) + { + unset($this->relationships[$name]); + + return $this; + } + /** * Create a singular relation to another item. * diff --git a/src/Relations/AbstractOneRelation.php b/src/Relations/AbstractOneRelation.php index 848b24f..da7bfa7 100644 --- a/src/Relations/AbstractOneRelation.php +++ b/src/Relations/AbstractOneRelation.php @@ -8,7 +8,7 @@ abstract class AbstractOneRelation extends AbstractRelation { /** - * @var \Swis\JsonApi\Client\Interfaces\ItemInterface + * @var \Swis\JsonApi\Client\Interfaces\ItemInterface|null */ protected $included; @@ -33,7 +33,6 @@ public function associate(DataInterface $included) } $this->setId($included->getId()); - $this->setType($included->getType()); $this->included = $included; @@ -45,6 +44,8 @@ public function associate(DataInterface $included) */ public function dissociate() { + $this->setId(null); + $this->included = null; return $this; diff --git a/src/Relations/AbstractRelation.php b/src/Relations/AbstractRelation.php index 2db9239..943f8b6 100644 --- a/src/Relations/AbstractRelation.php +++ b/src/Relations/AbstractRelation.php @@ -29,7 +29,7 @@ public function setType(string $type) } /** - * @return string|null + * @return string */ public function getType(): string { diff --git a/src/Relations/MorphToRelation.php b/src/Relations/MorphToRelation.php index a077cbc..90e12e9 100644 --- a/src/Relations/MorphToRelation.php +++ b/src/Relations/MorphToRelation.php @@ -2,6 +2,33 @@ namespace Swis\JsonApi\Client\Relations; +use Swis\JsonApi\Client\Interfaces\DataInterface; + class MorphToRelation extends AbstractOneRelation { + /** + * {@inheritdoc} + */ + public function associate(DataInterface $included) + { + parent::associate($included); + + /* @var \Swis\JsonApi\Client\Interfaces\ItemInterface $included */ + + $this->type = $included->getType(); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function dissociate() + { + parent::dissociate(); + + $this->type = null; + + return $this; + } } diff --git a/tests/ItemTest.php b/tests/ItemTest.php index 551b329..7cb3cc6 100644 --- a/tests/ItemTest.php +++ b/tests/ItemTest.php @@ -2,9 +2,12 @@ namespace Swis\JsonApi\Client\Tests; +use Swis\JsonApi\Client\Collection; use Swis\JsonApi\Client\Item; +use Swis\JsonApi\Client\Tests\Mocks\Items\RelatedItem; use Swis\JsonApi\Client\Tests\Mocks\Items\WithGetMutatorItem; use Swis\JsonApi\Client\Tests\Mocks\Items\WithHiddenItem; +use Swis\JsonApi\Client\Tests\Mocks\Items\WithRelationshipItem; class ItemTest extends AbstractTest { @@ -169,4 +172,204 @@ public function is_does_not_show_attributes_in_to_json_api_array_when_it_has_no_ $item->toJsonApiArray() ); } + + /** + * @test + */ + public function is_adds_hasone_relation_in_to_json_api_array() + { + $item = new WithRelationshipItem(); + $item->setId(1234); + $item->hasoneRelation()->associate((new RelatedItem())->setId(5678)); + + $this->assertEquals( + [ + 'type' => 'item-with-relationship', + 'id' => 1234, + 'relationships' => [ + 'hasone_relation' => [ + 'data' => [ + 'type' => 'related-item', + 'id' => 5678, + ], + ], + ], + ], + $item->toJsonApiArray() + ); + } + + /** + * @test + */ + public function is_adds_empty_hasone_relation_in_to_json_api_array() + { + $item = new WithRelationshipItem(); + $item->setId(1234); + $item->hasoneRelation()->dissociate(); + + $this->assertEquals( + [ + 'type' => 'item-with-relationship', + 'id' => 1234, + 'relationships' => [ + 'hasone_relation' => [ + 'data' => null, + ], + ], + ], + $item->toJsonApiArray() + ); + } + + /** + * @test + */ + public function is_adds_morphto_relation_in_to_json_api_array() + { + $item = new WithRelationshipItem(); + $item->setId(1234); + $item->morphtoRelation()->associate((new RelatedItem())->setId(5678)); + + $this->assertEquals( + [ + 'type' => 'item-with-relationship', + 'id' => 1234, + 'relationships' => [ + 'morphto_relation' => [ + 'data' => [ + 'type' => 'related-item', + 'id' => 5678, + ], + ], + ], + ], + $item->toJsonApiArray() + ); + } + + /** + * @test + */ + public function is_adds_empty_morphto_relation_in_to_json_api_array() + { + $item = new WithRelationshipItem(); + $item->setId(1234); + $item->morphtoRelation()->dissociate(); + + $this->assertEquals( + [ + 'type' => 'item-with-relationship', + 'id' => 1234, + 'relationships' => [ + 'morphto_relation' => [ + 'data' => null, + ], + ], + ], + $item->toJsonApiArray() + ); + } + + /** + * @test + */ + public function is_adds_hasmany_relation_in_to_json_api_array() + { + $item = new WithRelationshipItem(); + $item->setId(1234); + $item->hasmanyRelation()->associate(new Collection([(new RelatedItem())->setId(5678)])); + + $this->assertEquals( + [ + 'type' => 'item-with-relationship', + 'id' => 1234, + 'relationships' => [ + 'hasmany_relation' => [ + 'data' => [ + [ + 'type' => 'related-item', + 'id' => 5678, + ], + ], + ], + ], + ], + $item->toJsonApiArray() + ); + } + + /** + * @test + */ + public function is_adds_empty_hasmany_relation_in_to_json_api_array() + { + $item = new WithRelationshipItem(); + $item->setId(1234); + $item->hasmanyRelation()->dissociate(); + + $this->assertEquals( + [ + 'type' => 'item-with-relationship', + 'id' => 1234, + 'relationships' => [ + 'hasmany_relation' => [ + 'data' => [], + ], + ], + ], + $item->toJsonApiArray() + ); + } + + /** + * @test + */ + public function is_adds_morphtomany_relation_in_to_json_api_array() + { + $item = new WithRelationshipItem(); + $item->setId(1234); + $item->morphtomanyRelation()->associate(new Collection([(new RelatedItem())->setId(5678)])); + + $this->assertEquals( + [ + 'type' => 'item-with-relationship', + 'id' => 1234, + 'relationships' => [ + 'morphtomany_relation' => [ + 'data' => [ + [ + 'type' => 'related-item', + 'id' => 5678, + ], + ], + ], + ], + ], + $item->toJsonApiArray() + ); + } + + /** + * @test + */ + public function is_adds_empty_morphtomany_relation_in_to_json_api_array() + { + $item = new WithRelationshipItem(); + $item->setId(1234); + $item->morphtomanyRelation()->dissociate(); + + $this->assertEquals( + [ + 'type' => 'item-with-relationship', + 'id' => 1234, + 'relationships' => [ + 'morphtomany_relation' => [ + 'data' => [], + ], + ], + ], + $item->toJsonApiArray() + ); + } }