From 47d867fc06a1fbc37b30d46ca9407efed1d9cb04 Mon Sep 17 00:00:00 2001 From: Lode Claassen Date: Tue, 20 Oct 2020 22:26:01 +0200 Subject: [PATCH 1/4] allow adding local id if there is no normal id --- src/helpers/Validator.php | 1 + src/objects/ResourceIdentifierObject.php | 25 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/helpers/Validator.php b/src/helpers/Validator.php index 6e814d7..12d26fd 100644 --- a/src/helpers/Validator.php +++ b/src/helpers/Validator.php @@ -12,6 +12,7 @@ class Validator { const OBJECT_CONTAINER_TYPE = 'type'; const OBJECT_CONTAINER_ID = 'id'; + const OBJECT_CONTAINER_LID = 'lid'; const OBJECT_CONTAINER_ATTRIBUTES = 'attributes'; const OBJECT_CONTAINER_RELATIONSHIPS = 'relationships'; diff --git a/src/objects/ResourceIdentifierObject.php b/src/objects/ResourceIdentifierObject.php index fe32d91..cf68344 100644 --- a/src/objects/ResourceIdentifierObject.php +++ b/src/objects/ResourceIdentifierObject.php @@ -16,6 +16,8 @@ class ResourceIdentifierObject implements ObjectInterface, ResourceInterface { protected $type; /** @var string */ protected $id; + /** @var string */ + protected $lid; /** @var MetaObject */ protected $meta; /** @var Validator */ @@ -42,6 +44,7 @@ public function __construct($type=null, $id=null) { // always mark as used, as these keys are reserved $this->validator->claimUsedFields($fieldNames=['type'], Validator::OBJECT_CONTAINER_TYPE); $this->validator->claimUsedFields($fieldNames=['id'], Validator::OBJECT_CONTAINER_ID); + $this->validator->claimUsedFields($fieldNames=['lid'], Validator::OBJECT_CONTAINER_LID); } /** @@ -73,11 +76,30 @@ public function setType($type) { /** * @param string|int $id will be casted to a string + * + * @throws DuplicateException if localId is already set */ public function setId($id) { + if ($this->lid !== null) { + throw new DuplicateException('id is not allowed when localId is already set'); + } + $this->id = (string) $id; } + /** + * @param string|int $localId will be casted to a string + * + * @throws DuplicateException if normal id is already set + */ + public function setLocalId($localId) { + if ($this->id !== null) { + throw new DuplicateException('localId is not allowed when id is already set'); + } + + $this->lid = (string) $localId; + } + /** * @param MetaObject $metaObject */ @@ -179,6 +201,9 @@ public function toArray() { if ($this->id !== null) { $array['id'] = $this->id; } + elseif ($this->lid !== null) { + $array['lid'] = $this->lid; + } if ($this->meta !== null && $this->meta->isEmpty() === false) { $array['meta'] = $this->meta->toArray(); From f6481f4c2b6e765bf321e1d26dee9f4cdd8cddd1 Mon Sep 17 00:00:00 2001 From: Lode Claassen Date: Tue, 20 Oct 2020 22:26:22 +0200 Subject: [PATCH 2/4] make sure to pass along the local id when we normally pass the id --- src/objects/ResourceIdentifierObject.php | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/objects/ResourceIdentifierObject.php b/src/objects/ResourceIdentifierObject.php index cf68344..03a43ca 100644 --- a/src/objects/ResourceIdentifierObject.php +++ b/src/objects/ResourceIdentifierObject.php @@ -118,7 +118,7 @@ public function setMetaObject(MetaObject $metaObject) { * @return ResourceIdentifierObject */ public static function fromResourceObject(ResourceObject $resourceObject) { - $resourceIdentifierObject = new self($resourceObject->type, $resourceObject->id); + $resourceIdentifierObject = new self($resourceObject->type, $resourceObject->primaryId()); if ($resourceObject->meta !== null) { $resourceIdentifierObject->setMetaObject($resourceObject->meta); @@ -149,7 +149,7 @@ public function equals(ResourceInterface $resource) { * @return boolean */ public function hasIdentification() { - return ($this->type !== null && $this->id !== null); + return ($this->type !== null && $this->primaryId() !== null); } /** @@ -166,7 +166,7 @@ public function getIdentificationKey() { throw new Exception('resource has no identification yet'); } - return $this->type.'|'.$this->id; + return $this->type.'|'.$this->primaryId(); } /** @@ -177,7 +177,7 @@ public function getIdentificationKey() { * @inheritDoc */ public function isEmpty() { - if ($this->type !== null || $this->id !== null) { + if ($this->type !== null || $this->primaryId() !== null) { return false; } if ($this->meta !== null && $this->meta->isEmpty() === false) { @@ -222,4 +222,16 @@ public function toArray() { public function getResource($identifierOnly=false) { return $this; } + + /** + * @internal + */ + + private function primaryId() { + if ($this->lid !== null) { + return $this->lid; + } + + return $this->id; + } } From 95295216d72235e8473369868b99756071a125d1 Mon Sep 17 00:00:00 2001 From: Lode Claassen Date: Fri, 14 May 2021 17:03:45 +0200 Subject: [PATCH 3/4] explain when to use local id --- src/helpers/Validator.php | 2 +- src/objects/ResourceIdentifierObject.php | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/helpers/Validator.php b/src/helpers/Validator.php index 12d26fd..692d473 100644 --- a/src/helpers/Validator.php +++ b/src/helpers/Validator.php @@ -85,7 +85,7 @@ public function clearUsedFields($objectContainerToClear) { */ public function claimUsedResourceIdentifier(ResourceInterface $resource) { if ($resource->getResource()->hasIdentification() === false) { - throw new InputException('can not validate resource without identifier, set type and id first'); + throw new InputException('can not validate resource without identifier, set type and id/lid first'); } $resourceKey = $resource->getResource()->getIdentificationKey(); diff --git a/src/objects/ResourceIdentifierObject.php b/src/objects/ResourceIdentifierObject.php index 03a43ca..fb9869b 100644 --- a/src/objects/ResourceIdentifierObject.php +++ b/src/objects/ResourceIdentifierObject.php @@ -88,6 +88,10 @@ public function setId($id) { } /** + * set a local id to connect resources to each other when created on the client + * + * @note this should not be used to send back from the server to the client + * * @param string|int $localId will be casted to a string * * @throws DuplicateException if normal id is already set From a39626031d129986c94b442346b0f383254af513 Mon Sep 17 00:00:00 2001 From: Lode Claassen Date: Fri, 14 May 2021 17:04:35 +0200 Subject: [PATCH 4/4] test using local ids --- src/objects/ResourceIdentifierObject.php | 1 + .../objects/ResourceIdentifierObjectTest.php | 76 +++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/src/objects/ResourceIdentifierObject.php b/src/objects/ResourceIdentifierObject.php index fb9869b..a06c33f 100644 --- a/src/objects/ResourceIdentifierObject.php +++ b/src/objects/ResourceIdentifierObject.php @@ -3,6 +3,7 @@ namespace alsvanzelf\jsonapi\objects; use alsvanzelf\jsonapi\exceptions\Exception; +use alsvanzelf\jsonapi\exceptions\DuplicateException; use alsvanzelf\jsonapi\helpers\AtMemberManager; use alsvanzelf\jsonapi\helpers\Validator; use alsvanzelf\jsonapi\interfaces\ObjectInterface; diff --git a/tests/objects/ResourceIdentifierObjectTest.php b/tests/objects/ResourceIdentifierObjectTest.php index 2508479..edee0bd 100644 --- a/tests/objects/ResourceIdentifierObjectTest.php +++ b/tests/objects/ResourceIdentifierObjectTest.php @@ -3,10 +3,55 @@ namespace alsvanzelf\jsonapiTests\objects; use alsvanzelf\jsonapi\exceptions\Exception; +use alsvanzelf\jsonapi\exceptions\DuplicateException; use alsvanzelf\jsonapi\objects\ResourceIdentifierObject; use PHPUnit\Framework\TestCase; class ResourceIdentifierObjectTest extends TestCase { + public function testSetId_HappyPath() { + $resourceIdentifierObject = new ResourceIdentifierObject(); + $resourceIdentifierObject->setType('test'); + $resourceIdentifierObject->setId('1'); + + $array = $resourceIdentifierObject->toArray(); + + $this->assertArrayHasKey('id', $array); + $this->assertArrayNotHasKey('lid', $array); + $this->assertSame('1', $array['id']); + } + + public function testSetId_WithLocalIdAlreadySet() { + $resourceIdentifierObject = new ResourceIdentifierObject(); + $resourceIdentifierObject->setType('test'); + $resourceIdentifierObject->setLocalId('uuid-1'); + + $this->expectException(DuplicateException::class); + + $resourceIdentifierObject->setId('1'); + } + + public function testSetLocalId_HappyPath() { + $resourceIdentifierObject = new ResourceIdentifierObject(); + $resourceIdentifierObject->setType('test'); + $resourceIdentifierObject->setLocalId('uuid-1'); + + $array = $resourceIdentifierObject->toArray(); + + $this->assertArrayHasKey('lid', $array); + $this->assertArrayNotHasKey('id', $array); + $this->assertSame('uuid-1', $array['lid']); + } + + public function testSetLocalId_WithIdAlreadySet() { + $resourceIdentifierObject = new ResourceIdentifierObject(); + $resourceIdentifierObject->setType('test'); + $resourceIdentifierObject->setId('1'); + + $this->expectException(DuplicateException::class); + + $resourceIdentifierObject->setLocalId('uuid-1'); + } + public function testEquals_HappyPath() { $one = new ResourceIdentifierObject('test', 1); $two = new ResourceIdentifierObject('test', 2); @@ -25,6 +70,19 @@ public function testEquals_WithoutIdentification() { $one->equals($two); } + public function testEquals_WithLocalId() { + $one = new ResourceIdentifierObject('test'); + $two = new ResourceIdentifierObject('test'); + $new = new ResourceIdentifierObject('test'); + + $one->setLocalId('uuid-1'); + $two->setLocalId('uuid-2'); + $new->setLocalId('uuid-1'); + + $this->assertFalse($one->equals($two)); + $this->assertTrue($one->equals($new)); + } + public function testGetIdentificationKey_HappyPath() { $resourceIdentifierObject = new ResourceIdentifierObject('user', 42); @@ -32,6 +90,7 @@ public function testGetIdentificationKey_HappyPath() { $this->assertArrayHasKey('type', $array); $this->assertArrayHasKey('id', $array); + $this->assertArrayNotHasKey('lid', $array); $this->assertSame('user', $array['type']); $this->assertSame('42', $array['id']); $this->assertTrue($resourceIdentifierObject->hasIdentification()); @@ -59,6 +118,23 @@ public function testGetIdentificationKey_SetAfterwards() { $this->assertSame('user|42', $resourceIdentifierObject->getIdentificationKey()); } + public function testGetIdentificationKey_WithLocalId() { + $resourceIdentifierObject = new ResourceIdentifierObject(); + + $resourceIdentifierObject->setType('user'); + $resourceIdentifierObject->setLocalId('uuid-42'); + + $array = $resourceIdentifierObject->toArray(); + + $this->assertArrayHasKey('type', $array); + $this->assertArrayHasKey('lid', $array); + $this->assertArrayNotHasKey('id', $array); + $this->assertSame('user', $array['type']); + $this->assertSame('uuid-42', $array['lid']); + $this->assertTrue($resourceIdentifierObject->hasIdentification()); + $this->assertSame('user|uuid-42', $resourceIdentifierObject->getIdentificationKey()); + } + public function testGetIdentificationKey_NoIdentification() { $resourceIdentifierObject = new ResourceIdentifierObject();