diff --git a/composer.json b/composer.json index c28e846a0..015335b39 100755 --- a/composer.json +++ b/composer.json @@ -24,6 +24,10 @@ "Composer\\Config::disableProcessTimeout", "docker compose exec tests vendor/bin/phpunit --configuration phpunit.xml" ], + "mytest": [ + "Composer\\Config::disableProcessTimeout", + "docker compose exec tests vendor/bin/phpunit ./tests/Database/Adapter/MongoDBTest.php --stop-on-failure" + ], "lint": "./vendor/bin/pint --test", "format": "./vendor/bin/pint", "check": "./vendor/bin/phpstan analyse --level 7 src tests --memory-limit 512M", diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index 3ffc52da1..52403ad10 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -1046,17 +1046,26 @@ public function find(string $collection, array $queries = [], ?int $limit = 25, $results = $stmt->fetchAll(); foreach ($results as $key => $value) { - $results[$key]['$id'] = $value['_uid']; - $results[$key]['$internalId'] = $value['_id']; - $results[$key]['$createdAt'] = $value['_createdAt']; - $results[$key]['$updatedAt'] = $value['_updatedAt']; - $results[$key]['$permissions'] = json_decode($value['_permissions'] ?? '[]', true); - - unset($results[$key]['_uid']); - unset($results[$key]['_id']); - unset($results[$key]['_createdAt']); - unset($results[$key]['_updatedAt']); - unset($results[$key]['_permissions']); + if (array_key_exists('_uid', $value)) { + $results[$key]['$id'] = $value['_uid']; + unset($results[$key]['_uid']); + } + if (array_key_exists('_id', $value)) { + $results[$key]['$internalId'] = $value['_id']; + unset($results[$key]['_id']); + } + if (array_key_exists('_createdAt', $value)) { + $results[$key]['$createdAt'] = $value['_createdAt']; + unset($results[$key]['_createdAt']); + } + if (array_key_exists('_updatedAt', $value)) { + $results[$key]['$updatedAt'] = $value['_updatedAt']; + unset($results[$key]['_updatedAt']); + } + if (array_key_exists('_permissions', $value)) { + $results[$key]['$permissions'] = json_decode($value['_permissions'] ?? '[]', true); + unset($results[$key]['_permissions']); + } $results[$key] = new Document($results[$key]); } @@ -1189,10 +1198,32 @@ protected function getAttributeProjection(array $selections, string $prefix = '' return '*'; } + if (in_array('$id', $selections)) { + $index = array_search('$id', $selections); + unset($selections[$index]); + } + if (in_array('$internalId', $selections)) { + $index = array_search('$internalId', $selections); + $selections[$index] = '_id'; + } + if (in_array('$createdAt', $selections)) { + $index = array_search('$createdAt', $selections); + $selections[$index] = '_createdAt'; + } + if (in_array('$updatedAt', $selections)) { + $index = array_search('$updatedAt', $selections); + $selections[$index] = '_updatedAt'; + } + if (in_array('$permissions', $selections)) { + $index = array_search('$permissions', $selections); + unset($selections[$index]); + } + if (in_array('$collection', $selections)) { + $index = array_search('$collection', $selections); + unset($selections[$index]); + } + $selections[] = '_uid'; - $selections[] = '_id'; - $selections[] = '_createdAt'; - $selections[] = '_updatedAt'; $selections[] = '_permissions'; if (!empty($prefix)) { diff --git a/src/Database/Adapter/Mongo.php b/src/Database/Adapter/Mongo.php index a68440e16..22c7b3f63 100644 --- a/src/Database/Adapter/Mongo.php +++ b/src/Database/Adapter/Mongo.php @@ -1331,10 +1331,28 @@ protected function getAttributeProjection(array $selections, string $prefix = '' } $projection['_uid'] = 1; - $projection['_id'] = 1; + $projection['_permissions'] = 1; $projection['_createdAt'] = 1; $projection['_updatedAt'] = 1; - $projection['_permissions'] = 1; + + if (isset($projection['$id'])) { + unset($projection['$id']); + } + if (isset($projection['$internalId'])) { + unset($projection['$internalId']); + } + if (isset($projection['$permissions'])) { + unset($projection['$permissions']); + } + if (isset($projection['$createdAt'])) { + unset($projection['$createdAt']); + } + if (isset($projection['$updatedAt'])) { + unset($projection['$updatedAt']); + } + if (isset($projection['$collection'])) { + unset($projection['$collection']); + } return $projection; } diff --git a/src/Database/Adapter/Postgres.php b/src/Database/Adapter/Postgres.php index 348a2bd51..43049c7ff 100644 --- a/src/Database/Adapter/Postgres.php +++ b/src/Database/Adapter/Postgres.php @@ -1056,17 +1056,26 @@ public function find(string $collection, array $queries = [], ?int $limit = 25, $results = $stmt->fetchAll(); foreach ($results as $key => $value) { - $results[$key]['$id'] = $value['_uid']; - $results[$key]['$internalId'] = $value['_id']; - $results[$key]['$createdAt'] = $value['_createdAt']; - $results[$key]['$updatedAt'] = $value['_updatedAt']; - $results[$key]['$permissions'] = json_decode($value['_permissions'] ?? '[]', true); - - unset($results[$key]['_uid']); - unset($results[$key]['_id']); - unset($results[$key]['_createdAt']); - unset($results[$key]['_updatedAt']); - unset($results[$key]['_permissions']); + if (array_key_exists('_uid', $value)) { + $results[$key]['$id'] = $value['_uid']; + unset($results[$key]['_uid']); + } + if (array_key_exists('_id', $value)) { + $results[$key]['$internalId'] = $value['_id']; + unset($results[$key]['_id']); + } + if (array_key_exists('_createdAt', $value)) { + $results[$key]['$createdAt'] = $value['_createdAt']; + unset($results[$key]['_createdAt']); + } + if (array_key_exists('_updatedAt', $value)) { + $results[$key]['$updatedAt'] = $value['_updatedAt']; + unset($results[$key]['_updatedAt']); + } + if (array_key_exists('_permissions', $value)) { + $results[$key]['$permissions'] = json_decode($value['_permissions'] ?? '[]', true); + unset($results[$key]['_permissions']); + } $results[$key] = new Document($results[$key]); } @@ -1215,6 +1224,14 @@ protected function getAttributeProjection(array $selections, string $prefix = '' } } + foreach ($selections as $key => $value) { + $valueUnquoted = \trim(\stripslashes($value), "\""); + $value = "$" . $valueUnquoted; + if (\in_array($value, Database::INTERNAL_ATTRIBUTES)) { + unset($selections[$key]); + } + } + return \implode(', ', $selections); } diff --git a/src/Database/Adapter/SQL.php b/src/Database/Adapter/SQL.php index 763986d5d..c42ccb7a7 100644 --- a/src/Database/Adapter/SQL.php +++ b/src/Database/Adapter/SQL.php @@ -127,17 +127,26 @@ public function getDocument(string $collection, string $id, array $queries = []) return new Document([]); } - $document['$id'] = $document['_uid']; - $document['$internalId'] = $document['_id']; - $document['$createdAt'] = $document['_createdAt']; - $document['$updatedAt'] = $document['_updatedAt']; - $document['$permissions'] = json_decode($document['_permissions'] ?? '[]', true); - - unset($document['_id']); - unset($document['_uid']); - unset($document['_createdAt']); - unset($document['_updatedAt']); - unset($document['_permissions']); + if (isset($document['_uid'])) { + $document['$id'] = $document['_uid']; + unset($document['_uid']); + } + if (isset($document['_id'])) { + $document['$internalId'] = $document['_id']; + unset($document['_id']); + } + if (isset($document['_createdAt'])) { + $document['$createdAt'] = $document['_createdAt']; + unset($document['_createdAt']); + } + if (isset($document['_updatedAt'])) { + $document['$updatedAt'] = $document['_updatedAt']; + unset($document['_updatedAt']); + } + if (isset($document['_permissions'])) { + $document['$permissions'] = json_decode($document['_permissions'] ?? '[]', true); + unset($document['_permissions']); + } return new Document($document); } diff --git a/src/Database/Database.php b/src/Database/Database.php index c90d68c6b..b84446368 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -121,6 +121,16 @@ class Database public const EVENT_INDEX_CREATE = 'index_create'; public const EVENT_INDEX_DELETE = 'index_delete'; + // Internal Parameters + public const INTERNAL_ATTRIBUTES = [ + '$id', + '$internalId', + '$createdAt', + '$updatedAt', + '$permissions', + '$collection' + ]; + protected Adapter $adapter; protected Cache $cache; @@ -2284,6 +2294,26 @@ public function getDocument(string $collection, string $id, array $queries = []) $this->cache->save($cacheKey, $document->getArrayCopy()); } + foreach ($queries as $query) { + if ($query->getMethod() == Query::TYPE_SELECT) { + $queriedValues = $query->getValues(); + $defaultKeys = Database::INTERNAL_ATTRIBUTES; + + foreach ($queriedValues as $queriedValue) { + if (in_array($queriedValue, $defaultKeys)) { + $index = array_search($queriedValue, $defaultKeys); + unset($defaultKeys[$index]); + } + } + + foreach ($defaultKeys as $defaultKey) { + if ($document->isSet($defaultKey)) { + $document->removeAttribute($defaultKey); + } + } + } + } + $this->trigger(self::EVENT_DOCUMENT_READ, $document); return $document; @@ -4015,6 +4045,17 @@ public function find(string $collection, array $queries = [], ?int $timeout = nu $results = $this->applyNestedQueries($results, $nestedQueries, $relationships); + // remove default keys $id and $permissions from the results + foreach ($queries as $query) { + if ($query->getMethod() === Query::TYPE_SELECT) { + foreach ($results as $result) { + foreach (Database::INTERNAL_ATTRIBUTES as $parameter) { + $result->removeAttribute($parameter); + } + } + } + } + $this->trigger(self::EVENT_DOCUMENT_FIND, $results); return $results; @@ -4351,8 +4392,17 @@ public function decode(Document $collection, Document $document, array $selectio } } - if (empty($selections) || \in_array($key, $selections) || \in_array('*', $selections)) { - $document->setAttribute($key, ($array) ? $value : $value[0]); + if ( + empty($selections) + || \in_array($key, $selections) + || \in_array('*', $selections) + || \in_array($key, ['$createdAt', '$updatedAt']) + ) { + if (\in_array($key, ['$createdAt', '$updatedAt']) && $value[0] === null) { + continue; + } else { + $document->setAttribute($key, ($array) ? $value : $value[0]); + } } } @@ -4510,6 +4560,8 @@ private function validateSelections(Document $collection, array $queries): array } } + $keys = \array_merge($keys, Database::INTERNAL_ATTRIBUTES); + $invalid = \array_diff($selections, $keys); if (!empty($invalid) && !\in_array('*', $invalid)) { throw new DatabaseException('Cannot select attributes: ' . \implode(', ', $invalid)); diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 8a83b39ba..96a39d3e2 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -1017,7 +1017,9 @@ public function testGetDocument(Document $document): Document */ public function testGetDocumentSelect(Document $document): Document { - $document = static::getDatabase()->getDocument('documents', $document->getId(), [ + $documentId = $document->getId(); + + $document = static::getDatabase()->getDocument('documents', $documentId, [ Query::select(['string', 'integer']), ]); @@ -1030,6 +1032,78 @@ public function testGetDocumentSelect(Document $document): Document $this->assertArrayNotHasKey('boolean', $document->getAttributes()); $this->assertArrayNotHasKey('colors', $document->getAttributes()); $this->assertArrayNotHasKey('with-dash', $document->getAttributes()); + $this->assertArrayNotHasKey('$id', $document); + $this->assertArrayNotHasKey('$internalId', $document); + $this->assertArrayNotHasKey('$collection', $document); + $this->assertArrayNotHasKey('$permissions', $document); + $this->assertArrayNotHasKey('$createdAt', $document); + $this->assertArrayNotHasKey('$updatedAt', $document); + + $document = static::getDatabase()->getDocument('documents', $documentId, [ + Query::select(['string', 'integer', '$id']), + ]); + + $this->assertArrayHasKey('$id', $document); + $this->assertArrayNotHasKey('$internalId', $document); + $this->assertArrayNotHasKey('$permissions', $document); + $this->assertArrayNotHasKey('$createdAt', $document); + $this->assertArrayNotHasKey('$updatedAt', $document); + $this->assertArrayNotHasKey('$collection', $document); + + $document = static::getDatabase()->getDocument('documents', $documentId, [ + Query::select(['string', 'integer', '$internalId']), + ]); + + $this->assertArrayNotHasKey('$id', $document); + $this->assertArrayHasKey('$internalId', $document); + $this->assertArrayNotHasKey('$permissions', $document); + $this->assertArrayNotHasKey('$createdAt', $document); + $this->assertArrayNotHasKey('$updatedAt', $document); + $this->assertArrayNotHasKey('$collection', $document); + + $document = static::getDatabase()->getDocument('documents', $documentId, [ + Query::select(['string', 'integer', '$permissions']), + ]); + + $this->assertArrayNotHasKey('$id', $document); + $this->assertArrayNotHasKey('$internalId', $document); + $this->assertArrayHasKey('$permissions', $document); + $this->assertArrayNotHasKey('$createdAt', $document); + $this->assertArrayNotHasKey('$updatedAt', $document); + $this->assertArrayNotHasKey('$collection', $document); + + $document = static::getDatabase()->getDocument('documents', $documentId, [ + Query::select(['string', 'integer', '$createdAt']), + ]); + + $this->assertArrayNotHasKey('$id', $document); + $this->assertArrayNotHasKey('$internalId', $document); + $this->assertArrayNotHasKey('$permissions', $document); + $this->assertArrayHasKey('$createdAt', $document); + $this->assertArrayNotHasKey('$updatedAt', $document); + $this->assertArrayNotHasKey('$collection', $document); + + $document = static::getDatabase()->getDocument('documents', $documentId, [ + Query::select(['string', 'integer', '$updatedAt']), + ]); + + $this->assertArrayNotHasKey('$id', $document); + $this->assertArrayNotHasKey('$internalId', $document); + $this->assertArrayNotHasKey('$permissions', $document); + $this->assertArrayNotHasKey('$createdAt', $document); + $this->assertArrayHasKey('$updatedAt', $document); + $this->assertArrayNotHasKey('$collection', $document); + + $document = static::getDatabase()->getDocument('documents', $documentId, [ + Query::select(['string', 'integer', '$collection']), + ]); + + $this->assertArrayNotHasKey('$id', $document); + $this->assertArrayNotHasKey('$internalId', $document); + $this->assertArrayNotHasKey('$permissions', $document); + $this->assertArrayNotHasKey('$createdAt', $document); + $this->assertArrayNotHasKey('$updatedAt', $document); + $this->assertArrayHasKey('$collection', $document); return $document; } @@ -2540,18 +2614,19 @@ public function testFindSelect(): void $documents = static::getDatabase()->find('movies', [ Query::select(['name', 'year']) ]); + foreach ($documents as $document) { - $this->assertArrayHasKey('$id', $document); - $this->assertArrayHasKey('$internalId', $document); - $this->assertArrayHasKey('$collection', $document); - $this->assertArrayHasKey('$createdAt', $document); - $this->assertArrayHasKey('$updatedAt', $document); - $this->assertArrayHasKey('$permissions', $document); $this->assertArrayHasKey('name', $document); $this->assertArrayHasKey('year', $document); - $this->assertArrayNotHasKey('director', $document); $this->assertArrayNotHasKey('price', $document); $this->assertArrayNotHasKey('active', $document); + $this->assertArrayNotHasKey('director', $document); + $this->assertArrayNotHasKey('$id', $document); + $this->assertArrayNotHasKey('$internalId', $document); + $this->assertArrayNotHasKey('$collection', $document); + $this->assertArrayNotHasKey('$createdAt', $document); + $this->assertArrayNotHasKey('$updatedAt', $document); + $this->assertArrayNotHasKey('$permissions', $document); } } @@ -4184,14 +4259,13 @@ public function testOneToOneOneWayRelationship(): void $this->assertArrayNotHasKey('area', $person->getAttribute('library')); $person = static::getDatabase()->getDocument('person', 'person1', [ - Query::select(['*', 'library.name']) + Query::select(['*', 'library.name', '$id']) ]); + $this->assertArrayHasKey('$id', $person); $this->assertEquals('Library 1', $person->getAttribute('library')->getAttribute('name')); $this->assertArrayNotHasKey('area', $person->getAttribute('library')); - - $document = static::getDatabase()->getDocument('person', $person->getId(), [ Query::select(['name']), ]);