From 3564debf040cc0ef4af5b6b743d356b63ea9eb00 Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Fri, 10 Apr 2026 12:32:10 +0530 Subject: [PATCH] refactor: streamline attribute filtering in Database class and add tests to ensure schema keys are exposed correctly --- src/Database/Database.php | 9 ++- tests/e2e/Adapter/Scopes/DocumentTests.php | 75 ++++++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index ac58d72f0..bae99ae79 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -8818,10 +8818,15 @@ public function decode(Document $collection, Document $document, array $selectio } if (\is_null($value)) { - $value = $document->getAttribute($this->adapter->filter($key)); + $filteredKey = $this->adapter->filter($key); + $value = $document->getAttribute($filteredKey); if (!\is_null($value)) { - $document->removeAttribute($this->adapter->filter($key)); + $document->removeAttribute($filteredKey); + } elseif ($filteredKey !== $key && $document->offsetExists($filteredKey)) { + // SQL adapter column names use filter($key); remove the alias so the + // in-memory document only exposes keys (e.g. "a.b") that match the schema. + $document->removeAttribute($filteredKey); } } diff --git a/tests/e2e/Adapter/Scopes/DocumentTests.php b/tests/e2e/Adapter/Scopes/DocumentTests.php index d16004d32..dd91cc0bd 100644 --- a/tests/e2e/Adapter/Scopes/DocumentTests.php +++ b/tests/e2e/Adapter/Scopes/DocumentTests.php @@ -6667,6 +6667,81 @@ public function testValidationGuardsWithNullRequired(): void $database->deleteCollection($collection); } + /** + * SQL adapters store columns under filter(attributeId). After getDocument + decode, and after + * updateDocument (return value + refetch), the document must expose only schema ids (e.g. + * pb.e_DSS.FIRMWARE_VERSION), never the filtered alias. + */ + public function testDottedAttributeKeyGetDocumentExposesOnlySchemaKeys(): void + { + /** @var Database $database */ + $database = static::getDatabase(); + + if (!$database->getAdapter()->getSupportForAttributes()) { + $this->expectNotToPerformAssertions(); + return; + } + + // Keep id short: MySQL/MariaDB table names are limited to 64 characters (namespace + collection). + $collectionId = 'dotkey_' . ID::unique(); + $attrId = 'pb.e_DSS.FIRMWARE_VERSION'; + $filteredStorageKey = $database->getAdapter()->filter($attrId); + + $database->createCollection($collectionId); + $this->assertTrue($database->createAttribute($collectionId, $attrId, Database::VAR_STRING, 128, false)); + + // Optional attribute omitted: DB column is NULL — decode must not leave the SQL column name as a key. + $database->createDocument($collectionId, new Document([ + '$id' => 'dev1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ])); + + $doc = $database->getDocument($collectionId, 'dev1'); + $this->assertSame('dev1', $doc->getId()); + $this->assertNull($doc->getAttribute($attrId)); + $this->assertArrayNotHasKey($filteredStorageKey, $doc->getAttributes()); + $userKeys = array_keys($doc->getAttributes()); + sort($userKeys); + $this->assertSame([$attrId], $userKeys); + + $updated = $database->updateDocument($collectionId, 'dev1', new Document([ + $attrId => '1.0.0', + ])); + $this->assertSame('1.0.0', $updated->getAttribute($attrId)); + $this->assertArrayNotHasKey($filteredStorageKey, $updated->getAttributes()); + $userKeys = array_keys($updated->getAttributes()); + sort($userKeys); + $this->assertSame([$attrId], $userKeys); + + $doc = $database->getDocument($collectionId, 'dev1'); + $this->assertSame('1.0.0', $doc->getAttribute($attrId)); + $this->assertArrayNotHasKey($filteredStorageKey, $doc->getAttributes()); + $userKeys = array_keys($doc->getAttributes()); + sort($userKeys); + $this->assertSame([$attrId], $userKeys); + + $updated = $database->updateDocument($collectionId, 'dev1', new Document([ + $attrId => '2.0.0', + ])); + $this->assertSame('2.0.0', $updated->getAttribute($attrId)); + $this->assertArrayNotHasKey($filteredStorageKey, $updated->getAttributes()); + $userKeys = array_keys($updated->getAttributes()); + sort($userKeys); + $this->assertSame([$attrId], $userKeys); + + $doc = $database->getDocument($collectionId, 'dev1'); + $this->assertSame('2.0.0', $doc->getAttribute($attrId)); + $this->assertArrayNotHasKey($filteredStorageKey, $doc->getAttributes()); + $userKeys = array_keys($doc->getAttributes()); + sort($userKeys); + $this->assertSame([$attrId], $userKeys); + + $database->deleteCollection($collectionId); + } + public function testUpsertWithJSONFilters(): void { $database = static::getDatabase();